社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

我只用半小时 | Python手写了个图像模板匹配算法

小白学视觉 • 3 年前 • 729 次点击  
点击上方小白学视觉”,选择加"星标"或“置顶
重磅干货,第一时间送达

微信公众号:OpenCV学堂

关注获取更多计算机视觉与深度学习知识

OpenCV中的模板匹配

OpenCV中的模板匹配是支持基于NCC相似度查找的,但是不是很好用,一个主要的原因是查找最大阈值,只能匹配一个,自己比对阈值,又导致无法正确设定阈值范围,所以问题很多。于是我重新写了纯Python版本的NCC图像模板匹配的代码实现了一个Python版本的,简单易用,支持多尺度,跟多进程并行!

主要思想

主要是基于NCC实现的像素相似度计算,这个OpenCV官方的模板匹配也有这中方式像素相似度计算支持,它的公式描述如下:

就是参照这个公式,然后基于OpenCV提供的积分图计算函数,实现了NCC相似度比较计算,值在0~1之间,1表示完全相似,0表示完全不相似。

代码实现

我把整个部分搞成了一个类,调用的方法主要是run_match,就可以直接运行,完成模板匹配。大体的功能跟OpenCV实现的模板匹配功能比较相似,改进的地方就是比较方便的实现多个对象匹配的直接输出Box框。该类完整的代码实现如下:
import cv2 as cv
import numpy as np
import time
import  concurrent.futures

class NCCTemplateMatch:
    def __init__(self, ref_imgs, target_imgs, scores, tpl_sums, tpl_sqr_sums, target_sums, target_sqr_sums):
        self.ref_imgs = ref_imgs
        self.target_imgs = target_imgs
        self.scores = scores
        self.tpls_sums = tpl_sums
        self.tpls_sqsums = tpl_sqr_sums
        self.target_sums = target_sums
        self.target_sqsums = target_sqr_sums
        self.nms_boxes = []

    def run_match(self):
        num_ps = min(6, len(self.ref_imgs))
        # print("num_ps: ", num_ps)
        start = time.perf_counter()
        with concurrent.futures.ProcessPoolExecutor(num_ps) as executor:
            matched = executor.map(self.ncc_run, self.ref_imgs, self.target_imgs, self.tpls_sums, self.tpls_sqsums, self.target_sums, self.target_sqsums, self.scores)
            self.nms_boxes = list(matched)
        end = time.perf_counter()
        print(f'Finished in {round(end-start, 2)} seconds')

    def ncc_run(self, tpl_gray, target_gray, tpl_sum, tpl_sqsum, target_sum, target_sqsum, score):
        print("run once~~~~")
        th, tw = tpl_gray.shape
        min_step = max(1, min(th // 16, tw // 16))
        h, w = target_gray.shape
        sr = 1 / (th * tw)
        t_s1 = tpl_sum[th, tw]
        t_s1_2 = t_s1 * t_s1 * sr
        t_s1_1 = t_s1 * sr
        t_s2 = tpl_sqsum[th, tw]
        sum_t = np.sqrt(t_s2 - t_s1_2)
        row = 0
        boxes = []
        confidences = []
        while row 1):
            col = 0
            while col 1):
                s1 = self.get_block_sum(target_sum, col, row, col + tw, row + th)
                s2 = self.get_block_sum(target_sqsum, col, row, col + tw, row + th)
                sum1 = t_s1_1 * s1
                ss_sqr = s2 - s1 * s1 * sr
                if ss_sqr 0:  # fix issue, 精度问题
                    ss_sqr = 0.0
                sum2 = sum_t * np.sqrt(ss_sqr)
                sum3 = np.sum(np.multiply(tpl_gray, target_gray[row:row + th, col:col + tw]))
                if sum2 == 0.0:
                    ncc = 0.0
                else:
                    ncc = (sum3 - sum1) / sum2
                if ncc > score:
                    boxes.append([col, row, tw, th])
                    confidences.append(float(ncc))
                    col += tw//2
                else:
                    col += min_step
            row += min_step

        # NMS Process
        nms_indices = cv.dnn.NMSBoxes(boxes, confidences, 0.50.5)
        det_boxes = []
        print(nms_indices)
        for i in range(len(nms_indices)):
            rect_box = boxes[nms_indices[i]]
            det_boxes.append(rect_box)
        return det_boxes

    def get_block_sum(self, integal_img, x1, y1, x2, y2):
        t1 = integal_img[y1, x1]
        t2 = integal_img[y1, x2]
        t3 = integal_img[y2, x1]
        t4 = integal_img[y2, x2]
        s = t4 - t2 - t3 + t1
        return s

相关的测试与调用代码如下:
print("test ncc......")
tpl_image = cv.imread("D:/images/llk_tpl.png")
target_image = cv.imread("D:/images/llk.jpg")

tpl_gray = cv.cvtColor(tpl_image, cv.COLOR_BGR2GRAY)
target_gray = cv.cvtColor(target_image, cv.COLOR_BGR2GRAY)
tpl_gray = np.float32(tpl_gray / 255.0)
target_gray = np.float32(target_gray / 255.0)
tpl_sum, tpl_sqsum = cv.integral2(tpl_gray)
t_sum, t_sqsum = cv.integral2(target_gray)
matcher = NCCTemplateMatch([tpl_gray], [target_gray], [0.85],
                           [tpl_sum], [tpl_sqsum], [t_sum], [t_sqsum])
matcher.run_match()
for rect_box in matcher.nms_boxes[0]:
    cv.rectangle(target_image, (rect_box[0], rect_box[1]),
                 (rect_box[0]+rect_box[2], rect_box[1]+rect_box[3]), (00255), 280)
cv.imshow("result", target_image)
cv.waitKey(0)
cv.destroyAllWindows()

模板图像:


运行结果如下:



好消息!

小白学视觉知识星球

开始面向外开放啦👇👇👇




下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲
小白学视觉公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲
小白学视觉公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群


欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/154101