目录
一、图像拼接
1、直接看案例
图1与图2展示:
合并完结果:
2、什么是图像拼接
3、图像拼接步骤
1)加载图像
2)特征点检测与描述
3)特征点匹配
4)图像配准
5)图像变换和拼接
6)图像调整
二、案例实现
1、定义函数返回图像的关键点和描述符
2、定义展示图像函数
3、计算读入图像的特征点和描述符
调试模式状态下:
kps对应值:
des对应值:
4、建立暴力匹配器和K近邻算法
1)关于BFMatcher暴力匹配
2)暴力匹配的K近邻
用法:
参数解析:
返回值:
3)续接上文代码
运行结果:
调试模式rawMatches内容:
5、绘制匹配结果
运行结果:
6、计算视角变换矩阵
调试模式下
kps_floatA与kps_floatB状态
matches状态
7、透视变换后拼接
运行结果:
8、完整代码:
一、图像拼接
1、直接看案例
图1与图2展示:
合并完结果:
2、什么是图像拼接
图像拼接是指将多个图像拼接成一个大图像。在计算机视觉和图像处理领域,图像拼接常用于创建全景图像、创建大幅面照片、图像拼接等应用。
3、图像拼接步骤
1)加载图像
使用OpenCV的cv::imread函数加载需要拼接的多个图像。
2)特征点检测与描述
使用特征提取算法(如SIFT、ORB等)检测图像中的特征点,并计算每个特征点的描述符。
3)特征点匹配
使用特征匹配算法(如KNN匹配)来找到两个图像间的对应关系。常见的方法有基于距离的匹配(如欧氏距离、汉明距离等)和基于相似性度量的匹配(如比率测试)。
4)图像配准
根据特征点的匹配结果,使用配准算法(如RANSAC)估计两个图像间的变换矩阵。常见的变换矩阵包括仿射变换、透视变换等。
5)图像变换和拼接
使用估计得到的变换矩阵,将需要拼接的图像进行变换,并将它们拼接在一起。可以使用OpenCV的cv::warpPerspective函数或cv::warpAffine函数来实现变换和拼接。
6)图像调整
对拼接后的图像进行调整,使得拼接边缘平滑过渡,消除拼接处的不连续性。常见的方法包括图像融合、图像平滑等。
二、案例实现
1、定义函数返回图像的关键点和描述符
import cv2
import numpy as np
import sys
def detectAndDescribe(image): # 函数用于
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 将影色园片转换成死没图
descriptor = cv2.SIFT_create() # 建立SIFT生成器
(kps,des) = descriptor.detectAndCompute(gray,None) # 读入参数为灰度图和可选参数掩膜,检测关键点及描述符,返回关键点列表和关键点对应的描述符列表,每个描述符都是一个向量,描述关键点周围图像内容
# 此处kps是元组类型,des是ndarry矩阵类型
# 将关键点列表的结果转换战NumPy数组
kps_float = np.float32([kp.pt for kp in kps])
# kp.pt 包含两个值,分别是关键点在图像中的 x 和 y 坐标。这些坐标通常是浮点数,可以精确的捕述关键点在图像中的位置
return (kps,kps_float,des) # 返回特征点集,及对应的描述特征
2、定义展示图像函数
def cv_show(name,img): # 函数用于展示图片
cv2.imshow(name,img)
cv2.waitKey(0)
3、计算读入图像的特征点和描述符
"""读取拼接图片"""
imageA = cv2.imread("1.jpg")
cv_show('imageA',imageA)
imageB = cv2.imread("2.jpg")
cv_show("imageB",imageB)
"""计算图片特征点及描述符"""
(kpsA,kps_floatA,desA) = detectAndDescribe(imageA)
(kpsB,kps_floatB,desB) = detectAndDescribe(imageB)
调试模式状态下:
kps对应值:
des对应值:
4、建立暴力匹配器和K近邻算法
1)关于BFMatcher暴力匹配
在图像处理中,特征点匹配是指在不同图像中找到对应的特征点。BFMatcher可用于在两个特征向量集合中计算最佳匹配。它通过计算两个特征向量的相似度(如欧氏距离、汉明距离等),并选择最近邻的特征点作为匹配点。
2)暴力匹配的K近邻
用法:
使用KNN检测来自A、B图的SIFT特征匹配对
# knnMatch(gueryDescriptors, trainDescriptors, k, mask=None, compactResult=None)
参数解析:
queryDescriptors:匹配图像A的描述符
trainDescriptors:匹配图像B的描述符
K:最佳匹配的描述符个数,一般K=2
mask 可选参数:一个掩码数组,用于过滤不需要匹配的特征点。默认为None
,表示不使用掩码。
compactResult 可选参数:一个布尔值,指定是否返回紧凑的匹配结果。默认为None
,表示根据特征描述符的类型自动选择。
返回值:
distance:匹配的特征点描述符的欧式距离,数值越小也就说明俩个特征点越相近
queryIdx:测试图像的特征点描述符的下标(第几个特征点描述符),同时也是描述符对应特征点的下标。
trainIdx:样本图像的特征点描述符下标,同时也是描述符对应特征点的下标。
3)续接上文代码
matcher = cv2.BFMatcher()
rawMatches = matcher.knnMatch(desB,desA,2) # 对desB中的每个描述符在desA中查找两个最近邻
good = [] # 设置空列表用于存放匹配成功的特征点
matches =[] # 用于存放匹配成功的两个点的索引值
for m in rawMatches:
# 当最近距离跟次近距离的比值小于0.65值时,保留此匹配对
if len(m) == 2 and m[0].distance < 0.65 * m[1].distance: # len(m) == 2 表示检查是否有两个匹配项
# m[0].distance < 0.65 * m[1].distance表示判断匹配的两个点最近邻和次近邻的比值是否小于0.65
good.append(m)
# 存储两个点在featuresA,featuresB中的索引值
matches.append((m[0].trainIdx, m[0].queryIdx))
print(len(good)) # 返回匹配成功的特征点个数
print(matches) # 打印匹配成功点的索引
运行结果:
调试模式rawMatches内容:
5、绘制匹配结果
# 绘制两组关键点的匹配结果,输入参数为B图原图,B图的关键点列表,A图原图,A图的关键点列表,匹配成功的点的坐标,掩码图像默认为None
# flag 表示绘制的标志,cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS表示在关键点周围绘制圆圈,圆圈大小与关键点尺度成比例
vis = cv2.drawMatchesKnn(imageB,kpsB,imageA,kpsA,good,None,flags = cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv_show('keypoint matches',vis)
运行结果:
6、计算视角变换矩阵
"""透视变换"""
if len(matches) > 4 : # 当前筛选后的匹配对大于4.计算视角变换矩阵
# 分别获取匹配成功的A图中点的坐标与B图中点的坐标
ptsA = np.float32([kps_floatA[i] for (i,_) in matches]) # kps_floatA是匹配成功点的坐标,matches是通过阈值筛选之后的特征点对象,其中存放匹配成功点的索引,
ptsB = np.float32([kps_floatB[i] for (_,i) in matches]) # kps_floatB是图片B中的全就特征点坐标
(H, mask) = cv2.findHomography(ptsB, ptsA, cv2.RANSAC, 10)
else:
print('图片未找到4个以上的匹配点')
sys.exit()
调试模式下
kps_floatA与kps_floatB状态
matches状态
7、透视变换后拼接
# 根据视角变换矩阵H将原图B进行透视变换,然后将变换后的图片与A进行拼接
result = cv2.warpPerspective(imageB,H,(imageB.shape[1] + imageA.shape[1],imageB.shape[0]))
cv_show('resultB',result)
# 将图片A传入result图片最左端
result[0:imageA.shape[0],0:imageA.shape[1]] = imageA
cv_show('result',result)
运行结果:
8、完整代码:
import cv2
import numpy as np
import sys
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
def detectAndDescribe(image):
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
descriptor = cv2.SIFT_create()
(kps,des) = descriptor.detectAndCompute(gray,None)
return (kps,kps_float,des)
imageA = cv2.imread("1.jpg")
cv_show('imageA',imageA)
imageB = cv2.imread("2.jpg")
cv_show("imageB",imageB)
(kpsA,kps_floatA,desA) = detectAndDescribe(imageA)
(kpsB,kps_floatB,desB) = detectAndDescribe(imageB)
rawMatches = matcher.knnMatch(desB,desA,2)
good = []
matches =[]
for m in rawMatches:
if len(m) == 2 and m[0].distance < 0.65 * m[1].distance:
good.append(m)
matches.append((m[0].trainIdx, m[0].queryIdx))
print(len(good)) # 返回匹配成功的特征点个数
print(matches) # 打印匹配成功点的索引
vis = cv2.drawMatchesKnn(imageB,kpsB,imageA,kpsA,good,None,flags = cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv_show('keypoint matches',vis)
if len(matches) > 4 :
ptsA = np.float32([kps_floatA[i] for (i,_) in matches])
ptsB = np.float32([kps_floatB[i] for (_,i) in matches])
(H, mask) = cv2.findHomography(ptsB, ptsA, cv2.RANSAC, 10)
else:
print('图片未找到4个以上的匹配点')
sys.exit() # 退出匹配
result = cv2.warpPerspective(imageB,H,(imageB.shape[1] + imageA.shape[1],imageB.shape[0]))
cv_show('resultB',result)
result[0:imageA.shape[0],0:imageA.shape[1]] = imageA
cv_show('result',result)