当前位置:   article > 正文

openCV-python模板匹配(旋转)_支持旋转的模板匹配方法

支持旋转的模板匹配方法

本文将介绍使用OpenCV实现多角度模板匹配的详细步骤 + 代码。

背景介绍

熟悉OpenCV的朋友肯定都知道OpenCV自带的模板匹配matchTemplate方法是不支持旋转的,也就是说当目标和模板有角度差异时匹配常常会失败,可能目标只是轻微的旋转,匹配分数就会下降很多,导致匹配精度下降甚至匹配出错。本文介绍基于matchTemplate + 旋转 + 金字塔下采样实现多角度的模板匹配,返回匹配结果(坐标、角度)。

实现思路

【1】如何适应目标的角度变化?我们可以将模板旋转,从0~360°依次匹配找到最佳的匹配位置;

【2】如何提高匹配速度?使用金字塔下采样,将模板和待匹配图均缩小后匹配;加大匹配搜寻角度的步长,比如从每1°匹配一次改为每5°匹配一次等。

实现步骤

【1】旋转模板图像。旋转图像本身比较简单,下面是代码:

  1. # 图片旋转函数
  2. def ImageRotate(img, angle): # img:输入图片;newIm:输出图片;angle:旋转角度(°)
  3. height, width = img.shape[:2] # 输入(H,W,C),取 H,W 的值
  4. center = (width // 2, height // 2) # 绕图片中心进行旋转
  5. M = cv.getRotationMatrix2D(center, angle, 1.0)
  6. image_rotation = cv.warpAffine(img, M, (width, height))
  7. return image_rotation

但需要注意,很多时候按照上面方法旋转时,会丢失模板信息产生黑边,这里是进行裁剪模板为圆形ROI

  1. # 取圆形ROI区域函数:具体实现功能为输入原图,取原图最大可能的原型区域输出
  2. def circle_tr(src):
  3. dst = np.zeros(src.shape, np.uint8) # 感兴趣区域ROI
  4. mask = np.zeros(src.shape, dtype='uint8') # 感兴趣区域ROI
  5. (h, w) = mask.shape[:2]
  6. (cX, cY) = (w // 2, h // 2) # 是向下取整
  7. radius = int(min(h, w) / 2)
  8. cv.circle(mask, (cX, cY), radius, (255, 255, 255), -1)
  9. # 以下是copyTo的算法原理:
  10. # 先遍历每行每列(如果不是灰度图还需遍历通道,可以事先把mask图转为灰度图)
  11. for row in range(mask.shape[0]):
  12. for col in range(mask.shape[1]):
  13. # 如果掩图的像素不等于0,则dst(x,y) = scr(x,y)
  14. if mask[row, col] != 0:
  15. # dst_image和scr_Image一定要高宽通道数都相同,否则会报错
  16. dst[row, col] = src[row, col]
  17. # 如果掩图的像素等于0,则dst(x,y) = 0
  18. elif mask[row, col] == 0:
  19. dst[row, col] = 0
  20. return dst

【2】图像金字塔下采样。什么是图像金字塔?什么是上下采样?直接百度。

减小图像分辨率提高图像匹配速度,代码如下:

  1. # 金字塔下采样
  2. def ImagePyrDown(image,NumLevels):
  3. for i in range(NumLevels):
  4. image = cv.pyrDown(image) #pyrDown下采样
  5. return image

【3】0~360°各角度匹配。旋转模板图像,依次调用matchTemplate在目标图中匹配,记录最佳匹配分数,以及对应的角度。旋转匹配代码:

  1. # 旋转匹配函数(输入参数分别为模板图像、待匹配图像)
  2. def RatationMatch(modelpicture, searchpicture):
  3. searchtmp = []
  4. modeltmp = []
  5. searchtmp = ImagePyrDown(searchpicture, 3)
  6. modeltmp = ImagePyrDown(modelpicture, 3)
  7. newIm = circle_tr(modeltmp)
  8. # 使用matchTemplate对原始灰度图像和图像模板进行匹配
  9. res = cv.matchTemplate(searchtmp, newIm, cv.TM_SQDIFF_NORMED)
  10. min_val, max_val, min_indx, max_indx = cv.minMaxLoc(res)
  11. location = min_indx
  12. temp = min_val
  13. angle = 0 # 当前旋转角度记录为0
  14. tic = time.time()
  15. # 以步长为5进行第一次粗循环匹配
  16. for i in range(-180, 181, 5):
  17. newIm = ImageRotate(modeltmp, i)
  18. newIm = circle_tr(newIm)
  19. res = cv.matchTemplate(searchtmp, newIm, cv.TM_SQDIFF_NORMED)
  20. min_val, max_val, min_indx, max_indx = cv.minMaxLoc(res)
  21. if min_val < temp:
  22. location = min_indx
  23. temp = min_val
  24. angle = i
  25. toc = time.time()
  26. print('第一次粗循环匹配所花时间为:' + str(1000*(toc - tic)) + 'ms')
  27. tic = time.time()
  28. # 在当前最优匹配角度周围10的区间以1为步长循环进行循环匹配计算
  29. for j in range(angle-5, angle+6):
  30. newIm = ImageRotate(modeltmp, j)
  31. newIm = circle_tr(newIm)
  32. res = cv.matchTemplate(searchtmp, newIm, cv.TM_SQDIFF_NORMED)
  33. min_val, max_val, min_indx, max_indx = cv.minMaxLoc(res)
  34. if min_val < temp:
  35. location = min_indx
  36. temp = min_val
  37. angle = j
  38. toc = time.time()
  39. print('在当前最优匹配角度周围10的区间以1为步长循环进行循环匹配所花时间为:' + str(1000*(toc - tic)) + 'ms')
  40. tic = time.time()
  41. # 在当前最优匹配角度周围2的区间以0.1为步长进行循环匹配计算
  42. k_angle = angle - 0.9
  43. for k in range(0, 19):
  44. k_angle = k_angle + 0.1
  45. newIm = ImageRotate(modeltmp, k_angle)
  46. newIm = circle_tr(newIm)
  47. res = cv.matchTemplate(searchtmp, newIm, cv.TM_SQDIFF_NORMED)
  48. min_val, max_val, min_indx, max_indx = cv.minMaxLoc(res)
  49. if min_val < temp:
  50. location = min_indx
  51. temp = min_val
  52. angle = k_angle
  53. toc = time.time()
  54. print('在当前最优匹配角度周围2的区间以0.1为步长进行循环匹配所花时间为:' + str(1000*(toc - tic)) + 'ms')
  55. # 用下采样前的图片来进行精匹配计算
  56. k_angle = angle - 0.1
  57. newIm = ImageRotate(modelpicture, k_angle)
  58. newIm = circle_tr(newIm)
  59. res = cv.matchTemplate(searchpicture, newIm, cv.TM_CCOEFF_NORMED)
  60. min_val, max_val, min_indx, max_indx = cv.minMaxLoc(res)
  61. location = max_indx
  62. temp = max_val
  63. angle = k_angle
  64. for k in range(1, 3):
  65. k_angle = k_angle + 0.1
  66. newIm = ImageRotate(modelpicture, k_angle)
  67. newIm = circle_tr(newIm)
  68. res = cv.matchTemplate(searchpicture, newIm, cv.TM_CCOEFF_NORMED)
  69. min_val, max_val, min_indx, max_indx = cv.minMaxLoc(res)
  70. if max_val > temp:
  71. location = max_indx
  72. temp = max_val
  73. angle = k_angle
  74. location_x = location[0] + 50
  75. location_y = location[1] + 50
  76. # 前面得到的旋转角度是匹配时模板图像旋转的角度,后面需要的角度值是待检测图像应该旋转的角度值,故需要做相反数变换
  77. angle = -angle
  78. match_point = {'angle': angle, 'point': (location_x, location_y)}
  79. return match_point

【4】标注匹配结果。根据模板图大小、匹配结果角度画出匹配框,代码如下:

  1. # 画图
  2. def draw_result(src, temp, match_point):
  3. cv.rectangle(src, match_point,
  4. (match_point[0] + temp.shape[1], match_point[1] + temp.shape[0]),
  5. (0, 255, 0), 2)
  6. cv.imshow('result', src)
  7. cv.waitKey()

【5】调用。对输入图像做预处理,并输出匹配到的结果,代码如下:

  1. def get_realsense(src, temp):
  2. ModelImage = temp
  3. SearchImage = srcx
  4. ModelImage_edge = cv.GaussianBlur(ModelImage, (5, 5), 0)
  5. ModelImage_edge = cv.Canny(ModelImage_edge, 10, 200, apertureSize=3)
  6. SearchImage_edge = cv.GaussianBlur(SearchImage, (5, 5), 0)
  7. (h1, w1) = SearchImage_edge.shape[:2]
  8. SearchImage_edge = cv.Canny(SearchImage_edge, 10, 180, apertureSize=3)
  9. serch_ROIPart = SearchImage_edge[50:h1 - 50, 50:w1 - 50] # 裁剪图像
  10. tic = time.time()
  11. match_points = RatationMatch(ModelImage_edge, serch_ROIPart)
  12. toc = time.time()
  13. print('匹配所花时间为:' + str(1000 * (toc - tic)) + 'ms')
  14. print('匹配的最优区域的起点坐标为:' + str(match_points['point']))
  15. print('相对旋转角度为:' + str(match_points['angle']))
  16. TmpImage_edge = ImageRotate(SearchImage_edge, match_points['angle'])
  17. cv.imshow("TmpImage_edge", TmpImage_edge)
  18. cv.waitKey()
  19. draw_result(SearchImage, ModelImage_edge, match_points['point'])
  20. return match_points

【6】举例演示。模板图从下图中截取并保存 template.png:

测试图像6张,匹配结果:

角度误差在正负1度左右。

后记

可以在此基础上添加匹配分数阈值和NMS实现多目标匹配。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/293447
推荐阅读
相关标签
  

闽ICP备14008679号