You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

bboxop_common.cc 9.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /**
  2. * Copyright 2020 Huawei Technologies Co., Ltd
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "bboxop_common.h"
  17. #include <memory>
  18. #include <string>
  19. #include <vector>
  20. #include <iostream>
  21. #include <stdio.h>
  22. #include "./tinyxml2.h"
  23. #include "opencv2/opencv.hpp"
  24. #include "utils/ms_utils.h"
  25. #include "minddata/dataset/core/cv_tensor.h"
  26. #include "minddata/dataset/util/path.h"
  27. #include "minddata/dataset/include/constants.h"
  28. #include "utils/log_adapter.h"
  29. using namespace mindspore::dataset;
  30. using namespace UT::CVOP::BBOXOP;
  31. using tinyxml2::XMLDocument;
  32. using tinyxml2::XMLElement;
  33. using tinyxml2::XMLError;
  34. const char kAnnotationsFolder[] = "/Annotations/";
  35. const char kImagesFolder[] = "/JPEGImages/";
  36. const char kExpectedName[] = "apple_expect_";
  37. const char kActualName[] = "Actual";
  38. const char kAnnotExt[] = ".xml";
  39. const char kImageExt[] = ".jpg";
  40. BBoxOpCommon::BBoxOpCommon() {}
  41. BBoxOpCommon::~BBoxOpCommon() {}
  42. void BBoxOpCommon::SetUp() {
  43. MS_LOG(INFO) << "starting test.";
  44. image_folder_build_ = "data/dataset/imagefolder/";
  45. image_folder_src_ = "../../../../../tests/ut/data/dataset/imagefolder/";
  46. std::string dir_path = "data/dataset/testVOC2012_2";
  47. GetInputImagesAndAnnotations(dir_path);
  48. }
  49. void BBoxOpCommon::GetInputImagesAndAnnotations(const std::string &dir, std::size_t num_of_samples) {
  50. std::string images_path = dir + std::string(kImagesFolder);
  51. std::string annots_path = dir + std::string(kAnnotationsFolder);
  52. Path dir_path(images_path);
  53. std::shared_ptr<Path::DirIterator> image_dir_itr = Path::DirIterator::OpenDirectory(&dir_path);
  54. std::vector<std::string> paths_to_fetch;
  55. if (!dir_path.Exists()) {
  56. MS_LOG(ERROR) << "Images folder was not found : " + images_path;
  57. EXPECT_TRUE(dir_path.Exists());
  58. }
  59. // get image file paths
  60. while (image_dir_itr->hasNext()) {
  61. Path image_path = image_dir_itr->next();
  62. if (image_path.Extension() == std::string(kImageExt)) {
  63. paths_to_fetch.push_back(image_path.toString());
  64. }
  65. }
  66. // sort fetched files
  67. std::sort(paths_to_fetch.begin(), paths_to_fetch.end());
  68. std::size_t files_fetched = 0;
  69. for (const auto &image_file : paths_to_fetch) {
  70. std::string image_ext = std::string(kImageExt);
  71. std::string annot_file = image_file;
  72. std::size_t pos = 0;
  73. // first replace the Image dir with the Annotation dir.
  74. if ((pos = image_file.find(std::string(kImagesFolder), 0)) != std::string::npos) {
  75. annot_file.replace(pos, std::string(kImagesFolder).length(), std::string(kAnnotationsFolder));
  76. }
  77. // then replace the extensions. the image extension to annotation extension
  78. if ((pos = annot_file.find(image_ext, 0)) != std::string::npos) {
  79. annot_file.replace(pos, std::string(kAnnotExt).length(), std::string(kAnnotExt));
  80. }
  81. std::shared_ptr<Tensor> annotation_tensor;
  82. // load annotations and log failure
  83. if (!LoadAnnotationFile(annot_file, &annotation_tensor)) {
  84. MS_LOG(ERROR) << "Loading Annotations failed in GetInputImagesAndAnnotations";
  85. EXPECT_EQ(0, 1);
  86. }
  87. // load image
  88. GetInputImage(image_file);
  89. // add image and annotation to the tensor table
  90. TensorRow row_data({std::move(input_tensor_), std::move(annotation_tensor)});
  91. images_and_annotations_.push_back(row_data);
  92. files_fetched++;
  93. if (files_fetched == num_of_samples) {
  94. break;
  95. }
  96. }
  97. }
  98. void BBoxOpCommon::SaveImagesWithAnnotations(BBoxOpCommon::FileType type, const std::string &op_name,
  99. const TensorTable &table) {
  100. int i = 0;
  101. for (auto &row : table) {
  102. std::shared_ptr<Tensor> row_to_save;
  103. Status swap_status = SwapRedAndBlue(row[0], &row_to_save);
  104. if (!swap_status.IsOk()) {
  105. MS_LOG(ERROR) << "Swapping red and blue channels failed in SaveImagesWithAnnotations.";
  106. EXPECT_TRUE(swap_status.IsOk());
  107. }
  108. cv::Mat image = std::static_pointer_cast<CVTensor>(row_to_save)->mat();
  109. uint32_t num_of_boxes = row[1]->shape()[0];
  110. bool passing_data_fetch = true;
  111. // For each bounding box draw on the image.
  112. for (uint32_t i = 0; i < num_of_boxes; i++) {
  113. float x = 0.0, y = 0.0, w = 0.0, h = 0.0;
  114. passing_data_fetch &= row[1]->GetItemAt<float>(&x, {i, 0}).IsOk();
  115. passing_data_fetch &= row[1]->GetItemAt<float>(&y, {i, 1}).IsOk();
  116. passing_data_fetch &= row[1]->GetItemAt<float>(&w, {i, 2}).IsOk();
  117. passing_data_fetch &= row[1]->GetItemAt<float>(&h, {i, 3}).IsOk();
  118. if (!passing_data_fetch) {
  119. MS_LOG(ERROR) << "Fetching bbox coordinates failed in SaveImagesWithAnnotations.";
  120. EXPECT_TRUE(passing_data_fetch);
  121. }
  122. cv::Rect bbox(x, y, w, h);
  123. cv::rectangle(image, bbox, cv::Scalar(255, 0, 0), 10, 8, 0);
  124. }
  125. bool im_write_success = false;
  126. // if user wants to save an expected image, use the path to the source folder.
  127. if (type == FileType::kExpected) {
  128. im_write_success = cv::imwrite(
  129. image_folder_src_ + std::string(kExpectedName) + op_name + std::to_string(i) + std::string(kImageExt), image);
  130. } else {
  131. // otherwise if we are saving actual images only for comparison, save in current working dir in build folders.
  132. im_write_success =
  133. cv::imwrite(std::string(kActualName) + op_name + std::to_string(i) + std::string(kImageExt), image);
  134. }
  135. if (!im_write_success) {
  136. MS_LOG(ERROR) << "Image write failed in SaveImagesWithAnnotations.";
  137. EXPECT_TRUE(im_write_success);
  138. }
  139. i += 1;
  140. }
  141. }
  142. void BBoxOpCommon::CompareActualAndExpected(const std::string &op_name) {
  143. size_t num_of_images = images_and_annotations_.size();
  144. for (size_t i = 0; i < num_of_images; i++) {
  145. // load actual and expected images.
  146. std::string actual_path = std::string(kActualName) + op_name + std::to_string(i) + std::string(kImageExt);
  147. std::string expected_path =
  148. image_folder_build_ + std::string(kExpectedName) + op_name + std::to_string(i) + std::string(kImageExt);
  149. cv::Mat expect_img = cv::imread(expected_path, cv::IMREAD_COLOR);
  150. cv::Mat actual_img = cv::imread(actual_path, cv::IMREAD_COLOR);
  151. // after comparison is done remove temporary file
  152. EXPECT_TRUE(remove(actual_path.c_str()) == 0);
  153. // compare using ==operator by Tensor
  154. std::shared_ptr<CVTensor> expect_img_t, actual_img_t;
  155. CVTensor::CreateFromMat(expect_img, &expect_img_t);
  156. CVTensor::CreateFromMat(actual_img, &actual_img_t);
  157. if (actual_img.data) {
  158. EXPECT_EQ(*expect_img_t == *actual_img_t, true);
  159. } else {
  160. MS_LOG(ERROR) << "Not pass verification! Image data is null.";
  161. EXPECT_EQ(0, 1);
  162. }
  163. }
  164. }
  165. bool BBoxOpCommon::LoadAnnotationFile(const std::string &path, std::shared_ptr<Tensor> *target_BBox) {
  166. if (!Path(path).Exists()) {
  167. MS_LOG(ERROR) << "File is not found : " + path;
  168. return false;
  169. }
  170. XMLDocument doc;
  171. XMLError e = doc.LoadFile(mindspore::common::SafeCStr(path));
  172. if (e != XMLError::XML_SUCCESS) {
  173. MS_LOG(ERROR) << "Xml load failed";
  174. return false;
  175. }
  176. XMLElement *root = doc.RootElement();
  177. if (root == nullptr) {
  178. MS_LOG(ERROR) << "Xml load root element error";
  179. return false;
  180. }
  181. XMLElement *object = root->FirstChildElement("object");
  182. if (object == nullptr) {
  183. MS_LOG(ERROR) << "No object find in " + path;
  184. return false;
  185. }
  186. std::vector<float> return_value_list;
  187. dsize_t bbox_count = 0; // keep track of number of bboxes in file
  188. dsize_t bbox_val_count = 4; // creating bboxes of size 4 to test function
  189. // FILE OK TO READ
  190. while (object != nullptr) {
  191. bbox_count += 1;
  192. std::string label_name;
  193. float xmin = 0.0, ymin = 0.0, xmax = 0.0, ymax = 0.0;
  194. XMLElement *bbox_node = object->FirstChildElement("bndbox");
  195. if (bbox_node != nullptr) {
  196. XMLElement *xmin_node = bbox_node->FirstChildElement("xmin");
  197. if (xmin_node != nullptr) xmin = xmin_node->FloatText();
  198. XMLElement *ymin_node = bbox_node->FirstChildElement("ymin");
  199. if (ymin_node != nullptr) ymin = ymin_node->FloatText();
  200. XMLElement *xmax_node = bbox_node->FirstChildElement("xmax");
  201. if (xmax_node != nullptr) xmax = xmax_node->FloatText();
  202. XMLElement *ymax_node = bbox_node->FirstChildElement("ymax");
  203. if (ymax_node != nullptr) ymax = ymax_node->FloatText();
  204. } else {
  205. MS_LOG(ERROR) << "bndbox dismatch in " + path;
  206. return false;
  207. }
  208. if (xmin > 0 && ymin > 0 && xmax > xmin && ymax > ymin) {
  209. for (auto item : {xmin, ymin, xmax - xmin, ymax - ymin}) {
  210. return_value_list.push_back(item);
  211. }
  212. }
  213. object = object->NextSiblingElement("object"); // Read next BBox if exists
  214. }
  215. std::shared_ptr<Tensor> ret_value;
  216. Status s = Tensor::CreateFromVector(return_value_list, TensorShape({bbox_count, bbox_val_count}), &ret_value);
  217. EXPECT_TRUE(s.IsOk());
  218. (*target_BBox) = ret_value; // load bbox from file into return
  219. return true;
  220. }