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.

372 lines
15 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 object_detection.utils.visualization_utils."""
  16. import logging
  17. import os
  18. import numpy as np
  19. import PIL.Image as Image
  20. import tensorflow as tf
  21. from object_detection.core import standard_fields as fields
  22. from object_detection.utils import visualization_utils
  23. _TESTDATA_PATH = 'object_detection/test_images'
  24. class VisualizationUtilsTest(tf.test.TestCase):
  25. def create_colorful_test_image(self):
  26. """This function creates an image that can be used to test vis functions.
  27. It makes an image composed of four colored rectangles.
  28. Returns:
  29. colorful test numpy array image.
  30. """
  31. ch255 = np.full([100, 200, 1], 255, dtype=np.uint8)
  32. ch128 = np.full([100, 200, 1], 128, dtype=np.uint8)
  33. ch0 = np.full([100, 200, 1], 0, dtype=np.uint8)
  34. imr = np.concatenate((ch255, ch128, ch128), axis=2)
  35. img = np.concatenate((ch255, ch255, ch0), axis=2)
  36. imb = np.concatenate((ch255, ch0, ch255), axis=2)
  37. imw = np.concatenate((ch128, ch128, ch128), axis=2)
  38. imu = np.concatenate((imr, img), axis=1)
  39. imd = np.concatenate((imb, imw), axis=1)
  40. image = np.concatenate((imu, imd), axis=0)
  41. return image
  42. def create_test_image_with_five_channels(self):
  43. return np.full([100, 200, 5], 255, dtype=np.uint8)
  44. def create_test_grayscale_image(self):
  45. return np.full([100, 200, 1], 255, dtype=np.uint8)
  46. def test_draw_bounding_box_on_image(self):
  47. test_image = self.create_colorful_test_image()
  48. test_image = Image.fromarray(test_image)
  49. width_original, height_original = test_image.size
  50. ymin = 0.25
  51. ymax = 0.75
  52. xmin = 0.4
  53. xmax = 0.6
  54. visualization_utils.draw_bounding_box_on_image(test_image, ymin, xmin, ymax,
  55. xmax)
  56. width_final, height_final = test_image.size
  57. self.assertEqual(width_original, width_final)
  58. self.assertEqual(height_original, height_final)
  59. def test_draw_bounding_box_on_image_array(self):
  60. test_image = self.create_colorful_test_image()
  61. width_original = test_image.shape[0]
  62. height_original = test_image.shape[1]
  63. ymin = 0.25
  64. ymax = 0.75
  65. xmin = 0.4
  66. xmax = 0.6
  67. visualization_utils.draw_bounding_box_on_image_array(
  68. test_image, ymin, xmin, ymax, xmax)
  69. width_final = test_image.shape[0]
  70. height_final = test_image.shape[1]
  71. self.assertEqual(width_original, width_final)
  72. self.assertEqual(height_original, height_final)
  73. def test_draw_bounding_boxes_on_image(self):
  74. test_image = self.create_colorful_test_image()
  75. test_image = Image.fromarray(test_image)
  76. width_original, height_original = test_image.size
  77. boxes = np.array([[0.25, 0.75, 0.4, 0.6],
  78. [0.1, 0.1, 0.9, 0.9]])
  79. visualization_utils.draw_bounding_boxes_on_image(test_image, boxes)
  80. width_final, height_final = test_image.size
  81. self.assertEqual(width_original, width_final)
  82. self.assertEqual(height_original, height_final)
  83. def test_draw_bounding_boxes_on_image_array(self):
  84. test_image = self.create_colorful_test_image()
  85. width_original = test_image.shape[0]
  86. height_original = test_image.shape[1]
  87. boxes = np.array([[0.25, 0.75, 0.4, 0.6],
  88. [0.1, 0.1, 0.9, 0.9]])
  89. visualization_utils.draw_bounding_boxes_on_image_array(test_image, boxes)
  90. width_final = test_image.shape[0]
  91. height_final = test_image.shape[1]
  92. self.assertEqual(width_original, width_final)
  93. self.assertEqual(height_original, height_final)
  94. def test_draw_bounding_boxes_on_image_tensors(self):
  95. """Tests that bounding box utility produces reasonable results."""
  96. category_index = {1: {'id': 1, 'name': 'dog'}, 2: {'id': 2, 'name': 'cat'}}
  97. fname = os.path.join(_TESTDATA_PATH, 'image1.jpg')
  98. image_np = np.array(Image.open(fname))
  99. images_np = np.stack((image_np, image_np), axis=0)
  100. original_image_shape = [[636, 512], [636, 512]]
  101. with tf.Graph().as_default():
  102. images_tensor = tf.constant(value=images_np, dtype=tf.uint8)
  103. image_shape = tf.constant(original_image_shape, dtype=tf.int32)
  104. boxes = tf.constant([[[0.4, 0.25, 0.75, 0.75], [0.5, 0.3, 0.6, 0.9]],
  105. [[0.25, 0.25, 0.75, 0.75], [0.1, 0.3, 0.6, 1.0]]])
  106. classes = tf.constant([[1, 1], [1, 2]], dtype=tf.int64)
  107. scores = tf.constant([[0.8, 0.1], [0.6, 0.5]])
  108. images_with_boxes = (
  109. visualization_utils.draw_bounding_boxes_on_image_tensors(
  110. images_tensor,
  111. boxes,
  112. classes,
  113. scores,
  114. category_index,
  115. original_image_spatial_shape=image_shape,
  116. true_image_shape=image_shape,
  117. min_score_thresh=0.2))
  118. with self.test_session() as sess:
  119. sess.run(tf.global_variables_initializer())
  120. # Write output images for visualization.
  121. images_with_boxes_np = sess.run(images_with_boxes)
  122. self.assertEqual(images_np.shape[0], images_with_boxes_np.shape[0])
  123. self.assertEqual(images_np.shape[3], images_with_boxes_np.shape[3])
  124. self.assertEqual(
  125. tuple(original_image_shape[0]), images_with_boxes_np.shape[1:3])
  126. for i in range(images_with_boxes_np.shape[0]):
  127. img_name = 'image_' + str(i) + '.png'
  128. output_file = os.path.join(self.get_temp_dir(), img_name)
  129. logging.info('Writing output image %d to %s', i, output_file)
  130. image_pil = Image.fromarray(images_with_boxes_np[i, ...])
  131. image_pil.save(output_file)
  132. def test_draw_bounding_boxes_on_image_tensors_with_additional_channels(self):
  133. """Tests the case where input image tensor has more than 3 channels."""
  134. category_index = {1: {'id': 1, 'name': 'dog'}}
  135. image_np = self.create_test_image_with_five_channels()
  136. images_np = np.stack((image_np, image_np), axis=0)
  137. with tf.Graph().as_default():
  138. images_tensor = tf.constant(value=images_np, dtype=tf.uint8)
  139. boxes = tf.constant(0, dtype=tf.float32, shape=[2, 0, 4])
  140. classes = tf.constant(0, dtype=tf.int64, shape=[2, 0])
  141. scores = tf.constant(0, dtype=tf.float32, shape=[2, 0])
  142. images_with_boxes = (
  143. visualization_utils.draw_bounding_boxes_on_image_tensors(
  144. images_tensor,
  145. boxes,
  146. classes,
  147. scores,
  148. category_index,
  149. min_score_thresh=0.2))
  150. with self.test_session() as sess:
  151. sess.run(tf.global_variables_initializer())
  152. final_images_np = sess.run(images_with_boxes)
  153. self.assertEqual((2, 100, 200, 3), final_images_np.shape)
  154. def test_draw_bounding_boxes_on_image_tensors_grayscale(self):
  155. """Tests the case where input image tensor has one channel."""
  156. category_index = {1: {'id': 1, 'name': 'dog'}}
  157. image_np = self.create_test_grayscale_image()
  158. images_np = np.stack((image_np, image_np), axis=0)
  159. with tf.Graph().as_default():
  160. images_tensor = tf.constant(value=images_np, dtype=tf.uint8)
  161. image_shape = tf.constant([[100, 200], [100, 200]], dtype=tf.int32)
  162. boxes = tf.constant(0, dtype=tf.float32, shape=[2, 0, 4])
  163. classes = tf.constant(0, dtype=tf.int64, shape=[2, 0])
  164. scores = tf.constant(0, dtype=tf.float32, shape=[2, 0])
  165. images_with_boxes = (
  166. visualization_utils.draw_bounding_boxes_on_image_tensors(
  167. images_tensor,
  168. boxes,
  169. classes,
  170. scores,
  171. category_index,
  172. original_image_spatial_shape=image_shape,
  173. true_image_shape=image_shape,
  174. min_score_thresh=0.2))
  175. with self.test_session() as sess:
  176. sess.run(tf.global_variables_initializer())
  177. final_images_np = sess.run(images_with_boxes)
  178. self.assertEqual((2, 100, 200, 3), final_images_np.shape)
  179. def test_draw_keypoints_on_image(self):
  180. test_image = self.create_colorful_test_image()
  181. test_image = Image.fromarray(test_image)
  182. width_original, height_original = test_image.size
  183. keypoints = [[0.25, 0.75], [0.4, 0.6], [0.1, 0.1], [0.9, 0.9]]
  184. visualization_utils.draw_keypoints_on_image(test_image, keypoints)
  185. width_final, height_final = test_image.size
  186. self.assertEqual(width_original, width_final)
  187. self.assertEqual(height_original, height_final)
  188. def test_draw_keypoints_on_image_array(self):
  189. test_image = self.create_colorful_test_image()
  190. width_original = test_image.shape[0]
  191. height_original = test_image.shape[1]
  192. keypoints = [[0.25, 0.75], [0.4, 0.6], [0.1, 0.1], [0.9, 0.9]]
  193. visualization_utils.draw_keypoints_on_image_array(test_image, keypoints)
  194. width_final = test_image.shape[0]
  195. height_final = test_image.shape[1]
  196. self.assertEqual(width_original, width_final)
  197. self.assertEqual(height_original, height_final)
  198. def test_draw_mask_on_image_array(self):
  199. test_image = np.asarray([[[0, 0, 0], [0, 0, 0]],
  200. [[0, 0, 0], [0, 0, 0]]], dtype=np.uint8)
  201. mask = np.asarray([[0, 1],
  202. [1, 1]], dtype=np.uint8)
  203. expected_result = np.asarray([[[0, 0, 0], [0, 0, 127]],
  204. [[0, 0, 127], [0, 0, 127]]], dtype=np.uint8)
  205. visualization_utils.draw_mask_on_image_array(test_image, mask,
  206. color='Blue', alpha=.5)
  207. self.assertAllEqual(test_image, expected_result)
  208. def test_add_cdf_image_summary(self):
  209. values = [0.1, 0.2, 0.3, 0.4, 0.42, 0.44, 0.46, 0.48, 0.50]
  210. visualization_utils.add_cdf_image_summary(values, 'PositiveAnchorLoss')
  211. cdf_image_summary = tf.get_collection(key=tf.GraphKeys.SUMMARIES)[0]
  212. with self.test_session():
  213. cdf_image_summary.eval()
  214. def test_add_hist_image_summary(self):
  215. values = [0.1, 0.2, 0.3, 0.4, 0.42, 0.44, 0.46, 0.48, 0.50]
  216. bins = [0.01 * i for i in range(101)]
  217. visualization_utils.add_hist_image_summary(values, bins,
  218. 'ScoresDistribution')
  219. hist_image_summary = tf.get_collection(key=tf.GraphKeys.SUMMARIES)[0]
  220. with self.test_session():
  221. hist_image_summary.eval()
  222. def test_eval_metric_ops(self):
  223. category_index = {1: {'id': 1, 'name': 'dog'}, 2: {'id': 2, 'name': 'cat'}}
  224. max_examples_to_draw = 4
  225. metric_op_base = 'Detections_Left_Groundtruth_Right'
  226. eval_metric_ops = visualization_utils.VisualizeSingleFrameDetections(
  227. category_index,
  228. max_examples_to_draw=max_examples_to_draw,
  229. summary_name_prefix=metric_op_base)
  230. original_image = tf.placeholder(tf.uint8, [4, None, None, 3])
  231. original_image_spatial_shape = tf.placeholder(tf.int32, [4, 2])
  232. true_image_shape = tf.placeholder(tf.int32, [4, 3])
  233. detection_boxes = tf.random_uniform([4, 20, 4],
  234. minval=0.0,
  235. maxval=1.0,
  236. dtype=tf.float32)
  237. detection_classes = tf.random_uniform([4, 20],
  238. minval=1,
  239. maxval=3,
  240. dtype=tf.int64)
  241. detection_scores = tf.random_uniform([4, 20],
  242. minval=0.,
  243. maxval=1.,
  244. dtype=tf.float32)
  245. groundtruth_boxes = tf.random_uniform([4, 8, 4],
  246. minval=0.0,
  247. maxval=1.0,
  248. dtype=tf.float32)
  249. groundtruth_classes = tf.random_uniform([4, 8],
  250. minval=1,
  251. maxval=3,
  252. dtype=tf.int64)
  253. eval_dict = {
  254. fields.DetectionResultFields.detection_boxes:
  255. detection_boxes,
  256. fields.DetectionResultFields.detection_classes:
  257. detection_classes,
  258. fields.DetectionResultFields.detection_scores:
  259. detection_scores,
  260. fields.InputDataFields.original_image:
  261. original_image,
  262. fields.InputDataFields.original_image_spatial_shape: (
  263. original_image_spatial_shape),
  264. fields.InputDataFields.true_image_shape: (true_image_shape),
  265. fields.InputDataFields.groundtruth_boxes:
  266. groundtruth_boxes,
  267. fields.InputDataFields.groundtruth_classes:
  268. groundtruth_classes
  269. }
  270. metric_ops = eval_metric_ops.get_estimator_eval_metric_ops(eval_dict)
  271. _, update_op = metric_ops[metric_ops.keys()[0]]
  272. with self.test_session() as sess:
  273. sess.run(tf.global_variables_initializer())
  274. value_ops = {}
  275. for key, (value_op, _) in metric_ops.iteritems():
  276. value_ops[key] = value_op
  277. # First run enough update steps to surpass `max_examples_to_draw`.
  278. for i in range(max_examples_to_draw):
  279. # Use a unique image shape on each eval image.
  280. sess.run(
  281. update_op,
  282. feed_dict={
  283. original_image:
  284. np.random.randint(
  285. low=0,
  286. high=256,
  287. size=(4, 6 + i, 7 + i, 3),
  288. dtype=np.uint8),
  289. original_image_spatial_shape: [[6 + i, 7 + i], [6 + i, 7 + i],
  290. [6 + i, 7 + i], [6 + i, 7 + i]],
  291. true_image_shape: [[6 + i, 7 + i, 3], [6 + i, 7 + i, 3],
  292. [6 + i, 7 + i, 3], [6 + i, 7 + i, 3]]
  293. })
  294. value_ops_out = sess.run(value_ops)
  295. for key, value_op in value_ops_out.iteritems():
  296. self.assertNotEqual('', value_op)
  297. # Now run fewer update steps than `max_examples_to_draw`. A single value
  298. # op will be the empty string, since not enough image summaries can be
  299. # produced.
  300. for i in range(max_examples_to_draw - 1):
  301. # Use a unique image shape on each eval image.
  302. sess.run(
  303. update_op,
  304. feed_dict={
  305. original_image:
  306. np.random.randint(
  307. low=0,
  308. high=256,
  309. size=(4, 6 + i, 7 + i, 3),
  310. dtype=np.uint8),
  311. original_image_spatial_shape: [[6 + i, 7 + i], [6 + i, 7 + i],
  312. [6 + i, 7 + i], [6 + i, 7 + i]],
  313. true_image_shape: [[6 + i, 7 + i, 3], [6 + i, 7 + i, 3],
  314. [6 + i, 7 + i, 3], [6 + i, 7 + i, 3]]
  315. })
  316. value_ops_out = sess.run(value_ops)
  317. self.assertEqual(
  318. '',
  319. value_ops_out[metric_op_base + '/' + str(max_examples_to_draw - 1)])
  320. if __name__ == '__main__':
  321. tf.test.main()