深入剖析 OpenCV:全面掌握基础操作、图像处理算法与特征匹配
深入剖析 OpenCV:全面掌握基础操作、图像处理算法与特征匹配
- 一、引言
- 二、OpenCV 的安装
- (一)使用 pip 安装
- (二)使用 Anaconda 安装
- 三、OpenCV 基础操作
- (一)图像的读取、显示与保存
- (二)图像的基本属性与操作
- (三)颜色空间转换
- 四、OpenCV 的图像处理算法
- (一)图像滤波
- (二)边缘检测
- (三)形态学操作
- 五、OpenCV 的特征提取与匹配
- (一)ORB 特征提取
- (二)SIFT 特征提取
- (三)特征匹配
- 1. ORB 特征匹配
- 2. SIFT 特征匹配
一、引言
在当今数字化的时代,计算机视觉技术正以前所未有的速度改变着我们的生活。从智能手机的人脸识别解锁到自动驾驶汽车的环境感知,从工业生产中的质量检测到医疗领域的影像分析,计算机视觉无处不在。OpenCV(Open Source Computer Vision Library)作为计算机视觉领域最具影响力的开源库之一,为开发者和研究人员提供了强大而便捷的工具。它拥有超过 2500 种优化算法,涵盖了图像和视频处理、特征提取、目标检测、机器学习等多个领域。本文将基于 Python 语言,全面深入地介绍 OpenCV 的各个方面,从基础操作到高级应用,帮助读者逐步掌握这一强大的工具。
二、OpenCV 的安装
在 Python 环境中安装 OpenCV 非常便捷,主要有两种常见的安装方式。
(一)使用 pip 安装
通过 pip
包管理器,我们可以轻松地安装 OpenCV。在命令行中输入以下命令:
pip install opencv-python
如果需要安装包含扩展功能的版本(如 SIFT、SURF 等算法,这些算法在非商业用途下可用),可以使用以下命令:
pip install opencv-contrib-python
(二)使用 Anaconda 安装
如果你使用 Anaconda 作为 Python 环境管理工具,可以使用以下命令进行安装:
conda install -c conda-forge opencv
安装完成后,我们可以在 Python 代码中导入 OpenCV 库进行测试:
import cv2
print(cv2.__version__)
如果能够正常输出 OpenCV 的版本号,说明安装成功。
如4.11.0
以下是去掉对图像读取检查后的代码内容,为你扩充了标题 3 - 5 的代码和解释:
三、OpenCV 基础操作
(一)图像的读取、显示与保存
- 读取图像
使用cv2.imread()
函数读取图像文件,支持常见的图像格式如.jpg
、.png
等。函数第一个参数为图像文件路径,第二个参数指定读取模式。常见的读取模式有:cv2.IMREAD_COLOR
:以彩色模式读取图像,默认模式。cv2.IMREAD_GRAYSCALE
:以灰度模式读取图像。cv2.IMREAD_UNCHANGED
:读取包含透明度通道的图像。
import cv2# 以彩色模式读取图像
color_image = cv2.imread('example.jpg', cv2.IMREAD_COLOR)
# 以灰度模式读取图像
gray_image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 读取包含透明度通道的图像
alpha_image = cv2.imread('example.png', cv2.IMREAD_UNCHANGED)
解释:cv2.imread()
是 OpenCV 中用于读取图像的核心函数。通过不同的读取模式参数,可以灵活地获取图像的不同表示形式。彩色模式读取的图像包含三个通道(BGR),灰度模式将图像转换为单通道,而读取带透明度通道的图像适用于处理 PNG 等支持透明度的图像格式。
- 显示图像
通过cv2.imshow()
函数显示图像,第一个参数是窗口名称,第二个参数是要显示的图像对象。使用cv2.waitKey()
等待用户按键,cv2.destroyAllWindows()
关闭所有窗口。
# 显示彩色图像
cv2.imshow('Color Image', color_image)
# 显示灰度图像
cv2.imshow('Gray Image', gray_image)# 等待用户按键(0 表示无限等待)
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()
解释:cv2.imshow()
用于在一个新窗口中显示图像,窗口名称用于区分不同的显示窗口。cv2.waitKey()
函数会阻塞程序,直到用户按下任意键,参数为 0 时表示无限等待。cv2.destroyAllWindows()
用于关闭所有由 OpenCV 创建的窗口,释放资源。
- 保存图像
利用cv2.imwrite()
函数保存图像,第一个参数为保存路径,第二个参数是要保存的图像。
# 保存灰度图像
cv2.imwrite('saved_gray_image.jpg', gray_image)
解释:cv2.imwrite()
函数将指定的图像保存到指定的文件路径。保存的文件格式由文件扩展名决定,如 .jpg
、.png
等。
(二)图像的基本属性与操作
- 图像的基本属性
图像在 OpenCV 中以 NumPy 数组形式存储,我们可以获取其高度、宽度和通道数等属性。
import cv2image = cv2.imread('example.jpg')
height, width, channels = image.shape
print(f"图像高度: {height}, 宽度: {width}, 通道数: {channels}")
# 额外获取图像的数据类型
print(f"图像的数据类型: {image.dtype}")
解释:图像在 OpenCV 里被存储为 NumPy 数组,通过 shape
属性可以获取图像的基本尺寸信息,dtype
属性则能查看图像的数据类型,通常为 uint8
(8 位无符号整数)。
- 图像的像素访问
可以直接通过索引访问和修改图像的像素值。
import cv2image = cv2.imread('example.jpg')
# 获取坐标(100, 100)处的像素值(BGR 格式)
pixel = image[100, 100]
print(f"坐标(100, 100)处的像素值(BGR 格式): {pixel}")
# 修改坐标(100, 100)处的像素值为白色
image[100, 100] = [255, 255, 255]
new_pixel = image[100, 100]
print(f"修改后坐标(100, 100)处的像素值(BGR 格式): {new_pixel}")
解释:由于图像是 NumPy 数组,所以可以像操作数组一样通过索引来访问和修改像素值。在 OpenCV 中,彩色图像的像素值是按 BGR(蓝、绿、红)顺序存储的。
- 图像的裁剪、缩放与旋转
- 裁剪
通过指定行和列的范围来裁剪图像。
import cv2image = cv2.imread('example.jpg')
# 裁剪图像,获取坐标(100, 100)到(300, 300)的区域
cropped_image = image[100:300, 100:300]
cv2.imshow('Cropped Image', cropped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:裁剪图像是通过指定数组的行和列范围来实现的,即选取图像中指定区域的像素值,形成新的图像。
- 缩放
使用cv2.resize()
函数缩放图像,可以指定缩放比例或目标尺寸。
import cv2image = cv2.imread('example.jpg')
# 按比例缩放,宽度和高度都变为原来的 0.5 倍
resized_image_1 = cv2.resize(image, None, fx=0.5, fy=0.5)
cv2.imshow('Resized Image by Scale', resized_image_1)# 指定目标尺寸进行缩放
resized_image_2 = cv2.resize(image, (300, 300))
cv2.imshow('Resized Image by Size', resized_image_2)cv2.waitKey(0)
cv2.destroyAllWindows()
解释:cv2.resize()
函数提供了灵活的缩放方式。fx
和 fy
参数用于指定水平和垂直方向的缩放比例,当第二个参数为 None
时使用这种方式。也可以直接指定目标尺寸,如 (300, 300)
来进行缩放。
- 旋转
借助cv2.getRotationMatrix2D()
获取旋转矩阵,再用cv2.warpAffine()
进行旋转操作。
import cv2
import numpy as npimage = cv2.imread('example.jpg')
height, width = image.shape[:2]
# 计算旋转中心
center = (width / 2, height / 2)
# 定义旋转角度和缩放因子
angle = 45
scale = 1.0
# 获取旋转矩阵
M = cv2.getRotationMatrix2D(center, angle, scale)
# 对图像进行旋转
rotated_image = cv2.warpAffine(image, M, (width, height))
cv2.imshow('Rotated Image', rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:cv2.getRotationMatrix2D()
用于计算旋转矩阵,该矩阵包含了旋转中心、旋转角度和缩放因子等信息。cv2.warpAffine()
函数根据这个旋转矩阵对图像进行仿射变换,实现图像的旋转操作。
(三)颜色空间转换
常见的颜色空间有 RGB、灰度、HSV、Lab 等,使用 cv2.cvtColor()
函数进行颜色空间转换。
import cv2color_image = cv2.imread('example.jpg')
# 转换为灰度图像
gray_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray Image', gray_image)# 转换为 HSV 图像
hsv_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2HSV)
cv2.imshow('HSV Image', hsv_image)# 转换为 Lab 图像
lab_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2Lab)
cv2.imshow('Lab Image', lab_image)cv2.waitKey(0)
cv2.destroyAllWindows()
解释:不同的颜色空间适用于不同的图像处理任务。cv2.cvtColor()
函数可以在不同颜色空间之间进行转换,例如将 BGR 彩色图像转换为灰度图像、HSV 图像或 Lab 图像,方便后续的处理和分析。
四、OpenCV 的图像处理算法
(一)图像滤波
- 均值滤波
使用cv2.blur()
函数实现,通过计算局部区域像素平均值平滑图像。
import cv2image = cv2.imread('example.jpg')
# 均值滤波,核大小为(5, 5)
blurred_image = cv2.blur(image, (5, 5))
cv2.imshow('Blurred Image (Mean Filter)', blurred_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:均值滤波是一种简单的线性滤波方法,它以一个固定大小的核(如 (5, 5)
)在图像上滑动,计算每个核内像素的平均值,并将该平均值作为中心像素的新值,从而达到平滑图像的效果。
- 高斯滤波
利用cv2.GaussianBlur()
函数,基于高斯函数对图像进行滤波,抑制噪声同时保留边缘。
import cv2image = cv2.imread('example.jpg')
# 高斯滤波,核大小为(5, 5),标准差为 0
gaussian_blurred_image = cv2.GaussianBlur(image, (5, 5), 0)
cv2.imshow('Blurred Image (Gaussian Filter)', gaussian_blurred_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:高斯滤波是一种非线性滤波方法,它根据高斯函数对核内的像素进行加权平均。相比于均值滤波,高斯滤波在抑制噪声的同时能更好地保留图像的边缘信息,因为它对不同位置的像素赋予了不同的权重。
- 中值滤波
通过cv2.medianBlur()
函数,用局部区域的中值替换当前像素值,有效去除椒盐噪声。
import cv2image = cv2.imread('example.jpg')
# 中值滤波,核大小为 5
median_blurred_image = cv2.medianBlur(image, 5)
cv2.imshow('Blurred Image (Median Filter)', median_blurred_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:中值滤波是一种非线性滤波方法,它将核内的像素值进行排序,然后用中值替换中心像素的值。这种方法对于去除椒盐噪声(图像中出现的黑白孤立点)非常有效。
- 双边滤波
cv2.bilateralFilter()
函数可以在平滑图像的同时保留边缘信息,适用于需要保留图像细节的场景。
import cv2image = cv2.imread('example.jpg')
# 双边滤波,直径为 9,颜色标准差为 75,空间标准差为 75
bilateral_blurred_image = cv2.bilateralFilter(image, 9, 75, 75)
cv2.imshow('Blurred Image (Bilateral Filter)', bilateral_blurred_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:双边滤波是一种保边滤波方法,它结合了空间域和值域的信息。在平滑图像时,它不仅考虑像素的空间距离,还考虑像素的颜色差异,因此能够在平滑图像的同时很好地保留边缘和细节信息。
(二)边缘检测
- Canny 边缘检测
cv2.Canny()
函数是常用的边缘检测算法,通过计算图像梯度和非极大值抑制等步骤检测边缘。
import cv2image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# Canny 边缘检测,设置高低阈值
edges = cv2.Canny(image, 100, 200)
cv2.imshow('Canny Edge Detection', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:Canny 边缘检测是一种多阶段的边缘检测算法,主要包括高斯平滑、梯度计算、非极大值抑制和双阈值处理等步骤。高低阈值参数用于控制边缘的检测范围,只有当梯度值大于高阈值时才被确定为边缘,介于高低阈值之间的像素如果与确定的边缘相连也会被保留。
- Sobel 边缘检测
cv2.Sobel()
函数用于计算图像在 x 和 y 方向上的梯度,从而检测边缘。
import cv2
import numpy as npimage = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 计算 x 方向的 Sobel 梯度
sobelx = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
# 计算 y 方向的 Sobel 梯度
sobely = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
# 取绝对值并转换为 8 位无符号整数
sobelx = np.uint8(np.absolute(sobelx))
sobely = np.uint8(np.absolute(sobely))
# 合并 x 和 y 方向的梯度
sobel_combined = cv2.bitwise_or(sobelx, sobely)
cv2.imshow('Sobel Edge Detection', sobel_combined)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:Sobel 算子是一种一阶导数算子,用于计算图像在 x 和 y 方向上的梯度。通过分别计算两个方向的梯度,可以检测出图像中的水平和垂直边缘。最后将两个方向的梯度合并,得到完整的边缘信息。
- Laplacian 边缘检测
cv2.Laplacian()
函数通过计算图像的二阶导数来检测边缘。
import cv2image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# Laplacian 边缘检测
laplacian_edges = cv2.Laplacian(image, cv2.CV_64F)
laplacian_edges = np.uint8(np.absolute(laplacian_edges))
cv2.imshow('Laplacian Edge Detection', laplacian_edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:Laplacian 算子是一种二阶导数算子,它对图像的灰度变化非常敏感,能够检测出图像中的边缘。由于二阶导数可能为负值,所以需要取绝对值并转换为 8 位无符号整数才能正确显示边缘图像。
(三)形态学操作
- 膨胀与腐蚀
膨胀操作可以扩大图像中的前景区域,腐蚀操作则可以缩小前景区域。
import cv2
import numpy as npimage = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 定义结构元素
kernel = np.ones((5, 5), np.uint8)
# 膨胀操作
dilated_image = cv2.dilate(image, kernel, iterations=1)
# 腐蚀操作
eroded_image = cv2.erode(image, kernel, iterations=1)cv2.imshow('Dilated Image', dilated_image)
cv2.imshow('Eroded Image', eroded_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:膨胀和腐蚀是基本的形态学操作,它们基于结构元素(如 (5, 5)
的全 1 矩阵)对图像进行处理。膨胀操作将结构元素在图像上滑动,如果结构元素与前景区域有重叠,则将中心像素置为前景;腐蚀操作则相反,如果结构元素完全包含在前景区域内,才将中心像素置为前景。
- 开运算与闭运算
开运算先进行腐蚀操作,再进行膨胀操作,用于去除小的噪声点;闭运算先进行膨胀操作,再进行腐蚀操作,用于填充小的空洞。
import cv2
import numpy as npimage = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
kernel = np.ones((5, 5), np.uint8)
# 开运算
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
# 闭运算
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)cv2.imshow('Opening Image', opening)
cv2.imshow('Closing Image', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:开运算和闭运算是基于膨胀和腐蚀操作的组合。开运算可以去除图像中的小噪声点,同时保持大的前景区域不变;闭运算则可以填充前景区域内的小空洞,使前景区域更加完整。
- 形态学梯度
形态学梯度是膨胀图像与腐蚀图像的差值,用于突出图像的边缘。形态学梯度能够帮助我们检测图像中物体的边界,在图像分割、目标检测等领域有着广泛的应用。
import cv2
import numpy as npimage = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 定义结构元素,这里使用 5x5 的全 1 矩阵
kernel = np.ones((5, 5), np.uint8)
# 进行膨胀操作
dilated_image = cv2.dilate(image, kernel, iterations=1)
# 进行腐蚀操作
eroded_image = cv2.erode(image, kernel, iterations=1)
# 计算形态学梯度,即膨胀图像减去腐蚀图像
gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)
# 也可以手动计算形态学梯度
manual_gradient = cv2.subtract(dilated_image, eroded_image)# 显示原始图像
cv2.imshow('Original Image', image)
# 显示通过 cv2.morphologyEx 计算得到的形态学梯度图像
cv2.imshow('Morphological Gradient (cv2.morphologyEx)', gradient)
# 显示手动计算得到的形态学梯度图像
cv2.imshow('Morphological Gradient (Manual)', manual_gradient)# 等待用户按键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()
解释:
- 结构元素定义:我们使用
np.ones((5, 5), np.uint8)
定义了一个 5x5 的全 1 矩阵作为结构元素。结构元素的大小和形状会影响形态学操作的效果,不同的任务可能需要不同的结构元素。 - 膨胀和腐蚀操作:通过
cv2.dilate()
和cv2.erode()
函数分别对图像进行膨胀和腐蚀操作。膨胀操作会使图像中的前景区域扩大,而腐蚀操作会使前景区域缩小。 - 形态学梯度计算:
- 使用
cv2.morphologyEx()
:这是 OpenCV 提供的用于进行复杂形态学操作的函数,cv2.MORPH_GRADIENT
表示计算形态学梯度。 - 手动计算:通过
cv2.subtract()
函数将膨胀后的图像减去腐蚀后的图像,也能得到形态学梯度。这两种方法的结果是相同的,但使用cv2.morphologyEx()
更简洁。
- 使用
- 图像显示:使用
cv2.imshow()
函数分别显示原始图像、通过cv2.morphologyEx
计算得到的形态学梯度图像以及手动计算得到的形态学梯度图像,方便我们对比和观察形态学梯度的效果。
通过形态学梯度,我们可以清晰地看到图像中物体的边缘信息,这对于后续的图像处理和分析非常有帮助。例如,在目标检测中,我们可以利用形态学梯度来初步定位物体的边界,然后再进行更精确的检测和识别。
五、OpenCV 的特征提取与匹配
(一)ORB 特征提取
ORB(Oriented FAST and Rotated BRIEF)是一种快速的特征提取和描述算法。它结合了 FAST(Features from Accelerated Segment Test)特征点检测和 BRIEF(Binary Robust Independent Elementary Features)描述符,并且对其进行了改进,使其具有旋转不变性。
import cv2image = cv2.imread('example.jpg')
# 创建 ORB 对象
orb = cv2.ORB_create()
# 检测特征点并计算描述符
keypoints, descriptors = orb.detectAndCompute(image, None)
# 在图像上绘制特征点
image_with_keypoints = cv2.drawKeypoints(image, keypoints, None, color=(0, 255, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow('ORB Keypoints', image_with_keypoints)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:首先使用 cv2.ORB_create()
创建一个 ORB 对象。detectAndCompute()
方法用于检测图像中的特征点并计算对应的描述符。特征点是图像中具有独特特征的点,描述符则是对这些特征点的一种量化表示,用于后续的特征匹配。cv2.drawKeypoints()
函数将检测到的特征点绘制在原始图像上,方便我们直观地看到特征点的位置。
(二)SIFT 特征提取
SIFT(尺度不变特征变换)是一种具有尺度、旋转和光照不变性的特征提取算法,但由于专利问题,在使用时需要安装 opencv - contrib - python
库。SIFT 算法通过在不同尺度空间上查找极值点来检测特征点,并计算其主方向和描述符。
import cv2image = cv2.imread('example.jpg')
# 创建 SIFT 对象
sift = cv2.SIFT_create()
# 检测特征点并计算描述符
keypoints, descriptors = sift.detectAndCompute(image, None)
# 在图像上绘制特征点
image_with_keypoints = cv2.drawKeypoints(image, keypoints, None, color=(0, 255, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow('SIFT Keypoints', image_with_keypoints)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:cv2.SIFT_create()
用于创建一个 SIFT 对象。与 ORB 类似,detectAndCompute()
方法检测特征点并计算描述符。SIFT 算法对图像的尺度、旋转和光照变化具有较好的鲁棒性,因此在很多场景下都能得到较好的特征提取效果。最后使用 cv2.drawKeypoints()
函数将特征点绘制在图像上进行可视化。
(三)特征匹配
特征匹配是计算机视觉中一个重要的任务,用于在不同图像中找到对应的特征点。这里将分别介绍使用 ORB 特征和 SIFT 特征进行匹配的方法。
1. ORB 特征匹配
使用 cv2.BFMatcher()
进行 ORB 特征匹配。
import cv2image1 = cv2.imread('image1.jpg')
image2 = cv2.imread('image2.jpg')orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(image1, None)
kp2, des2 = orb.detectAndCompute(image2, None)# 创建 Brute - Force 匹配器
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# 匹配描述符
matches = bf.match(des1, des2)
# 按照距离排序
matches = sorted(matches, key=lambda x: x.distance)
# 绘制前 10 个匹配点
matching_result = cv2.drawMatches(image1, kp1, image2, kp2, matches[:10], None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
cv2.imshow('ORB Feature Matching', matching_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:首先分别对两张图像使用 ORB 算法检测特征点并计算描述符。cv2.BFMatcher()
创建一个暴力匹配器,cv2.NORM_HAMMING
表示使用汉明距离来衡量描述符之间的相似度,crossCheck = True
表示只保留那些相互匹配的特征点对。bf.match()
方法进行特征匹配,返回匹配结果。将匹配结果按照距离排序,距离越小表示匹配度越高。最后使用 cv2.drawMatches()
函数绘制前 10 个匹配点,方便我们观察匹配效果。
2. SIFT 特征匹配
对于 SIFT 特征,可以使用 FLANN(快速最近邻近似库)进行匹配。
import cv2
import numpy as npimage1 = cv2.imread('image1.jpg')
image2 = cv2.imread('image2.jpg')sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(image1, None)
kp2, des2 = sift.detectAndCompute(image2, None)# FLANN 参数设置
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)# 创建 FLANN 匹配器
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 匹配描述符
matches = flann.knnMatch(des1, des2, k=2)# 应用比率测试
good_matches = []
for m, n in matches:if m.distance < 0.7 * n.distance:good_matches.append(m)# 绘制匹配点
matching_result = cv2.drawMatches(image1, kp1, image2, kp2, good_matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow('SIFT Feature Matching', matching_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
解释:同样先对两张图像使用 SIFT 算法检测特征点并计算描述符。FLANN 是一种快速的近似最近邻搜索算法,适合处理大规模的特征匹配问题。index_params
和 search_params
分别设置 FLANN 的索引参数和搜索参数。flann.knnMatch()
方法返回每个特征点的 k 个最近邻匹配结果。为了提高匹配的准确性,使用比率测试,即当第一个匹配的距离小于第二个匹配距离的 0.7 倍时,才认为这个匹配是有效的。最后使用 cv2.drawMatches()
函数绘制有效的匹配点。
后面的区域~以后的文章再来探索吧
相关文章:
深入剖析 OpenCV:全面掌握基础操作、图像处理算法与特征匹配
深入剖析 OpenCV:全面掌握基础操作、图像处理算法与特征匹配 一、引言二、OpenCV 的安装(一)使用 pip 安装(二)使用 Anaconda 安装 三、OpenCV 基础操作(一)图像的读取、显示与保存(…...
【C语言显示Linux系统参数】
在C语言中,可以通过调用Linux系统提供的API来获取和显示系统参数。以下是一些常见的系统参数及其获取方法: 1. 获取系统名称和版本 可以使用uname函数来获取系统名称、版本等信息。 #include <stdio.h> #include <sys/utsname.h>int main…...
突破Ajax跨域困境,解锁前端通信新姿势
一、引言 在当今的 Web 开发领域,前后端分离的架构模式已经成为主流,它极大地提升了开发效率和项目的可维护性。在这种开发模式下,前端通过 Ajax 技术与后端进行数据交互,然而,跨域问题却如影随形,成为了开…...
Kotlin协变与逆变区别
在Kotlin中,协变和逆变是泛型编程中的两个重要概念,它们允许我们在类型系统中更加灵活地处理类型关系。 1.协变:协变允许我们使用比原始类型更具体的类型。在kotlin中,通过在类型参数上加out关键字来表示协变,生产者,例…...
driver中为什么要使用非阻塞赋值
1. 模拟硬件时序行为 实际硬件行为:DUT的输入信号通常在时钟边沿被采样。Driver需要确保信号的更新与时钟同步,而非阻塞赋值的延迟更新特性(在时间步结束时统一生效)能够准确模拟寄存器的行为。 示例: always (posedg…...

模板字符串【ES6】
“路漫漫其修远兮,吾将上下而求索。”—— 屈原《离骚》 目录 什么是模板字符串?模板字符串特性及代码举例:详细举例用法: 什么是模板字符串? 模板字符串(Template Literals)是JavaScript中引入…...
通往 AI 之路:Python 机器学习入门-数据结构
Python 数据结构 Python 提供了多种数据结构来存储和操作数据,其中列表(list)、字典(dict)、元组(tuple)和集合(set)是最常用的几种。本章将详细介绍这些数据结构的基本…...
我们应该如何优化UI(基于UGUI)
这是一道面试题,下面,我们来详细分析这个问题。 目录 1. 减少 Draw Call 合理设置图集 避免材质和 Shader 的频繁切换 减少 UI 元素的重叠 2. 优化UI布局 3. 优化UI元素的渲染 4.优化UI动画 5. 优化 UI 事件处理 6. 运行时优化 1. 减少 Draw C…...
CSS3 圆角:实现与优化指南
CSS3 圆角:实现与优化指南 随着网页设计的发展,CSS3 圆角已经成为了现代网页设计中不可或缺的元素之一。本文将详细讲解 CSS3 圆角的基本用法、实现方式以及优化技巧,帮助您在网页设计中更好地运用这一功能。 一、CSS3 圆角基本用法 1.1 基…...
【网络安全 | 扫描子域+发现真实IP】CloakQuest3r安装使用详细教程
原创文章,禁止转载。 本文仅作学习交流使用,不得用于非法渗透,笔者不承担任何责任。 文章目录 简介功能介绍执行流程限制安装步骤可选功能:SecurityTrails API使用示例简介 CloakQuest3r 是一款强大的 Python 工具,专为揭示受 Cloudflare 及类似服务保护的网站真实 IP 地…...

Mellanox OFED驱动如何给全局编译添加gcc的编译选项?(subdir-ccflags-y += -Wall)
背景 有些时候编译驱动需要给全局加一个编译选项,假设configure已经完成。可以直接在Makefile中修改 添加方式 修改OFED驱动目录下的: ./Makefile subdir-ccflags-y -Wall修改效果: 然后执行make,就能让添加的编译选项生效…...

【愚公系列】《Python网络爬虫从入门到精通》037-文件的存取
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…...

【一起学Rust | Tauri2.0框架】单实例应用程序的深入解析:零漏洞实现与优化实战
文章目录 前言一、 单实例应用的意义二、 实现单实例应用的方法1 Windows下的实现1.1 创建命名Mutex1.2 在Tauri应用中集成Mutex检查 2 macOS下的实现2.1 获取Bundle Identifier2.2 检查是否已经有实例在运行 3 Linux下的实现3.1 获取进程列表3.2 检查是否已经有实例在运行 4 在…...
PhyloSuite v1.2.3安装与使用-生信工具049
PhyloSuite 一个好用的win集成建树平台,官方相关文档视频等做的可好了PhyloSuite (jushengwu.com) 官网 https://github.com/dongzhang0725/PhyloSuite/releases #官网 http://phylosuite.jushengwu.com/dongzhang0725.github.io/installation/ #官方说明文档…...
使用Apache Lucene构建高效的全文搜索服务
使用Apache Lucene构建高效的全文搜索服务 在现代应用程序中,全文搜索功能是不可或缺的一部分。无论是电子商务网站、内容管理系统,还是数据分析平台,快速、准确地搜索大量数据是提升用户体验的关键。Apache Lucene 是一个强大的全文搜索引擎…...
SSH远程登录并执行命令
SSH远程登录并执行命令 1、登录远程服务器2、远程执行命令3、远程执行交互命令4、远程执行脚本5、退出远程SSH连接 SSH是Linux中的远程连接管理工具,可以在本地服务器上通过SSH协议连接到远程服务器,并在远程服务器上执行命令 SSH不仅可以用来登录远程服…...

EasyRTC:支持任意平台设备的嵌入式WebRTC实时音视频通信SDK解决方案
随着互联网技术的飞速发展,实时音视频通信已成为各行各业数字化转型的核心需求之一。无论是远程办公、在线教育、智慧医疗,还是智能安防、直播互动,用户对低延迟、高可靠、跨平台的音视频通信需求日益增长。 一、WebRTC与WebP2P:实…...

Golang语言特性
1.Go语言的优势 1.1极简单的部署方式 —可以直接编译成机器码。代码可以直接转换为二进制数据,在操作系统上可以直接./去执行。 —不依赖其他库。最终生成的可执行程序是一个静态的二进制文本文件。 —可以直接运行即可部署。 —静态类型语言。编译的时候检查出来隐…...
LangPrompt提示词
LangPrompt提示词 https://github.com/langgptai/LangGPT 学习LangGPT的仓库,帮我创建 一个专门生成LangGPT格式prompt的助手 根据LangGPT的格式规范设计的专业提示词生成助手框架。以下是分步骤的解决方案: 助手角色定义模板 # Role: LangGPT提示词架…...
Java 容器之 List
在 Java 的集合框架中,List 是 Collection 的重要子接口,以其有序、可重复的特点广泛应用于开发中。本文将详细探讨 List 的核心概念、主要实现类(如 ArrayList 和 LinkedList)的底层原理,以及使用中需要注意的常见问题…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...