😊😊😊欢迎来到本博客😊😊😊
本次博客内容将继续讲解关于OpenCV的相关知识
🎉作者简介:⭐️⭐️⭐️目前计算机研究生在读。主要研究方向是人工智能和群智能算法方向。目前熟悉深度学习(keras、pytorch、yolo),python网页爬虫、机器学习、计算机视觉(OpenCV)、群智能算法。然后正在学习深度学习的相关内容。以后可能会涉及到网络安全相关领域,毕竟这是每一个学习计算机的梦想嘛!
📝目前更新:🌟🌟🌟目前已经更新了关于网络爬虫的相关知识、机器学习的相关知识、目前正在更新计算机视觉-OpenCV的相关内容。
💛💛💛本文摘要💛💛💛
本文我们将继续讲解计算机视觉领域项目-基于特征点匹配的图像拼接。
之前我们介绍过基于OpenCv的特征匹配操作,我们通过特征匹配可以精确的找到目标。本节我们继续探索基于特征匹配还可以做哪些事情。我们都在拍一个集体的过程中使用过苹果手机的全图效果进行拍照留念。那么苹果手机这个效果它是基于什么技术来做的呢?没错其实就是特征匹配。他是实时拍取多个照片,然后使用特征匹配操作继续两个图像之间特征点的匹配,然后生成转换矩阵,最后转换成效果图,我们本次博客就是要介绍一下这个操作如何使用OpenCv进行实现。

原理图,这里第一个图表示的就是平面灰度值没有明显变化,第二个图就是要给边界灰度值水平方向变化明显垂直方向灰度值变化并不明显,第三个图表示的就是一个角点,无论水平还是垂直方向都很明显。主要看灰度级的变化结果:

边界:一个特征值大,一个特征值小,自相关函数在某一个方向上大,在其他方向上小。
平面:两个特征都小,且近似相等。
角点:两个特征都大,且近似相等,自相关函数在所有方向都大。

在OpenCV当中我们使用,cv2.cornerHarris()来进行角点检测。
其中参数都有:

import cv2
import numpy as np
img = cv2.imread('white-black.webp')
print ('img.shape:',img.shape)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
print ('dst.shape:',dst.shape)
img[dst>0.01*dst.max()]=[0,0,255]
cv2.imshow('dst',img)
cv2.waitKey(0)
cv2.destroyAllWindows()


这里有一些点检测的不是特别好,然后我们用黑白棋盘来看一下。

这个效果堪称完美!!!
SIFT是指尺度空间:是指在一定的范围内,无论物体是大是小,人眼都可以进行一个识别,然后计算机要去识别却很难,所以要让计算机能够对物体进行一个在不同尺度下都存在一个统一的认知,就要考虑图像在不同的尺度在都存在的特点,尺度空间的获取一般使用高斯模糊来实现-高斯滤波。
我们再说说SIFT有什么优点:
1、具有较好的稳定性和不变性,能够适应旋转、尺度缩放、亮度的变化,能在一定程度上不受视角变化、仿射变换、噪声的干扰。
2、区分性好,能够在海量特征数据库中进行快速准确的区分信息进行匹配
3、多量性,就算只有单个物体,也能产生大量特征向量
4、高速性,能够快速的进行特征向量匹配
5、可扩展性,能够与其它形式的特征向量进行联合
我们都知道如果图片经过高斯滤波操作之后呢,他会变模糊,那么为什么要这么做呢?因为当我们从很近的看一个人的时候,他是清晰的,那么从很远看的时候他就是模糊的。所以我们为了模拟这个过程,就用高斯滤波来进行相同了一个模拟。
不同σ的高斯函数决定了对图像的平滑程度,越大的σ值对应的图像越模糊。
因此我们要介绍一个金字塔,高斯差分金字塔。
我们需要做一个多分辨率的金字塔,对于金字塔的每一层都要做高斯滤波。

根据意思就是5个输入的高斯图像,相邻的进行像素值相减,得到4张差分后的结果。那么我们想要找什么呢?找SIFT,就是特征点,那么什么样的点被认为是特征点呢?通常关键点数值较大、差分结果较大的、极值里面较大的。是不是有点像之前讲的图像金字塔。

前面我们简单的将特征匹配介绍了一下,然后我们开始讲一下我们本次博客的内容。
主函数:
from Stitcher import Stitcher
import cv2
# 读取拼接图片
imageA = cv2.imread("left_02.jpg")
imageB = cv2.imread("right_02.jpg")
# 把图片拼接成全景图
stitcher = Stitcher()
(result, vis) = stitcher.stitch([imageA, imageB], showMatches=True)
# 显示所有图片
cv2.imshow("Image A", imageA)
cv2.imshow("Image B", imageB)
cv2.imshow("Keypoint Matches", vis)
cv2.imshow("Result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
在主函数这里我们把两张图像导入进来,注意这里的图像宽度必须一致,因为后续要进行拼接。长度可以保持不一致。然后我们进入拼接全景图的操作当中。
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, featuresA) = self.detectAndDescribe(imageA)
(kpsB, featuresB) = self.detectAndDescribe(imageB)
M = self.matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh)
# 如果返回结果为空,没有匹配成功的特征点,退出算法
if M is None:
return None
# 否则,提取匹配结果
# H是3x3视角变换矩阵
(matches, H, status) = M
# 将图片A进行视角变换,result是变换后图片
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
#self.cv_show('result', result)
# 将图片B传入result图片最左端
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
首先我们将程序写成一个类,然后在类中使用self.detectAndDescribe()函数检测A、B图片的SIFT关键特征点,并计算特征描述子。
def detectAndDescribe(self, image): # 将彩色图片转换成灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) descriptor = cv2.SIFT_create() (kps, features) = descriptor.detectAndCompute(image, None) kps = np.float32([kp.pt for kp in kps]) return (kps, features)'运行
首先我们将图像转换成了灰度图,然后我们建立了使用SIFT检测特征点的检测器。然后对两张图片进行特征点检测,然后将结果转化成numpy数组并且返回。
然后在使用self.matchKeypoints()对特征点进行匹配。
def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh): # 建立暴力匹配器 matcher = cv2.BFMatcher() # 使用KNN检测来自A、B图的SIFT特征匹配对,K=2 rawMatches = matcher.knnMatch(featuresA, featuresB, 2) matches = [] for m in rawMatches: # 当最近距离跟次近距离的比值小于ratio值时,保留此匹配对 if len(m) == 2 and m[0].distance < m[1].distance * ratio: # 存储两个点在featuresA, featuresB中的索引值 matches.append((m[0].trainIdx, m[0].queryIdx)) # 当筛选后的匹配对大于4时,计算视角变换矩阵 if len(matches) > 4: # 获取匹配对的点坐标 ptsA = np.float32([kpsA[i] for (_, i) in matches]) ptsB = np.float32([kpsB[i] for (i, _) in matches]) # 计算视角变换矩阵 (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh) # 返回结果 return (matches, H, status) # 如果匹配对小于4时,返回None return None'运行
通过上述代码我们可以计算出来两张图象的视角变换矩阵,然后返回结果。最后进行可视化操作。
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'运行
我们来看一下效果图:
左一图:

右一图:

最终效果图:

在用一个经典案例:



🔎支持:🎁🎁🎁如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦!这就是给予我最大的支持!