最近实验室的老师布置了第一个简单作业:给出一张图片和一张由图片中人物截取出来的眼睛图片,设计一个算法,在完整图片中找到眼睛的位置。
先上直观效果
输入图片
输出图片
输入图片
输出图片
完成这个任务需要的预备知识(只需了解即可不用深入)有向量相乘的性质以及卷积的概念。
所谓卷积,简单来讲就是矩阵与矩阵对应的元素分别相乘(注意,不是矩阵乘法),一般会有一个较小的矩阵作为过滤器,从较大图片的左上角遍历到右下角,计算每个小矩阵的卷积和(小矩阵的大小和过滤矩阵相同),其中最大和对应的点就是匹配程度最高的矩阵的左上角。
为什么卷积和最大就是最匹配呢?想一想,卷积的过程是矩阵中对应的点相乘,这和矩阵展开成一维的向量再相乘结果是一样的。由向量相乘的性质可以知道:两个向量平行时可以取得相乘的最大值,因为夹角为0,cos0 = 1。两个完全相同的向量当然是平行的了,这也是其卷积和最大的原因。
但是,还是不对,你可能会发出这样的疑问,大矩阵中可以分成许多小矩阵,如果某两个矩阵的值的大小相差非常大,大到过滤器的作用也不大,这时候该怎么办呢?答案很简单,就是对于大矩阵中的每个小矩阵,在与过滤矩阵做卷积之前先减去自身的平均值,这样就可以避免值的差距过于悬殊了。
既然大矩阵里的小矩阵已经减去其平均值了,过滤矩阵也减去其平均值吧?
好,虽然没有必要这样做,但这样做却可以使计算的数值相对小一些。
公式
经过以上一波分析,再加上除去分母,其实最终版本是简单的求相关系数orz,相关系数越大则说明匹配程度越高。
至此,算法的结构就出来了,代码也很简洁,如下:
import matplotlib.pyplot as plt # plt 用于显示图片
import numpy as np
from PIL import Image
def find_piece_in_pic(whole_pic, part_pic) :
#两个参数分别是两张图片的地址
part = Image.open(part_pic)
#转化为灰度图
part = part.convert('L')
part = np.array(part).astype('float64')
whole = Image.open(whole_pic)
whole = whole.convert('L')
whole = np.array(whole).astype('float64')
H, W = whole.shape
h, w = part.shape
part = part - int(np.average(part))
res = np.zeros((whole.shape))
for r in range(H - h + 1) :
for c in range(W - w + 1) :
cur_whole = whole[r : r + h, c : c + w]
cur_whole = cur_whole - np.average(cur_whole)
temp1 = (math.sqrt(np.sum(part * part)) * math.sqrt(np.sum(cur_whole * cur_whole)))
if temp1 == 0 : continue
temp = np.sum(cur_whole * part) / temp1
res[r ,c] = temp
topr, topc = np.where(res == np.max(res))
print(topr, topc)
print(np.max(res))
plt.figure()
plt.imshow(whole, cmap='gray')#灰度图要加上这个参数
plt.gca().add_patch(plt.Rectangle((topc,topr), w, h, color='black'))
plt.show()
find_piece_in_pic('einstain.png', 'eye.png')
Matlab函数
这么简单且应用性如此高的算法当然要收录进Matlab中啦,在Matlab下就变得无比简单了,因为函数都是封装好的。
仍然以爱因斯坦和他迷人的大眼睛为例:
eye = rgb2gray(imread('eye.png'));
einstain = rgb2gray(imread('einstain.png'));
imshowpair(peppers,onion,'montage')
c = normxcorr2(eye,einstain); #就是这个函数
figure, surf(c), shading flat
[ypeak, xpeak] = find(c==max(c(:)));
yoffSet = ypeak-size(eye,1);
xoffSet = xpeak-size(eye,2);
figure
imshow(einstain);
imrect(gca, [xoffSet+1, yoffSet+1, size(eye,2), size(eye,1)]);