【计算机视觉(五)】模板匹配

前请提要

前三期(【计算机视觉(二)】常用颜色空间及其转换)、【计算机视觉(三)】形态学处理【计算机视觉(四)】轮廓检测)在介绍基本知识的同时穿插了一个很naive的检测车牌位置的方法,对参数设置有很强的依赖。后续会慢慢涉及到高级的方法。

本期内容

本期作为一个引子介绍模板匹配法,引出后来的Cascade检测算法。

一、全等模板

car_test.jpg

假如我们要在上面这张固定的图上找出左边这辆车的位置,一个很简单的方法就是我们可以先人工把左边这辆车用“截图”工具裁剪出来。
template.jpg

然后拿着这张图在大图上找,一开始,我们把模板图放到大图的左上角,看它们是不是所有像素值都相等,然后往右移一个像素,再看是不是所有值相等,如此遍历整幅图,直到找到全等的位置。
sw1.jpg

sw2.jpg

这样的搜寻策略叫做滑动窗口
template_match_result.jpg

实现代码如下:

# coding: utf-8
import cv2
import numpy as np

'''
函数名:template_match
输入:
template    模板
img   原图
输出:
(x,y)    匹配位置的左上角坐标,找不到返回None
'''
def template_match(template, img):
    tpl_h, tpl_w = template.shape[:2]
    img_h, img_w = img.shape[:2]
    for i in xrange(img_h - tpl_h):
        for j in xrange(img_w - tpl_w):
            roi = img[i:i+tpl_h, j:j+tpl_w]
            if (template == roi).all():
                return (j,i)
    return None

# 程序入口
def main():
    # 整图
    cars = cv2.imread('car_test.jpg')
    # 车模板
    car_tpl = cars[117:199, 24:246].copy()

    pos = template_match(car_tpl, cars)
    tpl_h, tpl_w = car_tpl.shape[:2]

    if pos is not None:
        x,y = pos
        cv2.rectangle(cars, (x,y), (x+tpl_w,y+tpl_h), (0,255,0), 2)

    cv2.imshow('template', car_tpl)
    cv2.imshow('result', cars)
    cv2.imwrite('template_match_result.jpg', cars)
    cv2.waitKey(0)

if __name__ == '__main__':
    main()

这样做的缺点很明显,只要有一个像素点跟模板不相等就找不到了,如果只是要找些非常固定的东西那用这种方法是可以的,比如以前做游戏脚本的时候要实现鼠标点到某个固定的技能或道具上。

二、相关系数

这时候要用更靠谱的测度——例如相关系数(除此之外还有平方差等)。
图像的相关系数的计算方法参考这个公式,是从Matlab的文档截下来的。

相关系数公式

分子是图像与模板的协方差,分母是它们的标准差的乘积。
具体原理参考知乎的这个回答
它会返回一个数值表示图像的相关程度,越相关,值越靠近1,实现代码如下:

# 输入灰度图
def norm_corr(template, img):
    tpl_h, tpl_w = template.shape[:2]
    img_h, img_w = img.shape[:2]
    expand_img = np.zeros((tpl_h+img_h, tpl_w+img_w), dtype=np.uint8)
    expand_img[:img_h, :img_w] = img
    img_h, img_w = expand_img.shape[:2]

    # 图像均值
    tpl_mean = np.mean(template)
    # 减均值
    tpl_sub_mean = template - tpl_mean
    # 标准差
    sigma_tpl = np.sum(tpl_sub_mean**2)
    # 相关系数图
    corr = np.zeros(img.shape, dtype=np.float32)

    for i in xrange(img_h - tpl_h):
        for j in xrange(img_w - tpl_w):
            roi = expand_img[i:i+tpl_h, j:j+tpl_w]
            # 图像均值
            roi_mean = np.mean(roi)
            # 减均值
            roi_sub_mean = roi - roi_mean
            # 标准差
            sigma_roi = np.sum(roi_sub_mean**2)
            # 协方差
            cov = np.sum(tpl_sub_mean * roi_sub_mean)
            # 相关系数
            corr[i,j] = cov / np.sqrt(sigma_tpl * sigma_roi)

    # 归一化到0-255
    corr_max = corr.max()
    corr_min = corr.min()
    print 'max = ', corr_max
    print 'min = ', corr_min
    if corr_max != corr_min:
        corr = 255 * (corr - corr_min) / (corr_max - corr_min)
    return corr.astype(np.uint8)

要注意输入是灰度图,最后输出是一张表示每一个位置上的相关系数的图,归一化到0-255就可以显示出来了。再次使用上面的模板图和大图,得到的相关系数图如下:

相关系数图

图中越亮的地方表示把模板的左上角在大图的对应位置的可能性越大。
我们可以把相关图上值超过127(最大255,可以自行设置这个阈值)的地方用矩形框标出来。
代码如下:

idx = np.where(corr > 0.5 * 255)
rows = idx[0]
cols = idx[1]
rects = []
for r,c in zip(rows, cols):
    if c+tpl_w < img_w and r+tpl_h < img_h:
        rects.append((c,r,c+tpl_w,r+tpl_h))

for rect in rects:
    tx,ty,bx,by = rect
    cv2.rectangle(cars, (tx,ty), (bx,by), (0,255,0), 2)

效果如下:


所有框

可以看到虽然用的模板是左边的车,但由于两台车很相似,所以右边的车也被检出来了,但是框非常的多,我们可以想办法做去重,把顶点靠近的矩形框当作一个子集,最后分别给出各个矩形集的平均位置,代码如下,可以根据需要子集改进:

def group_rects(rects, diff=20):
    groups = [[rects[0]]]
    for rect in rects[1:]:
        tx,ty,bx,by = rect
        found = False
        for gr in groups:
            for gre in gr:
                if abs(gre[0]-tx) < diff and abs(gre[1]-ty) < diff and abs(gre[2]-bx) < diff and abs(gre[3]-by) < diff:
                    gr.append(rect)
                    found = True
                    break
            if found:
                break
        if not found:
            groups.append([rect])
    result = []
    for group in groups:
        result.append(np.array(group).mean(axis=0).astype(np.int32).tolist())
    return result

这样就得到了很好的两个框。


框融合结果

总结

即使改变了使用的距离函数,我们也只使用了一个数据(图像)就希望能检测到其他的同类物体,的确是很以偏概全的想法。以检测车子的例子来说,我们是把一台特定的车当作模板,而没有从更接近本质的角度去解构这个问题,假设我们能人为的定义车子该长什么样也许就能很好的解决这个问题,比如说车有轮子、车窗、车灯、整体是流线型的,等等。这些性质可以称为车子的特征(feature)。计算机视觉有很大部分研究的问题都在围绕着如何更好的描述物体,也就是如何得到物体更好的特征。特征可能是级联的,意思是说有轮子是车子的特征,轮子也有自己的特征(圆的,黑的),特征总是从低层的只包含结构、形状等信息往高层的更不可描述的信息走。发展到现在,特征可以分为人为设计和通过机器学习的方法学习出来两种,人为设计的特征如HOG、Haar等,有着固定的计算过程,可以手工计算出来,在深度学习大热的当今已经慢慢被新晋行业的人视为“传统的技术”,但在我看来这两者并没有那么大的不同,没有必要放弃这些“old-school”的技术,这样只会造成“拿起锤子,看什么都是钉子”的想法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容

  • 不同图像灰度不同,边界处一般会有明显的边缘,利用此特征可以分割图像。需要说明的是:边缘和物体间的边界并不等同,边缘...
    大川无敌阅读 13,815评论 0 29
  • 这些年计算机视觉识别和搜索这个领域非常热闹,后期出现了很多的创业公司,大公司也在这方面也花了很多力气在做。做视觉搜...
    方弟阅读 6,436评论 6 24
  • 前天晚上被头条上关于电影《无问西东》的评论吸引,也到电影院观影,可以说,这个冬天最温暖、最文艺的两部电影,一部是《...
    傍晚的早晨阅读 213评论 0 0
  • 一周的践行又到了尾声,每周写践行记录似乎也变的麻木起来,重复次数的增加都给我们带来了哪方面的进步呢? 英语 最近一...
    穆建园阅读 224评论 0 0
  • 是一个月以前的一次旅行了,我在台湾交换学习,周末有时间就出去走走。前一天已经到了花莲,不过没玩什么地方,第二天与朋...
    双木中堂阅读 483评论 5 10