Python进行SIFT图像对准

基础方法

SIFT特征点和特征描述提取

SIFT算法广泛使用在计算机视觉领域,在OpenCV中也对其进行了实现。

import cv2 
#这里使用的Python 3
def sift_kp(image):
    gray_image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    sift = cv2.xfeatures2d_SIFT.create()
    kp,des = sift.detectAndCompute(image,None)
    kp_image = cv2.drawKeypoints(gray_image,kp,None)
    return kp_image,kp,des

SIFT特征点匹配

SIFT算法得到了图像中的特征点以及相应的特征描述,如何把两张图像中的特征点匹配起来呢?一般的可以使用K近邻(KNN)算法。K近邻算法求取在空间中距离最近的K个数据点,并将这些数据点归为一类。在进行特征点匹配时,一般使用KNN算法找到最近邻的两个数据点,如果最接近和次接近的比值大于一个既定的值,那么我们保留这个最接近的值,认为它和其匹配的点为good match(有Lowe在SIFT论文中提出)。

import cv2
def get_good_match(des1,des2):
    bf = cv2.BFMatcher()
    matches = bf.knnMatch(des1, des2, k=2)
    good = []
    for m, n in matches:
        if m.distance < 0.75 * n.distance:
            good.append(m)
    return good

单应性矩阵Homography Matrix

通过上面的步骤,我们找到了若干两张图中的匹配点,如何将其中一张图通过旋转、变换等方式将其与另一张图对齐呢?这就用到了单应性矩阵了。Homography这个词由Homo和graphy,Homo意为同一,graphy意为图像,也就是同一个东西产生的图像。不同视角的图像上的点具有如下关系:

其中[x1 y1 1][x2 y2 1]分别表示对应像素的齐次坐标。单应性矩阵就是下面这个矩阵:

单应性矩阵

可以看到,单应性矩阵有八个参数,如果要解这八个参数的话,需要八个方程,由于每一个对应的像素点可以产生2个方程(x一个,y一个),那么总共只需要四个像素点就能解出这个单应性矩阵。

在Opencv-Python中可以使用如下方式:

import cv2
H, status = cv2.findHomography(ptsA,ptsB,cv2.RANSAC,ransacReprojThreshold)
#其中H为求得的单应性矩阵矩阵
#status则返回一个列表来表征匹配成功的特征点。
#ptsA,ptsB为关键点
#cv2.RANSAC, ransacReprojThreshold这两个参数与RANSAC有关

这里就出现了一个问题,我们用SIFT算法已经找到了若干个匹配的点了(几十个甚至上百个),那么应该选择哪四个点来计算Homography matrix呢? RANSAC算法巧妙的解决了这一问题。

随机抽样一致算法(Random sample consensus:RANSAC)

说道估算模型的算法,很容易想到最小二乘法。最小二乘法在数据误差比较小的情况下是可以的,但是针对噪声很大的数据集的时候,容易出问题,我们可以通过下面的例子来说明

包含许多离群的一组数据,要找一条最适合的线

很明显,我们的数据应该是一条由左下到右上的一条线,但是由于离群数据太多(左上和右下区域),如果用最小二乘法的话,就无法准确的找到我们期望的模型(因为最小二乘法无法剔除误差很大的点,一视同仁的将所有的点都用于模型的计算)。RANSAC(Random Sample Consensus)算法的不同之处就在于,它能够有效的去除误差很大的点,并且这些点不计入模型的计算之中。该算法的框架如下:

  • 在数据中随机的选择几个点设定为内群(也就是用来计算模型的点)
  • 用这些选择的数据计算出一个模型
  • 把其它刚才没选到的点带入刚才建立的模型中,计算是否为内群(也就是看这些点是否符合模型,如果符合模型的话,将新的点也计入内群)
  • 如果此时的内群数量足够多的话,可以认为这个模型还算OK,那么就用现在的内群数据重新计算一个稍微好些的模型。
  • 重复以上步骤,最后,我们保留那个内群数量最多的模型。

可以看到,RANSAC利用其独有的方式,在每一次的计算中都将一些数据点排除在外,起到了消除它们噪声的作用,并在此基础上计算模型。

所以,在得到了众多的匹配点以后,使用RANSAC算法,每次从中筛选四个随机的点,然后求得H矩阵,不断的迭代,直到求得最优的H矩阵为止。所以在前面使用cv2.findHomography方法时,参数cv2.RANSAC表明使用RANSAC算法来筛选关键点

import cv2
H, status = cv2.findHomography(ptsA,ptsB,cv2.RANSAC,reprojThresh)
#其中H为求得的单应性矩阵矩阵
#status则返回一个列表来表征匹配成功的特征点。
#ptsA,ptsB为关键点
#cv2.RANSAC
#ransacReprojThreshold 则表示一对内群点所能容忍的最大投影误差
#Maximum allowed reprojection error to treat a point pair as an inlier (used in the RANSAC method only)

其中最大投影误差的计算公式如下:


误差计算

应用

在OpenCV提供了上述基础方法以后,我们可以利用这些方法来对图像进行匹配。

def siftImageAlignment(img1,img2):
   _,kp1,des1 = sift_kp(img1)
   _,kp2,des2 = sift_kp(img2)
   goodMatch = get_good_match(des1,des2)
   if len(goodMatch) > 4:
       ptsA= np.float32([kp1[m.queryIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
       ptsB = np.float32([kp2[m.trainIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
       ransacReprojThreshold = 4
       H, status =cv2.findHomography(ptsA,ptsB,cv2.RANSAC,ransacReprojThreshold);
       imgOut = cv2.warpPerspective(img2, H, (img1.shape[1],img1.shape[0]),flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP)
   return imgOut,H,status

上面的函数首先找出img1和img2中的特征点,然后找到较好的匹配点对,最后通过warpPerspective方法对图像img2进行投影映射。其计算公式如下:


perspective transformation

其中:

  • 第一个参数为需要投影的图像(img2
  • 第二个参数为单应性矩阵(H
  • 第三个参数为所得图像的矩阵大小((img1.shape[1],img1.shape[0]) )
  • 最后的参数cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP,为插值时使用的插值方法INTER_LINEAR,cv2.WARP_INVERSE_MAP则将M设置为dst--->src的方向变换。

我们简单的测试一下我们的函数:

import numpy as np
import cv2
import Utility

img1 = cv2.imread('stack_alignment/IMG_4209.jpg')
img2 = cv2.imread('stack_alignment/IMG_4211.jpg')

result,_,_ = Utility.siftImageAlignment(img1,img2)
allImg = np.concatenate((img1,img2,result),axis=1)
cv2.namedWindow('Result',cv2.WINDOW_NORMAL)
cv2.imshow('Result',allImg)
cv2.waitKey(0)
测试

其中左一和左二为原始图像,但是两张图在拍摄的时候采用了不同的曝光,并且稍微有一些位移,使用我们提供的方法,可以将图二移动到和图一完全匹配的位置,也就是图三。

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