柚子快报激活码778899分享:算法 图像拼接——python

http://www.51969.com/

SIFT特征点检测以及图像拼接

结合SIFT、单应性变换、RANSAC和SVD分解等算法实现指定几幅图像之间的拼接

1. SIFT特征点检测,得到特征点以及特征向量

如果要实现图像之间的特征点匹配,要通过特征描述子集之间比对完成。常见的匹配器有暴力匹配器和快速近似最邻近算法匹配器。暴力匹配器就是将两幅图像中的特征描述子全都匹配一遍,得到最优的结果,它的优点是精度高,但是缺点也是显而易见的,在大量的匹配时,匹配时间会很长。快速近似最邻近算法匹配器,故名思意,它只搜索邻近的点,找到邻近的最优匹配,它的匹配准确度会比暴力匹配器低,但是它的匹配时间大大的缩减了。

cv2.xfeatures2d.SIFT_create(nfeatures=None, nOctaveLayers=None, contrastThreshold=None, edgeThreshold=None, sigma=None)-->descriptor

函数用于创建一个用于提取SIFT特征的描述符

* nfeatures,保留的最佳特性的数量。特征按其得分进行排序(以SIFT算法作为局部对比度进行测量);

* nOctavelLayers,高斯金字塔最小层级数,由图像自动计算出;

* constrastThreshold,对比度阈值用于过滤区域中的弱特征。阈值越大,检测器产生的特征越少;

* edgeThreshold ,用于过滤掉类似边缘特征的阈值。 请注意,其含义与contrastThreshold不同,即edgeThreshold越大,滤出的特征越少

* sigma,高斯输入层级, 如果图像分辨率较低,则可能需要减少数值

def detectAndDescribe(self, image):

# 转换为灰度图

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 建立SIFT生成器

descriptor = cv2.xfeatures2d.SIFT_create()

# 检测特征点并计算描述子

kps, features = descriptor.detectAndCompute(gray, None)

kps = np.float32([kp.pt for kp in kps])

return kps, features

通过SIFT_create()实例化SIFT对象,调用detectAndCompute方法得到特征点和特征向量

2. 使用KNN得到和图像A中一个点最接近的前K的点在B中的位置

这里使用RANSAC方法,找到和一个点在另一张图片上最相似的两个点,如果第一个点的近似程度远远大于第二个点,那么才认为第一个点是可靠的匹配点。

rawMatches = matcher.knnMatch(featuresA, featuresB, 2)

参数:

* featureA和featureB 表示两个特征向量集;

* K 表示按knn匹配规则输出的最优的K个结果

在该算法中,输出的结果是:featureA(检测图像)中每个点与被匹配对象featureB(样本图像)中特征向量进行运算的匹配结果。

则,rawMatches中共有featureA条记录,每一条有最优K个匹配结果。

每个结果中包含三个非常重要的数据分别是queryIdx,trainIdx,distance

- queryIdx:特征向量的特征点描述符的下标(第几个特征点描述符),同时也是描述符对应特征点的下标

- trainIdx:样本特征向量的特征点描述符下标,同时也是描述符对应特征点的下标

- distance:代表匹配的特征点描述符的欧式距离,数值越小也就说明俩个特征点越相近

def matchKeypoints(self, kpsA, kpsB, featureA, featureB, ratio, reprojThresh):

# 建立暴力匹配器

matcher = cv2.BFMatcher()

# 使用KNN检测来自AB图的SIFT特征匹配

rawMatches = matcher.knnMatch(featureA, featureB, 2)

# 过滤

matches = []

for m in rawMatches:

if len(m) == 2 and m[0].distance < m[1].distance * ratio:

matches.append((m[0].trainIdx, m[0].queryIdx))

if len(matches) > 4:

# 获取匹配对的点坐标

ptsA = np.float32([kpsA[i] for (_, i) in matches])

ptsB = np.float32([kpsB[i] for (i, _) in matches])

3. 通过点对计算H矩阵

cv2.findHomography(srcPoints, dstPoints, method=None, ransacReprojThreshold=None, mask=None, maxIters=None, confidence=None) --> (H, status)

计算多个二维点对之间的最优单映射变换矩阵H(3*3),status为可选的输出掩码,0/1 表示在变换映射中无效/有效

* method(0, RANSAC, LMEDS, RHO)

* ransacReprojThreshold 最大允许冲投影错误阈值(限方法RANSAC和RHO)

* mask可选输出掩码矩阵,通常由鲁棒算法(RANSAC或LMEDS)设置,是不需要设置的

* maxIters为RANSAC算法的最大迭代次数,默认值为2000

* confidence可信度值,取值范围为0到1.

# 计算H矩阵

H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh)

4. 利用单应矩阵对图片进行变换拼接

cv.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None) --> result

将图像按照变换映射M执行后返回变换后的图像result。

# 将图片A进行视角变换 中间结果

result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))

# 将图片B传入]

result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB

self.cv_show('result', result)

tip:两张图片分左右

源图片:

目标图像:

源代码:

main文件:

from Stitcher import Stitcher

import cv2

# 读取图片

img1 = cv2.imread('img1.jpg')

img2 = cv2.imread('img2.jpg')

# 图片拼接

stitcher = Stitcher()

result, vis = stitcher.stitch([img1, img2], showMatches=True)

cv2.imshow('img1', img1)

cv2.imshow('img2', img2)

cv2.imshow('keypoints matches', vis)

cv2.imshow('result', result)

cv2.waitKey(0)

cv2.destroyWindow()

Stitcher文件:

import numpy as np

import cv2

class Stitcher:

# 拼接函数

def stitch(self, images, ratio = 0.75, reprojThresh = 4.0, showMatches = False):

# 读取图像

imageB, imageA = images

# 计算特征点和特征向量

kpsA, featureA = self.detectAndDescribe(imageA)

kpsB, featureB = self.detectAndDescribe(imageB)

# 匹配两张图片的特征点

M = self.matchKeypoints(kpsA, kpsB, featureA, featureB, ratio, reprojThresh)

# 没有匹配点,退出

if not M:

return None

matches, H, status = M

# 将图片A进行视角变换 中间结果

result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))

# 将图片B传入]

result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB

self.cv_show('result', result)

# 检测是否需要显示图片匹配

if showMatches:

# 生成匹配图片

vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches, status)

# 返回结果

return result, vis

# 返回匹配结果

return result

def detectAndDescribe(self, image):

# 转换为灰度图

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 建立SIFT生成器

descriptor = cv2.xfeatures2d.SIFT_create()

# 检测特征点并计算描述子

kps, features = descriptor.detectAndCompute(gray, None)

kps = np.float32([kp.pt for kp in kps])

return kps, features

def matchKeypoints(self, kpsA, kpsB, featureA, featureB, ratio, reprojThresh):

# 建立暴力匹配器

matcher = cv2.BFMatcher()

# 使用KNN检测来自AB图的SIFT特征匹配

rawMatches = matcher.knnMatch(featureA, featureB, 2)

# 过滤

matches = []

for m in rawMatches:

if len(m) == 2 and m[0].distance < m[1].distance * ratio:

matches.append((m[0].trainIdx, m[0].queryIdx))

if len(matches) > 4:

# 获取匹配对的点坐标

ptsA = np.float32([kpsA[i] for (_, i) in matches])

ptsB = np.float32([kpsB[i] for (i, _) in matches])

# 计算H矩阵

H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh)

return matches, H, status

# 展示图像

def cv_show(self,name, img):

cv2.imshow(name, img)

cv2.waitKey(0)

cv2.destroyAllWindows()

def drawMatches(self, imageA, imageB, kpsA, kpsB, matches, status):

# 初始化可视化图片,将A、B图左右连接到一起

hA, wA = imageA.shape[:2]

hB, wB = imageB.shape[:2]

vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")

vis[0:hA, 0:wA] = imageA

vis[0:hB, wA:] = imageB

# 联合遍历,画出匹配对

for ((trainIdx, queryIdx), s) in zip(matches, status):

# 当点对匹配成功时,画到可视化图上

if s == 1:

# 画出匹配对

ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))

ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1]))

cv2.line(vis, ptA, ptB, (0, 255, 0), 1)

# 返回可视化结果

return vis

柚子快报激活码778899分享:算法 图像拼接——python

http://www.51969.com/

查看原文