This would mean the matching function focuses only on the ball and not on any background noise.
But you should also convert the template to grayscale to make it more distinguishable. You can see this done in "OpenCV-Python Tutorials / Image Processing in OpenCV /Template Matching": template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
.
Then manually determine the bounding box of the ball and crop the template: cropped_template = template_gray[y:y+h, x:x+w]
.
OpenCV's matchTemplate
可以取mask as an optional argument,它告诉它在匹配过程中只考虑蒙版的白色部分.
So you can create a mask for the cropped template, and pass it to matchTemplate
to only consider the ball during the matching process: mask = np.zeros(cropped_template.shape, dtype=np.uint8)
Use numpy.zeros
: ``
It will create an array of zeros with the same dimensions as template_gray
(which represents the template image converted to grayscale).
Draw a white circle on the mask where the ball is located:
cv2.circle(mask, (mask.shape[1]//2, mask.shape[0]//2), radius, 255, -1)
radius
应该被设置为与模板图像中感兴趣对象的大小相匹配的值,circle
函数将用值255
填充圆,从而在蒙版中创建一个白色区域.
你的代码是:
import cv2
import numpy as np
import matplotlib.pyplot as plt
template = cv2.imread("path_to_cropped_template.jpg")
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
x, y, w, h = 100, 100, 50, 50 # Replace with actual values
cropped_template = template_gray[y:y+h, x:x+w]
# Create a mask for the cropped template
mask = np.zeros(cropped_template.shape, dtype=np.uint8)
cv2.circle(mask, (w//2, h//2), w//2, (255), -1)
search_img = cv2.imread("path_to_search_image.jpg")
search_img_gray = cv2.cvtColor(search_img, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(search_img_gray, cropped_template, cv2.TM_CCOEFF_NORMED, mask=mask)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(search_img, top_left, bottom_right, 255, 2)
plt.imshow(cv2.cvtColor(search_img, cv2.COLOR_BGR2RGB))
plt.show()
同样,裁剪部分中的x, y, w, h
应该是实际模板图像中球的坐标和大小.
如果模板在搜索图像中多次出现,则可以添加non_max_suppression
:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from imutils.object_detection import non_max_suppression
template = cv2.imread('path_to_cropped_template.jpg', cv2.IMREAD_COLOR)
img = cv2.imread('path_to_search_image.jpg', cv2.IMREAD_COLOR)
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mask = np.zeros(template_gray.shape, dtype=np.uint8)
cv2.circle(mask, (mask.shape[1] // 2, mask.shape[0] // 2), mask.shape[1] // 2, 255, -1)
h, w = template_gray.shape[:2]
# Match template using the mask
res = cv2.matchTemplate(img_gray, template_gray, cv2.TM_CCOEFF_NORMED, mask=mask)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
top_left = min_loc if res.min() == min_val else max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img, top_left, bottom_right, (255, 0, 0), 2)
# Apply non-maximum suppression to the bounding boxes
rects = [[*top_left, *bottom_right]]
pick = non_max_suppression(np.array(rects))
for (startX, startY, endX, endY) in pick:
cv2.rectangle(img, (startX, startY), (endX, endY), (0, 255, 0), 2)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('Detected Point')
plt.show()
这不能通过模板匹配来解决.你可以看到不同的3D物体.
I agree: template matching is generally well-suited for 2D image patterns that do not change perspective or shape. It would assume the template and the portion of the image being searched are similarly oriented and scaled.
When the object in question is three-dimensional, like a soccer ball, and the images capture it from different angles or under different lighting conditions, the template's appearance might change due to perspective distortion, lighting changes, and occlusions.
您可以try 多尺度模板匹配和蒙版的组合,这将比简单的模板匹配更强大,但仍然不是匹配3D对象图像的包罗万象的解决方案,这些图像可能由于旋转、透视偏移和其他因素而显得非常不同.
import cv2
import numpy as np
import matplotlib.pyplot as plt
def multi_scale_template_matching_with_mask(search_img, template, template_mask, scale_steps=20, method=cv2.TM_CCOEFF_NORMED):
h, w = template.shape[:2]
# keep track of the most accurate matching
found = None
search_img_gray = cv2.cvtColor(search_img, cv2.COLOR_BGR2GRAY)
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
for scale in np.linspace(0.2, 1.0, scale_steps)[::-1]:
# Resize the image according to the scale, and keep track of the ratio of the resizing
resized = cv2.resize(search_img_gray, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
r = search_img_gray.shape[1] / float(resized.shape[1])
# If the resized image is smaller than the template, then break from the loop
if resized.shape[0] < h or resized.shape[1] < w:
break
# Apply template Matching with the mask
result = cv2.matchTemplate(resized, template_gray, method, mask=template_mask)
_, max_val, _, max_loc = cv2.minMaxLoc(result)
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
matchLoc = min_loc
else:
matchLoc = max_loc
# If we have found a new maximum correlation value, then update the bookkeeping variable
if found is None or max_val > found[0]:
found = (max_val, matchLoc, r)
# Unpack the bookkeeping variable and compute the (x, y) coordinates of the bounding box
_, max_loc, r = found
start_x, start_y = int(max_loc[0] * r), int(max_loc[1] * r)
end_x, end_y = int((max_loc[0] + w) * r), int((max_loc[1] + h) * r)
cv2.rectangle(search_img, (start_x, start_y), (end_x, end_y), (255, 0, 0), 2)
plt.imshow(cv2.cvtColor(search_img, cv2.COLOR_BGR2RGB))
plt.show()
search_img = cv2.imread('path_to_search_image.jpg')
template = cv2.imread('path_to_template.jpg')
template_mask = cv2.imread('path_to_template_mask.jpg', 0) # Assuming the mask is a grayscale image
multi_scale_template_matching_with_mask(search_img, template, template_mask)
multi_scale_template_matching_with_mask
将try 跨不同尺度在搜索图像中找到模板,同时使用掩模将匹配集中在模板的特定特征上(例如,球).