Метод 1: изображение 'mask' перед cv2.matchTemplate
Я просто пытался создать собственную маску изображения, которое я передаю
cv2.matchTemplate , чтобы увидеть, чтовид производительности, которого я могу достичь.Чтобы было ясно, это не правильная маска - я установил все пиксели, чтобы игнорировать один цвет (черный или белый).Это делается для того, чтобы обойти тот факт, что только TM_SQDIFF и TM_CORR_NORMED поддерживают правильную маску.
@ Александр Рейнольдс очень хорошо замечает в комментариях, что нужно соблюдать осторожность, если изображение шаблона (то, что мы пытаемся сделать)найти) имеет много черного или много белого.Для многих проблем мы будем знать a priori , как выглядит шаблон, и мы можем указать белый или черный фон.
Я использую cv2.multiply , чтокажется быстрее чем numpy.multiply .cv2.multiply
имеет дополнительное преимущество, заключающееся в том, что он автоматически обрезает результаты в диапазоне от 0 до 255.
import numpy as np
import cv2
import time
img = cv2.imread('clock1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.imread('target.jpg')
t_h, t_w = template.shape[0:2] # template height and width
mask_background = 'WHITE'
start_time = time.time()
for i in range(100): # do 100 cycles for timing
# find circle in gray image using Hough transform
circles = cv2.HoughCircles(gray, method = cv2.HOUGH_GRADIENT, dp = 1,
minDist = 150, param1 = 50, param2 = 70,
minRadius = 131, maxRadius = 200)
i = circles[0,0]
x0 = i[0]
y0 = i[1]
r = i[2]
# display circle on color image
cv2.circle(img,(x0, y0), r,(0,255,0),2)
if mask_background == 'BLACK': # black = 0, white = 255 on grayscale
mask = np.zeros(img.shape, dtype = np.uint8)
elif mask_background == 'WHITE':
mask = 255*np.ones(img.shape, dtype = np.uint8)
cv2.circle(mask, (x0, y0), r, color = (1,1,1), thickness = -1)
img2 = cv2.multiply(img, mask) # element wise multiplication
# values > 255 are truncated at 255
# do the template match
result = cv2.matchTemplate(img2, template, cv2.TM_CCOEFF_NORMED)
# call minMaxLoc
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
# draw found rectangle on img
if max_val > 0.4:
cv2.rectangle(img, max_loc, (max_loc[0]+t_w, max_loc[1]+t_h), (0,255,0), 4)
fps = 100/(time.time()-start_time)
print('fps ', fps)
cv2.imwrite('output.jpg', img)
Результаты профилирования:
- ЧЕРНЫЙ фон 12,3 кадра в секунду
- БЕЛЫЙ фон 12,1 кадра в секунду
При использовании этого метода очень незначительное снижение производительности по сравнению с 12,7 кадрами в секунду в исходном вопросе.Однако у него есть недостаток, заключающийся в том, что он по-прежнему будет находить шаблоны, которые все еще немного прилипают к краю.В зависимости от точного характера проблемы, это может быть приемлемо во многих приложениях.
Метод 2: используйте cv2.boxFilter , чтобы создать маску для minMaxLoc
В этом методеМы начинаем с круговой маски (как в OP), но затем модифицируем ее с помощью cv2.boxFilter.Мы изменяем
anchor
с центра ядра по умолчанию на верхний левый угол (0, 0)
import numpy as np
import cv2
import time
img = cv2.imread('clock1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.imread('target.jpg')
t_h, t_w = template.shape[0:2] # template height and width
print('t_h, t_w ', t_h, ' ', t_w)
start_time = time.time()
for i in range(100):
# find circle in gray image using Hough transform
circles = cv2.HoughCircles(gray, method = cv2.HOUGH_GRADIENT, dp = 1,
minDist = 150, param1 = 50, param2 = 70,
minRadius = 131, maxRadius = 200)
i = circles[0,0]
x0 = i[0]
y0 = i[1]
r = i[2]
# display circle on color image
cv2.circle(img,(x0, y0), r,(0,255,0),2)
# do the template match
result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
# finally, here is the part that gets tricky. we want to find highest
# rated match inside circle and we'd like to use minMaxLoc
# start to make mask by drawing circle on zero array
mask = np.zeros(result.shape, dtype = np.float)
cv2.circle(mask, (x0, y0), r, color = 1, thickness = -1)
mask = cv2.boxFilter(mask,
ddepth = -1,
ksize = (t_w, t_h),
anchor = (0,0),
normalize = True,
borderType = cv2.BORDER_ISOLATED)
# mask now contains values from zero to 1. we want to make anything
# less than 1 equal to zero
_, mask = cv2.threshold(mask, thresh = 0.9999,
maxval = 1.0, type = cv2.THRESH_BINARY)
mask = mask.astype(np.uint8)
# call minMaxLoc
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result, mask = mask)
# draw found rectangle on img
if max_val > 0.4:
cv2.rectangle(img, max_loc, (max_loc[0]+t_w, max_loc[1]+t_h), (0,255,0), 4)
fps = 100/(time.time()-start_time)
print('fps ', fps)
cv2.imwrite('output.jpg', img)
Этот код дает маску, идентичную OP, но со скоростью 11,89 кадров в секунду.Этот метод дает нам больше точности при чуть большем увеличении производительности, чем Метод 1 .