template_tracker.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #!/usr/bin/python
  2. """ template_tracker.py - Version 1.1 2013-12-20
  3. Based on the C++ FastMatchTemplate code by Tristen Georgiou:
  4. http://opencv.willowgarage.com/wiki/FastMatchTemplate
  5. """
  6. from rbx1_vision.ros2opencv2 import ROS2OpenCV2
  7. from sensor_msgs.msg import Image, RegionOfInterest
  8. from geometry_msgs.msg import Point
  9. import sys
  10. import cv2.cv as cv
  11. import cv2
  12. import numpy as np
  13. from math import pow
  14. class TemplateTracker(ROS2OpenCV2):
  15. def __init__(self, node_name):
  16. ROS2OpenCV2.__init__(self, node_name)
  17. self.match_threshold = rospy.get_param("~match_threshold", 0.7)
  18. self.find_multiple_targets = rospy.get_param("~find_multiple_targets", False)
  19. self.n_pyr = rospy.get_param("~n_pyr", 2)
  20. self.min_template_size = rospy.get_param("~min_template_size", 25)
  21. self.scale_factor = rospy.get_param("~scale_factor", 1.2)
  22. self.scale_and_rotate = rospy.get_param("~scale_and_rotate", False)
  23. self.use_depth_for_detection = rospy.get_param("~use_depth_for_detection", False)
  24. self.fov_width = rospy.get_param("~fov_width", 1.094)
  25. self.fov_height = rospy.get_param("~fov_height", 1.094)
  26. self.max_object_size = rospy.get_param("~max_object_size", 0.28)
  27. # Intialize the detection box
  28. self.detect_box = None
  29. self.detector_loaded = False
  30. rospy.loginfo("Waiting for video topics to become available...")
  31. # Wait until the image topics are ready before starting
  32. rospy.wait_for_message("input_rgb_image", Image)
  33. if self.use_depth_for_detection:
  34. rospy.wait_for_message("input_depth_image", Image)
  35. rospy.loginfo("Ready.")
  36. def process_image(self, cv_image):
  37. # STEP 1. Load a detector if one is specified
  38. if not self.detector_loaded:
  39. self.detector_loaded = self.load_template_detector()
  40. # STEP 2: Detect the object
  41. self.detect_box = self.match_template(cv_image)
  42. return cv_image
  43. def load_template_detector(self):
  44. try:
  45. """ Read in the template image """
  46. template_file = rospy.get_param("~template_file", "")
  47. self.template = cv2.imread(template_file, cv.CV_LOAD_IMAGE_COLOR)
  48. cv2.imshow("Template", self.template)
  49. if self.scale_and_rotate:
  50. """ Compute the min and max scales """
  51. width_ratio = float(self.frame_size[0]) / self.template.shape[0]
  52. height_ratio = float(self.frame_size[1]) / self.template.shape[1]
  53. max_scale = 0.9 * min(width_ratio, height_ratio)
  54. max_template_dimension = max(self.template.shape[0], self.template.shape[1])
  55. min_scale = 1.1 * float(self.min_template_size) / max_template_dimension
  56. self.scales = list()
  57. scale = min_scale
  58. while scale < max_scale:
  59. self.scales.append(scale)
  60. scale *= self.scale_factor
  61. self.rotations = [-45, 0, 45]
  62. else:
  63. self.scales = [1]
  64. self.rotations = [0]
  65. self.last_scale = 0 # index in self.scales
  66. self.last_rotation = 0
  67. #self.rotations = [0]
  68. print self.scales
  69. print self.rotations
  70. return True
  71. except:
  72. rospy.loginfo("Exception loading face detector!")
  73. return False
  74. def match_template(self, frame):
  75. H,W = frame.shape[0], frame.shape[1]
  76. h,w = self.template.shape[0], self.template.shape[1]
  77. # Make sure that the template image is smaller than the source
  78. if W < w or H < h:
  79. rospy.loginfo( "Template image must be smaller than video frame." )
  80. return None
  81. if frame.dtype != self.template.dtype:
  82. rospy.loginfo("Template and video frame must have same depth and number of channels.")
  83. return None
  84. # Create a copy of the frame to modify
  85. frame_copy = frame.copy()
  86. for i in range(self.n_pyr):
  87. frame_copy = cv2.pyrDown(frame_copy)
  88. template_height, template_width = self.template.shape[:2]
  89. # Cycle through all scales starting with the last successful scale
  90. scales = self.scales[self.last_scale:] + self.scales[:self.last_scale - 1]
  91. # Track which scale and rotation gives the best match
  92. maxScore = -1
  93. best_s = 1
  94. best_r = 0
  95. best_x = 0
  96. best_y = 0
  97. for s in self.scales:
  98. for r in self.rotations:
  99. # Scale the template by s
  100. template_copy = cv2.resize(self.template, (int(template_width * s), int(template_height * s)))
  101. # Rotate the template through r degrees
  102. rotation_matrix = cv2.getRotationMatrix2D((template_copy.shape[1]/2, template_copy.shape[0]/2), r, 1.0)
  103. template_copy = cv2.warpAffine(template_copy, rotation_matrix, (template_copy.shape[1], template_copy.shape[0]), borderMode=cv2.BORDER_REPLICATE)
  104. # Use pyrDown() n_pyr times on the scaled and rotated template
  105. for i in range(self.n_pyr):
  106. template_copy = cv2.pyrDown(template_copy)
  107. # Create the results array to be used with matchTempate()
  108. h,w = template_copy.shape[:2]
  109. H,W = frame_copy.shape[:2]
  110. result_width = W - w + 1
  111. result_height = H - h + 1
  112. try:
  113. result_mat = cv.CreateMat(result_height, result_width, cv.CV_32FC1)
  114. result = np.array(result_mat, dtype = np.float32)
  115. except:
  116. continue
  117. # Run matchTemplate() on the reduced images
  118. cv2.matchTemplate(frame_copy, template_copy, cv.CV_TM_CCOEFF_NORMED, result)
  119. # Find the maximum value on the result map
  120. (minValue, maxValue, minLoc, maxLoc) = cv2.minMaxLoc(result)
  121. if maxValue > maxScore:
  122. maxScore = maxValue
  123. best_x, best_y = maxLoc
  124. best_s = s
  125. best_r = r
  126. best_template = template_copy.copy()
  127. self.last_scale = self.scales.index(s)
  128. best_result = result.copy()
  129. # Transform back to original image sizes
  130. best_x *= int(pow(2.0, self.n_pyr))
  131. best_y *= int(pow(2.0, self.n_pyr))
  132. h,w = self.template.shape[:2]
  133. h = int(h * best_s)
  134. w = int(w * best_s)
  135. best_result = cv2.resize(best_result, (int(pow(2.0, self.n_pyr)) * best_result.shape[1], int(pow(2.0, self.n_pyr)) * best_result.shape[0]))
  136. display_result = np.abs(best_result)**3
  137. cv2.imshow("Result", display_result)
  138. best_template = cv2.resize(best_template, (int(pow(2.0, self.n_pyr)) * best_template.shape[1], int(pow(2.0, self.n_pyr)) * best_template.shape[0]))
  139. cv2.imshow("Best Template", best_template)
  140. #match_box = ((best_x + w/2, best_y + h/2), (w, h), -best_r)
  141. return (best_x, best_y, w, h)
  142. if __name__ == '__main__':
  143. try:
  144. node_name = "template_tracker"
  145. TemplateTracker(node_name)
  146. rospy.spin()
  147. except KeyboardInterrupt:
  148. print "Shutting down fast match template node."
  149. cv.DestroyAllWindows()