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.

262 lines
9.4 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. """Matcher interface and Match class.
  16. This module defines the Matcher interface and the Match object. The job of the
  17. matcher is to match row and column indices based on the similarity matrix and
  18. other optional parameters. Each column is matched to at most one row. There
  19. are three possibilities for the matching:
  20. 1) match: A column matches a row.
  21. 2) no_match: A column does not match any row.
  22. 3) ignore: A column that is neither 'match' nor no_match.
  23. The ignore case is regularly encountered in object detection: when an anchor has
  24. a relatively small overlap with a ground-truth box, one neither wants to
  25. consider this box a positive example (match) nor a negative example (no match).
  26. The Match class is used to store the match results and it provides simple apis
  27. to query the results.
  28. """
  29. from abc import ABCMeta
  30. from abc import abstractmethod
  31. import tensorflow as tf
  32. from object_detection.utils import ops
  33. class Match(object):
  34. """Class to store results from the matcher.
  35. This class is used to store the results from the matcher. It provides
  36. convenient methods to query the matching results.
  37. """
  38. def __init__(self, match_results, use_matmul_gather=False):
  39. """Constructs a Match object.
  40. Args:
  41. match_results: Integer tensor of shape [N] with (1) match_results[i]>=0,
  42. meaning that column i is matched with row match_results[i].
  43. (2) match_results[i]=-1, meaning that column i is not matched.
  44. (3) match_results[i]=-2, meaning that column i is ignored.
  45. use_matmul_gather: Use matrix multiplication based gather instead of
  46. standard tf.gather. (Default: False).
  47. Raises:
  48. ValueError: if match_results does not have rank 1 or is not an
  49. integer int32 scalar tensor
  50. """
  51. if match_results.shape.ndims != 1:
  52. raise ValueError('match_results should have rank 1')
  53. if match_results.dtype != tf.int32:
  54. raise ValueError('match_results should be an int32 or int64 scalar '
  55. 'tensor')
  56. self._match_results = match_results
  57. self._gather_op = tf.gather
  58. if use_matmul_gather:
  59. self._gather_op = ops.matmul_gather_on_zeroth_axis
  60. @property
  61. def match_results(self):
  62. """The accessor for match results.
  63. Returns:
  64. the tensor which encodes the match results.
  65. """
  66. return self._match_results
  67. def matched_column_indices(self):
  68. """Returns column indices that match to some row.
  69. The indices returned by this op are always sorted in increasing order.
  70. Returns:
  71. column_indices: int32 tensor of shape [K] with column indices.
  72. """
  73. return self._reshape_and_cast(tf.where(tf.greater(self._match_results, -1)))
  74. def matched_column_indicator(self):
  75. """Returns column indices that are matched.
  76. Returns:
  77. column_indices: int32 tensor of shape [K] with column indices.
  78. """
  79. return tf.greater_equal(self._match_results, 0)
  80. def num_matched_columns(self):
  81. """Returns number (int32 scalar tensor) of matched columns."""
  82. return tf.size(self.matched_column_indices())
  83. def unmatched_column_indices(self):
  84. """Returns column indices that do not match any row.
  85. The indices returned by this op are always sorted in increasing order.
  86. Returns:
  87. column_indices: int32 tensor of shape [K] with column indices.
  88. """
  89. return self._reshape_and_cast(tf.where(tf.equal(self._match_results, -1)))
  90. def unmatched_column_indicator(self):
  91. """Returns column indices that are unmatched.
  92. Returns:
  93. column_indices: int32 tensor of shape [K] with column indices.
  94. """
  95. return tf.equal(self._match_results, -1)
  96. def num_unmatched_columns(self):
  97. """Returns number (int32 scalar tensor) of unmatched columns."""
  98. return tf.size(self.unmatched_column_indices())
  99. def ignored_column_indices(self):
  100. """Returns column indices that are ignored (neither Matched nor Unmatched).
  101. The indices returned by this op are always sorted in increasing order.
  102. Returns:
  103. column_indices: int32 tensor of shape [K] with column indices.
  104. """
  105. return self._reshape_and_cast(tf.where(self.ignored_column_indicator()))
  106. def ignored_column_indicator(self):
  107. """Returns boolean column indicator where True means the colum is ignored.
  108. Returns:
  109. column_indicator: boolean vector which is True for all ignored column
  110. indices.
  111. """
  112. return tf.equal(self._match_results, -2)
  113. def num_ignored_columns(self):
  114. """Returns number (int32 scalar tensor) of matched columns."""
  115. return tf.size(self.ignored_column_indices())
  116. def unmatched_or_ignored_column_indices(self):
  117. """Returns column indices that are unmatched or ignored.
  118. The indices returned by this op are always sorted in increasing order.
  119. Returns:
  120. column_indices: int32 tensor of shape [K] with column indices.
  121. """
  122. return self._reshape_and_cast(tf.where(tf.greater(0, self._match_results)))
  123. def matched_row_indices(self):
  124. """Returns row indices that match some column.
  125. The indices returned by this op are ordered so as to be in correspondence
  126. with the output of matched_column_indicator(). For example if
  127. self.matched_column_indicator() is [0,2], and self.matched_row_indices() is
  128. [7, 3], then we know that column 0 was matched to row 7 and column 2 was
  129. matched to row 3.
  130. Returns:
  131. row_indices: int32 tensor of shape [K] with row indices.
  132. """
  133. return self._reshape_and_cast(
  134. self._gather_op(self._match_results, self.matched_column_indices()))
  135. def _reshape_and_cast(self, t):
  136. return tf.cast(tf.reshape(t, [-1]), tf.int32)
  137. def gather_based_on_match(self, input_tensor, unmatched_value,
  138. ignored_value):
  139. """Gathers elements from `input_tensor` based on match results.
  140. For columns that are matched to a row, gathered_tensor[col] is set to
  141. input_tensor[match_results[col]]. For columns that are unmatched,
  142. gathered_tensor[col] is set to unmatched_value. Finally, for columns that
  143. are ignored gathered_tensor[col] is set to ignored_value.
  144. Note that the input_tensor.shape[1:] must match with unmatched_value.shape
  145. and ignored_value.shape
  146. Args:
  147. input_tensor: Tensor to gather values from.
  148. unmatched_value: Constant tensor value for unmatched columns.
  149. ignored_value: Constant tensor value for ignored columns.
  150. Returns:
  151. gathered_tensor: A tensor containing values gathered from input_tensor.
  152. The shape of the gathered tensor is [match_results.shape[0]] +
  153. input_tensor.shape[1:].
  154. """
  155. input_tensor = tf.concat(
  156. [tf.stack([ignored_value, unmatched_value]),
  157. tf.to_float(input_tensor)],
  158. axis=0)
  159. gather_indices = tf.maximum(self.match_results + 2, 0)
  160. gathered_tensor = self._gather_op(input_tensor, gather_indices)
  161. return gathered_tensor
  162. class Matcher(object):
  163. """Abstract base class for matcher.
  164. """
  165. __metaclass__ = ABCMeta
  166. def __init__(self, use_matmul_gather=False):
  167. """Constructs a Matcher.
  168. Args:
  169. use_matmul_gather: Force constructed match objects to use matrix
  170. multiplication based gather instead of standard tf.gather.
  171. (Default: False).
  172. """
  173. self._use_matmul_gather = use_matmul_gather
  174. def match(self, similarity_matrix, valid_rows=None, scope=None):
  175. """Computes matches among row and column indices and returns the result.
  176. Computes matches among the row and column indices based on the similarity
  177. matrix and optional arguments.
  178. Args:
  179. similarity_matrix: Float tensor of shape [N, M] with pairwise similarity
  180. where higher value means more similar.
  181. valid_rows: A boolean tensor of shape [N] indicating the rows that are
  182. valid for matching.
  183. scope: Op scope name. Defaults to 'Match' if None.
  184. Returns:
  185. A Match object with the results of matching.
  186. """
  187. with tf.name_scope(scope, 'Match') as scope:
  188. if valid_rows is None:
  189. valid_rows = tf.ones(tf.shape(similarity_matrix)[0], dtype=tf.bool)
  190. return Match(self._match(similarity_matrix, valid_rows),
  191. self._use_matmul_gather)
  192. @abstractmethod
  193. def _match(self, similarity_matrix, valid_rows):
  194. """Method to be overridden by implementations.
  195. Args:
  196. similarity_matrix: Float tensor of shape [N, M] with pairwise similarity
  197. where higher value means more similar.
  198. valid_rows: A boolean tensor of shape [N] indicating the rows that are
  199. valid for matching.
  200. Returns:
  201. match_results: Integer tensor of shape [M]: match_results[i]>=0 means
  202. that column i is matched to row match_results[i], match_results[i]=-1
  203. means that the column is not matched. match_results[i]=-2 means that
  204. the column is ignored (usually this happens when there is a very weak
  205. match which one neither wants as positive nor negative example).
  206. """
  207. pass