使用OpenCV-SIFT方法进行图像校准的方法

 

最近在研究一些关于岩石薄片的图像处理工作。其中一个问题是拿到了不同偏光照射条件下(不必深究原理了)的相同目标的照片,发现不同的照片是无法精确叠合在一起的,需要进行配准才行。如图所示:

    
由于图片的位置并不是很一致,导致直接叠加(或计算)的时候是有很大误差的。如下面所示:        直接叠加后明显错位了              

OpenCV中是有这样的配准方法的。下面介绍的就是如何使用这样的方法。
我用python调用了一下。最近都在用python,Qt和C++用的少了。

#-*- coding:utf-8 -*-
import numpy as np
import cv2 as cv
#from matplotlib import pyplot as plt
import time

'''
抽取关键点
kp  是一个list<关键点>。 关键点:特殊结构体,属性当中有坐标,方向、角度等等
des 是关键点的特征向量,为128维的向量
'''
def function_sift(image):
    sift = cv.xfeatures2d_SIFT.create()
    kp,des = sift.detectAndCompute(image,None)
    # 可以对关键点进行一些自定义的筛选。
    return kp,des

'''
匹配比较合适的关键点
使用K近邻(KNN)算法。
K近邻算法求取在空间中距离最近的K个数据点,并将这些数据点归为一类。
在进行特征点匹配时,一般使用KNN算法找到最近邻的两个数据点,
    如果最接近和次接近的比值大于一个既定的值,那么我们保留这个最接近的值,
    认为它和其匹配的点为good match(有Lowe在SIFT论文中提出)。
'''
def function_good_match(des1,des2, delta = 0.5):
    bfm = cv.BFMatcher()
    matches = bfm.knnMatch(des1, des2, k=2)
    good_match = []
    for m1, m2 in matches:
        if m1.distance < delta * m2.distance:
            good_match.append(m1)
    return good_match

'''
根据img1将img2进行校准后重新输出,使得img2与img1能够得到较好的像素匹配
完全根据原始图像的大小来的,所以适合不太大的图像。
@imgOut 根据img1将img2进行配准后的图片
@H      单应矩阵
@status 使用状态。(基本无用)
'''
def function_Alignment(img1,img2):
    kp1,des1 = function_sift(img1)
    kp2,des2 = function_sift(img2)
    goodMatch = function_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 =cv.findHomography(ptsA,ptsB,cv.RANSAC,ransacReprojThreshold);
        imgOut = cv.warpPerspective(img2, H, (img1.shape[1],img1.shape[0]),flags=cv.INTER_LINEAR + cv.WARP_INVERSE_MAP)
    return imgOut,H,status

'''
根据img1将img2进行校准后重新输出,使得img2与img1能够得到较好的像素匹配
适合比较大的图像,先将两幅图缩小到tSize大小,然后计算配准矩阵
'''
def function_Alignment2(img10, img20, tSize=(512,512)):
    img1 = cv.resize(img10,tSize)
    img2 = cv.resize(img20,tSize)
    h, w = img10.shape[0], img10.shape[1]
    delta_h = h/tSize[0]
    delta_w = w/tSize[1]
    kp1,des1 = function_sift(img1)
    kp2,des2 = function_sift(img2)
    goodMatch = function_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 =cv.findHomography(ptsA,ptsB,cv.RANSAC,ransacReprojThreshold)
        H[0,2] *= delta_w
        H[1,2] *= delta_h
        imgOut = cv.warpPerspective(img20, H, (img10.shape[1],img10.shape[0]),flags=cv.INTER_LINEAR + cv.WARP_INVERSE_MAP)
    return imgOut,H,status

这样叠加起来以后效果就好了。校正后的叠加效果。仔细看的话能看到合并以后上沿上有个明显的边。
      
我在python上封装了一下,直接输出的是调整后的图像,以及变换用的单应矩阵。
由于图片大小对关键点检测影响比较大,如果是比较大的图片(原图是2560x1920)在我的笔记本(i7-6500U)上运行是需要13秒,降低分辨率到512x512的话只需要0.6秒左右。所以我在上面的代码里封装了两次。

值得注意的一个坑是关于在OpenCV中的这个方法,这是因为xfeatures2d中的算法已经申请专利,开源OpenCV没有版权,所以CV4x是没法使用的,库里去掉了这个算法。sift = cv.xfeatures2d.SIFT_create()函数无法使用会报错的。
opencv-python-3.4.2.16和opencv-contrib-python-3.4.2.16是可以的。我也是将4.2卸载后重新安装的。

如果有机会,再想想自己根据论文是否能实现SIFT的算法。

 

 

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值