基于灰度投影法的图像配准(图片偏移测量)

如题

优点

1.算法简单易懂

缺点

1.对于偏移量过大的图效果差

2.效率较慢。与SIFT相比,处理同样几组1920*1080的图,灰度投影法所需时间为0.5s左右,而SIFT为0.4s左右。当然不排除个人编程能力的影响。

3.对图像有要求,需图像灰度变化明显,存在对比度,无局部移动的物体

理论

灰度投影,字面意思,将图片的灰度信息投影到一条线上(二维降一维),来作为图像的特征。投影的方法很多,我用的时将方向上的像素点的灰度相加与投影的灰度均值相减。如下面两组图,是将图像的灰度投影到水平线后的结果(数值越低,灰度越高)

很明显的从左到右的黑边渐变,将灰度沿竖直方向投影到水平面后,投影数值左低右高

同样,这只蓝的左右两侧有比较亮的背景,所以投影后左右两端的投影数值相对较高

灰度投影可以作为该图像在投影方向上的特征,如果两张图中有相似的部分,那么他们在同一方向上的灰度投影也应该类似,如下图

这两张图是从同一张图上截取的,两张图左右差了200像素,但可以看到第一张图的灰度投影200~1000和第二张图的灰度投影的0~800是相似的,根据相似的这段,我们就可以计算出图片的偏移量,结果如下,代码稍后放出

水平位移的数值大致是对的,但水平位移同样影响了在水平方向上的灰度投影,如图中让竖直位移出现了4,所以在匹配两张图的灰度投影时还需考虑相似度问题。

计算过程

img0 = cv2.imread("./图1.jpg")
img0 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
height,width = img0.shape
# 计算竖直方向投影的灰度投影
row_gray_sum0 = np.sum(img0,axis=0)
row_gray_mean0 = np.sum(row_gray_sum0)/height
row_gray_projection0 = row_gray_sum0-row_gray_mean0
# 计算水平方向投影的灰度投影
column_gray_sum0 = np.sum(img0,axis=1)
column_gray_mean0 = np.sum(column_gray_sum0)/height
column_gray_projection0 = column_gray_sum0-row_gray_mean0

用cv2对图片进行读取后用cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)将图片由彩图转化为灰度图,会返回一个包含了像素灰度信息的二维列表,用np.sum()计算灰度图在两个方向上的投影,用np.sum(row_gray_sum0)/height计算投影的平均量,最后用row_gray_projection0 = row_gray_sum0-row_gray_mean0求所需出灰度投影。与平均值相减是因为row_gray_sum0里的数据容易过大超出numpy的默认大小限制

同理对另一张图img1进行处理后就可以计算偏移量了

height0,width0 = img0.shape
height1,width1 = img1.shape
width=min(width0,width1)
dx = width//2
row_offset = np.zeros(dx)
for i in range(dx):
    for k in range(dx):
        row_offset[i] += (row_gray_projection0[i+k]-row_gray_projection1[k+dx])**2
vertical_offset=np.argmin(row_offset)-dx
print('水平位移:',vertical_offset)      # img0到img1负值为图像向左位移

dx是匹配的范围,理论上越大匹配越精准但是越慢,同时dx最大值为width//2,两张图的大小不一样的话按小的来。(row_gray_projection0[i+k]-row_gray_projection1[k+dx])**2就是对row_gray_projection0i~i+dxrow_gray_projection1dx~2dx的范围内的灰度利用方差进行匹配,灰度差距越小方差也就越小方差最小的位置i,也就是匹配出img0中的i位置和img1中的dx位置是同一位置,根据这俩的差距就可以求得位移量,也就是np.argmin(row_offset)-dx,np.argmin()会返回目标中数值最小的数的索引,也就是我们要找的i

但这样还有个缺点,得出的结果只能是负数,也就是只能算出是否只有一个方向的位移,比如我们将img0img1掉换后得到的结果就是

对此可以改成这样

height0,width0 = img0.shape
height1,width1 = img1.shape
width=min(width0,width1)
dx = width//2
row_offset = np.zeros(dx)
for i in range(dx):
    for k in range(dx):
        row_offset[i] += (row_gray_projection0[i+k]-row_gray_projection1[k+dx//2])**2
vertical_offset=np.argmin(row_offset)-dx//2
print('水平位移:',vertical_offset)      # img0到img1负值为图像向左位移

将两个dx换成了dx//2,也就是将第二张图的匹配开始位置从dx改为了dx//2,结果如下

存在一点误差,但在可接受范围内,将img0img1调换后同样也可以

竖直方向同理

注:若两张图的相似部分大于半张图,匹配效果最好,小于半张图后越小越不准,可以通过手动调小dx解决,但这个操作同样会影响到准确度。

完整代码

import cv2
import numpy as np

img0 = cv2.imread("图零")
img0 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
height0,width0 = img0.shape
# 计算竖直方向投影的灰度投影
row_gray_sum0 = np.sum(img0,axis=0)
row_gray_mean0 = np.sum(row_gray_sum0)/width0
row_gray_projection0 = row_gray_sum0-row_gray_mean0
# 计算水平方向投影的灰度投影
column_gray_sum0 = np.sum(img0,axis=1)
column_gray_mean0 = np.sum(column_gray_sum0)/height0
column_gray_projection0 = column_gray_sum0-column_gray_mean0

# 同理处理img1
img1 = cv2.imread("图一")
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
height1,width1 = img1.shape
# 计算竖直方向投影的灰度投影
row_gray_sum1 = np.sum(img1,axis=0)
row_gray_mean1 = np.sum(row_gray_sum1)/width1
row_gray_projection1 = row_gray_sum1-row_gray_mean1
# 计算水平方向投影的灰度投影
column_gray_sum1 = np.sum(img1,axis=1)
column_gray_mean1 = np.sum(column_gray_sum1)/height1
column_gray_projection1 = column_gray_sum1-column_gray_mean1


width=min(width0,width1)
dx = width//2
row_offset = np.zeros(dx)
for i in range(dx):
    for k in range(dx):
        row_offset[i] += (row_gray_projection0[i+k]-row_gray_projection1[k+dx//2])**2
horizontal_offset=np.argmin(row_offset)-dx//2

height=min(height0,height1)
dy = height//2
column_offset = np.zeros(dy)
for i in range(dy):
    for k in range(dy):
        column_offset[i] += (column_gray_projection0[i+k]-column_gray_projection1[k+dy//2])**2
vertical_offset=np.argmin(column_offset)-dy//2
print('水平位移:',horizontal_offset)      # img0到img1正值为图像右位移
print('竖直位移:',vertical_offset)      # img0到img1正值为图像向下位移
print('斜方向位移为:',(horizontal_offset**2+vertical_offset**2)**0.5)
点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注