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

295 lines
12 KiB

6 years ago
  1. # Copyright 2017 The TensorFlow Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # ==============================================================================
  15. """Tests for tensorflow_model.object_detection.metrics.coco_tools."""
  16. import json
  17. import os
  18. import re
  19. import numpy as np
  20. from pycocotools import mask
  21. import tensorflow as tf
  22. from object_detection.metrics import coco_tools
  23. class CocoToolsTest(tf.test.TestCase):
  24. def setUp(self):
  25. groundtruth_annotations_list = [
  26. {
  27. 'id': 1,
  28. 'image_id': 'first',
  29. 'category_id': 1,
  30. 'bbox': [100., 100., 100., 100.],
  31. 'area': 100.**2,
  32. 'iscrowd': 0
  33. },
  34. {
  35. 'id': 2,
  36. 'image_id': 'second',
  37. 'category_id': 1,
  38. 'bbox': [50., 50., 50., 50.],
  39. 'area': 50.**2,
  40. 'iscrowd': 0
  41. },
  42. ]
  43. image_list = [{'id': 'first'}, {'id': 'second'}]
  44. category_list = [{'id': 0, 'name': 'person'},
  45. {'id': 1, 'name': 'cat'},
  46. {'id': 2, 'name': 'dog'}]
  47. self._groundtruth_dict = {
  48. 'annotations': groundtruth_annotations_list,
  49. 'images': image_list,
  50. 'categories': category_list
  51. }
  52. self._detections_list = [
  53. {
  54. 'image_id': 'first',
  55. 'category_id': 1,
  56. 'bbox': [100., 100., 100., 100.],
  57. 'score': .8
  58. },
  59. {
  60. 'image_id': 'second',
  61. 'category_id': 1,
  62. 'bbox': [50., 50., 50., 50.],
  63. 'score': .7
  64. },
  65. ]
  66. def testCocoWrappers(self):
  67. groundtruth = coco_tools.COCOWrapper(self._groundtruth_dict)
  68. detections = groundtruth.LoadAnnotations(self._detections_list)
  69. evaluator = coco_tools.COCOEvalWrapper(groundtruth, detections)
  70. summary_metrics, _ = evaluator.ComputeMetrics()
  71. self.assertAlmostEqual(1.0, summary_metrics['Precision/mAP'])
  72. def testExportGroundtruthToCOCO(self):
  73. image_ids = ['first', 'second']
  74. groundtruth_boxes = [np.array([[100, 100, 200, 200]], np.float),
  75. np.array([[50, 50, 100, 100]], np.float)]
  76. groundtruth_classes = [np.array([1], np.int32), np.array([1], np.int32)]
  77. categories = [{'id': 0, 'name': 'person'},
  78. {'id': 1, 'name': 'cat'},
  79. {'id': 2, 'name': 'dog'}]
  80. output_path = os.path.join(tf.test.get_temp_dir(), 'groundtruth.json')
  81. result = coco_tools.ExportGroundtruthToCOCO(
  82. image_ids,
  83. groundtruth_boxes,
  84. groundtruth_classes,
  85. categories,
  86. output_path=output_path)
  87. self.assertDictEqual(result, self._groundtruth_dict)
  88. with tf.gfile.GFile(output_path, 'r') as f:
  89. written_result = f.read()
  90. # The json output should have floats written to 4 digits of precision.
  91. matcher = re.compile(r'"bbox":\s+\[\n\s+\d+.\d\d\d\d,', re.MULTILINE)
  92. self.assertTrue(matcher.findall(written_result))
  93. written_result = json.loads(written_result)
  94. self.assertAlmostEqual(result, written_result)
  95. def testExportDetectionsToCOCO(self):
  96. image_ids = ['first', 'second']
  97. detections_boxes = [np.array([[100, 100, 200, 200]], np.float),
  98. np.array([[50, 50, 100, 100]], np.float)]
  99. detections_scores = [np.array([.8], np.float), np.array([.7], np.float)]
  100. detections_classes = [np.array([1], np.int32), np.array([1], np.int32)]
  101. categories = [{'id': 0, 'name': 'person'},
  102. {'id': 1, 'name': 'cat'},
  103. {'id': 2, 'name': 'dog'}]
  104. output_path = os.path.join(tf.test.get_temp_dir(), 'detections.json')
  105. result = coco_tools.ExportDetectionsToCOCO(
  106. image_ids,
  107. detections_boxes,
  108. detections_scores,
  109. detections_classes,
  110. categories,
  111. output_path=output_path)
  112. self.assertListEqual(result, self._detections_list)
  113. with tf.gfile.GFile(output_path, 'r') as f:
  114. written_result = f.read()
  115. # The json output should have floats written to 4 digits of precision.
  116. matcher = re.compile(r'"bbox":\s+\[\n\s+\d+.\d\d\d\d,', re.MULTILINE)
  117. self.assertTrue(matcher.findall(written_result))
  118. written_result = json.loads(written_result)
  119. self.assertAlmostEqual(result, written_result)
  120. def testExportSegmentsToCOCO(self):
  121. image_ids = ['first', 'second']
  122. detection_masks = [np.array(
  123. [[[0, 1, 0, 1], [0, 1, 1, 0], [0, 0, 0, 1], [0, 1, 0, 1]]],
  124. dtype=np.uint8), np.array(
  125. [[[0, 1, 0, 1], [0, 1, 1, 0], [0, 0, 0, 1], [0, 1, 0, 1]]],
  126. dtype=np.uint8)]
  127. for i, detection_mask in enumerate(detection_masks):
  128. detection_masks[i] = detection_mask[:, :, :, None]
  129. detection_scores = [np.array([.8], np.float), np.array([.7], np.float)]
  130. detection_classes = [np.array([1], np.int32), np.array([1], np.int32)]
  131. categories = [{'id': 0, 'name': 'person'},
  132. {'id': 1, 'name': 'cat'},
  133. {'id': 2, 'name': 'dog'}]
  134. output_path = os.path.join(tf.test.get_temp_dir(), 'segments.json')
  135. result = coco_tools.ExportSegmentsToCOCO(
  136. image_ids,
  137. detection_masks,
  138. detection_scores,
  139. detection_classes,
  140. categories,
  141. output_path=output_path)
  142. with tf.gfile.GFile(output_path, 'r') as f:
  143. written_result = f.read()
  144. written_result = json.loads(written_result)
  145. mask_load = mask.decode([written_result[0]['segmentation']])
  146. self.assertTrue(np.allclose(mask_load, detection_masks[0]))
  147. self.assertAlmostEqual(result, written_result)
  148. def testExportKeypointsToCOCO(self):
  149. image_ids = ['first', 'second']
  150. detection_keypoints = [
  151. np.array(
  152. [[[100, 200], [300, 400], [500, 600]],
  153. [[50, 150], [250, 350], [450, 550]]], dtype=np.int32),
  154. np.array(
  155. [[[110, 210], [310, 410], [510, 610]],
  156. [[60, 160], [260, 360], [460, 560]]], dtype=np.int32)]
  157. detection_scores = [np.array([.8, 0.2], np.float),
  158. np.array([.7, 0.3], np.float)]
  159. detection_classes = [np.array([1, 1], np.int32), np.array([1, 1], np.int32)]
  160. categories = [{'id': 1, 'name': 'person', 'num_keypoints': 3},
  161. {'id': 2, 'name': 'cat'},
  162. {'id': 3, 'name': 'dog'}]
  163. output_path = os.path.join(tf.test.get_temp_dir(), 'keypoints.json')
  164. result = coco_tools.ExportKeypointsToCOCO(
  165. image_ids,
  166. detection_keypoints,
  167. detection_scores,
  168. detection_classes,
  169. categories,
  170. output_path=output_path)
  171. with tf.gfile.GFile(output_path, 'r') as f:
  172. written_result = f.read()
  173. written_result = json.loads(written_result)
  174. self.assertAlmostEqual(result, written_result)
  175. def testSingleImageDetectionBoxesExport(self):
  176. boxes = np.array([[0, 0, 1, 1],
  177. [0, 0, .5, .5],
  178. [.5, .5, 1, 1]], dtype=np.float32)
  179. classes = np.array([1, 2, 3], dtype=np.int32)
  180. scores = np.array([0.8, 0.2, 0.7], dtype=np.float32)
  181. coco_boxes = np.array([[0, 0, 1, 1],
  182. [0, 0, .5, .5],
  183. [.5, .5, .5, .5]], dtype=np.float32)
  184. coco_annotations = coco_tools.ExportSingleImageDetectionBoxesToCoco(
  185. image_id='first_image',
  186. category_id_set=set([1, 2, 3]),
  187. detection_boxes=boxes,
  188. detection_classes=classes,
  189. detection_scores=scores)
  190. for i, annotation in enumerate(coco_annotations):
  191. self.assertEqual(annotation['image_id'], 'first_image')
  192. self.assertEqual(annotation['category_id'], classes[i])
  193. self.assertAlmostEqual(annotation['score'], scores[i])
  194. self.assertTrue(np.all(np.isclose(annotation['bbox'], coco_boxes[i])))
  195. def testSingleImageDetectionMaskExport(self):
  196. masks = np.array(
  197. [[[1, 1,], [1, 1]],
  198. [[0, 0], [0, 1]],
  199. [[0, 0], [0, 0]]], dtype=np.uint8)
  200. classes = np.array([1, 2, 3], dtype=np.int32)
  201. scores = np.array([0.8, 0.2, 0.7], dtype=np.float32)
  202. coco_annotations = coco_tools.ExportSingleImageDetectionMasksToCoco(
  203. image_id='first_image',
  204. category_id_set=set([1, 2, 3]),
  205. detection_classes=classes,
  206. detection_scores=scores,
  207. detection_masks=masks)
  208. expected_counts = ['04', '31', '4']
  209. for i, mask_annotation in enumerate(coco_annotations):
  210. self.assertEqual(mask_annotation['segmentation']['counts'],
  211. expected_counts[i])
  212. self.assertTrue(np.all(np.equal(mask.decode(
  213. mask_annotation['segmentation']), masks[i])))
  214. self.assertEqual(mask_annotation['image_id'], 'first_image')
  215. self.assertEqual(mask_annotation['category_id'], classes[i])
  216. self.assertAlmostEqual(mask_annotation['score'], scores[i])
  217. def testSingleImageGroundtruthExport(self):
  218. masks = np.array(
  219. [[[1, 1,], [1, 1]],
  220. [[0, 0], [0, 1]],
  221. [[0, 0], [0, 0]]], dtype=np.uint8)
  222. boxes = np.array([[0, 0, 1, 1],
  223. [0, 0, .5, .5],
  224. [.5, .5, 1, 1]], dtype=np.float32)
  225. coco_boxes = np.array([[0, 0, 1, 1],
  226. [0, 0, .5, .5],
  227. [.5, .5, .5, .5]], dtype=np.float32)
  228. classes = np.array([1, 2, 3], dtype=np.int32)
  229. is_crowd = np.array([0, 1, 0], dtype=np.int32)
  230. next_annotation_id = 1
  231. expected_counts = ['04', '31', '4']
  232. # Tests exporting without passing in is_crowd (for backward compatibility).
  233. coco_annotations = coco_tools.ExportSingleImageGroundtruthToCoco(
  234. image_id='first_image',
  235. category_id_set=set([1, 2, 3]),
  236. next_annotation_id=next_annotation_id,
  237. groundtruth_boxes=boxes,
  238. groundtruth_classes=classes,
  239. groundtruth_masks=masks)
  240. for i, annotation in enumerate(coco_annotations):
  241. self.assertEqual(annotation['segmentation']['counts'],
  242. expected_counts[i])
  243. self.assertTrue(np.all(np.equal(mask.decode(
  244. annotation['segmentation']), masks[i])))
  245. self.assertTrue(np.all(np.isclose(annotation['bbox'], coco_boxes[i])))
  246. self.assertEqual(annotation['image_id'], 'first_image')
  247. self.assertEqual(annotation['category_id'], classes[i])
  248. self.assertEqual(annotation['id'], i + next_annotation_id)
  249. # Tests exporting with is_crowd.
  250. coco_annotations = coco_tools.ExportSingleImageGroundtruthToCoco(
  251. image_id='first_image',
  252. category_id_set=set([1, 2, 3]),
  253. next_annotation_id=next_annotation_id,
  254. groundtruth_boxes=boxes,
  255. groundtruth_classes=classes,
  256. groundtruth_masks=masks,
  257. groundtruth_is_crowd=is_crowd)
  258. for i, annotation in enumerate(coco_annotations):
  259. self.assertEqual(annotation['segmentation']['counts'],
  260. expected_counts[i])
  261. self.assertTrue(np.all(np.equal(mask.decode(
  262. annotation['segmentation']), masks[i])))
  263. self.assertTrue(np.all(np.isclose(annotation['bbox'], coco_boxes[i])))
  264. self.assertEqual(annotation['image_id'], 'first_image')
  265. self.assertEqual(annotation['category_id'], classes[i])
  266. self.assertEqual(annotation['iscrowd'], is_crowd[i])
  267. self.assertEqual(annotation['id'], i + next_annotation_id)
  268. if __name__ == '__main__':
  269. tf.test.main()