深入剖析 OpenCV:全面掌握基础操作、图像处理算法与特征匹配

news2025/2/28 11:33:53

深入剖析 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 基础操作

(一)图像的读取、显示与保存

  1. 读取图像
    使用 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 等支持透明度的图像格式。

  1. 显示图像
    通过 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 创建的窗口,释放资源。

  1. 保存图像
    利用 cv2.imwrite() 函数保存图像,第一个参数为保存路径,第二个参数是要保存的图像。
# 保存灰度图像
cv2.imwrite('saved_gray_image.jpg', gray_image)

解释:cv2.imwrite() 函数将指定的图像保存到指定的文件路径。保存的文件格式由文件扩展名决定,如 .jpg.png 等。

(二)图像的基本属性与操作

  1. 图像的基本属性
    图像在 OpenCV 中以 NumPy 数组形式存储,我们可以获取其高度、宽度和通道数等属性。
import cv2

image = cv2.imread('example.jpg')
height, width, channels = image.shape
print(f"图像高度: {height}, 宽度: {width}, 通道数: {channels}")
# 额外获取图像的数据类型
print(f"图像的数据类型: {image.dtype}")

解释:图像在 OpenCV 里被存储为 NumPy 数组,通过 shape 属性可以获取图像的基本尺寸信息,dtype 属性则能查看图像的数据类型,通常为 uint8(8 位无符号整数)。

  1. 图像的像素访问
    可以直接通过索引访问和修改图像的像素值。
import cv2

image = 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(蓝、绿、红)顺序存储的。

  1. 图像的裁剪、缩放与旋转
  • 裁剪
    通过指定行和列的范围来裁剪图像。
import cv2

image = 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 cv2

image = 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() 函数提供了灵活的缩放方式。fxfy 参数用于指定水平和垂直方向的缩放比例,当第二个参数为 None 时使用这种方式。也可以直接指定目标尺寸,如 (300, 300) 来进行缩放。

  • 旋转
    借助 cv2.getRotationMatrix2D() 获取旋转矩阵,再用 cv2.warpAffine() 进行旋转操作。
import cv2
import numpy as np

image = 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 cv2

color_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 的图像处理算法

(一)图像滤波

  1. 均值滤波
    使用 cv2.blur() 函数实现,通过计算局部区域像素平均值平滑图像。
import cv2

image = 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))在图像上滑动,计算每个核内像素的平均值,并将该平均值作为中心像素的新值,从而达到平滑图像的效果。

  1. 高斯滤波
    利用 cv2.GaussianBlur() 函数,基于高斯函数对图像进行滤波,抑制噪声同时保留边缘。
import cv2

image = 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()

解释:高斯滤波是一种非线性滤波方法,它根据高斯函数对核内的像素进行加权平均。相比于均值滤波,高斯滤波在抑制噪声的同时能更好地保留图像的边缘信息,因为它对不同位置的像素赋予了不同的权重。

  1. 中值滤波
    通过 cv2.medianBlur() 函数,用局部区域的中值替换当前像素值,有效去除椒盐噪声。
import cv2

image = 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()

解释:中值滤波是一种非线性滤波方法,它将核内的像素值进行排序,然后用中值替换中心像素的值。这种方法对于去除椒盐噪声(图像中出现的黑白孤立点)非常有效。

  1. 双边滤波
    cv2.bilateralFilter() 函数可以在平滑图像的同时保留边缘信息,适用于需要保留图像细节的场景。
import cv2

image = 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()

解释:双边滤波是一种保边滤波方法,它结合了空间域和值域的信息。在平滑图像时,它不仅考虑像素的空间距离,还考虑像素的颜色差异,因此能够在平滑图像的同时很好地保留边缘和细节信息。

(二)边缘检测

  1. Canny 边缘检测
    cv2.Canny() 函数是常用的边缘检测算法,通过计算图像梯度和非极大值抑制等步骤检测边缘。
import cv2

image = 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 边缘检测是一种多阶段的边缘检测算法,主要包括高斯平滑、梯度计算、非极大值抑制和双阈值处理等步骤。高低阈值参数用于控制边缘的检测范围,只有当梯度值大于高阈值时才被确定为边缘,介于高低阈值之间的像素如果与确定的边缘相连也会被保留。

  1. Sobel 边缘检测
    cv2.Sobel() 函数用于计算图像在 x 和 y 方向上的梯度,从而检测边缘。
import cv2
import numpy as np

image = 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 方向上的梯度。通过分别计算两个方向的梯度,可以检测出图像中的水平和垂直边缘。最后将两个方向的梯度合并,得到完整的边缘信息。

  1. Laplacian 边缘检测
    cv2.Laplacian() 函数通过计算图像的二阶导数来检测边缘。
import cv2

image = 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 位无符号整数才能正确显示边缘图像。

(三)形态学操作

  1. 膨胀与腐蚀
    膨胀操作可以扩大图像中的前景区域,腐蚀操作则可以缩小前景区域。
import cv2
import numpy as np

image = 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 矩阵)对图像进行处理。膨胀操作将结构元素在图像上滑动,如果结构元素与前景区域有重叠,则将中心像素置为前景;腐蚀操作则相反,如果结构元素完全包含在前景区域内,才将中心像素置为前景。

  1. 开运算与闭运算
    开运算先进行腐蚀操作,再进行膨胀操作,用于去除小的噪声点;闭运算先进行膨胀操作,再进行腐蚀操作,用于填充小的空洞。
import cv2
import numpy as np

image = 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()

解释:开运算和闭运算是基于膨胀和腐蚀操作的组合。开运算可以去除图像中的小噪声点,同时保持大的前景区域不变;闭运算则可以填充前景区域内的小空洞,使前景区域更加完整。

  1. 形态学梯度
    形态学梯度是膨胀图像与腐蚀图像的差值,用于突出图像的边缘。形态学梯度能够帮助我们检测图像中物体的边界,在图像分割、目标检测等领域有着广泛的应用。
import cv2
import numpy as np

image = 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 cv2

image = 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 cv2

image = 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 cv2

image1 = 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 np

image1 = 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_paramssearch_params 分别设置 FLANN 的索引参数和搜索参数。flann.knnMatch() 方法返回每个特征点的 k 个最近邻匹配结果。为了提高匹配的准确性,使用比率测试,即当第一个匹配的距离小于第二个匹配距离的 0.7 倍时,才认为这个匹配是有效的。最后使用 cv2.drawMatches() 函数绘制有效的匹配点。

后面的区域~以后的文章再来探索吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2307359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Immich自托管服务的本地化部署与随时随地安全便捷在线访问数据

文章目录 前言1.关于Immich2.安装Docker3.本地部署Immich4.Immich体验5.安装cpolar内网穿透6.创建远程链接公网地址7.使用固定公网地址远程访问 前言 小伙伴们&#xff0c;你们好呀&#xff01;今天要给大家揭秘一个超炫的技能——如何把自家电脑变成私人云相册&#xff0c;并…

Apache-iotdb 基本概念

问题背景 定义&#xff08;写得太好了&#xff01;&#xff09; root 是整个树状结构的父节点&#xff0c; CirroData-TimeS 有存储组、设备、测点等概念&#xff0c;数据在存储的时候&#xff0c;不同的存储组的数据是存储在不同的文件夹中的。上图中有 root.sgcc、root.ln两…

CryptoJS库中WordArray对象支持哪些输出格式?除了toString() 方法还有什么方法可以输出吗?WordArray对象的作用是什么?

前言&#xff1a;这里只说js用的CryptoJS库里的相关内容&#xff0c;只用js来进行代码操作和讲解。 这里网上相关的帖子很少&#xff0c;不得已问了很长时间AI 想引用CryptoJS库情况分两种&#xff0c;一种是html引用&#xff0c;另一种是在Nodejs里引用。 一、引用CryptoJS库…

springboot浅析

springboot浅析 什么是springboot&#xff1f; 实际上springboot就是一个给我们提供了快速搭建使用spring的一种方式&#xff0c;让我们省去了繁琐的xml配置。 为什么无需进行大量的xml配置&#xff0c;就是因为springboot是基于约定优于配置的思想&#xff0c;简单来说就是遵循…

【文件基础操作】小笔记

Step1: 现在项目文件夹&#xff08;我的项目叫做RunPony&#xff09;下创建一个a.txt文本文件&#xff0c;手动写入一些数字&#xff0c;保存 Step2: 现在在main.c内写一个基本的文件处理的程序 Step3: 现在已经知道如何打开关闭文件&#xff0c;下一步要搞懂如何读取txt内的…

SSL 证书是 SSL 协议实现安全通信的必要组成部分

SSL证书和SSL/TLS协议有着密切的关系&#xff0c;但它们本质上是不同的概念。下面是两者的区别和它们之间的关系的表格&#xff1a; 属性SSL/TLS 协议SSL证书英文全称SSL&#xff08;Secure Sockets Layer&#xff09;&#xff0c;TLS&#xff08;Transport Layer Security&am…

AI问答-供应链管理:排队模型M/D/5/100/m/FCFS代表的含义是什么

在供应链管理中&#xff0c;排队模型M/D/5/100/m/FCFS代表的含义如下&#xff1a; M&#xff1a; 表示顾客到达时间间隔服从负指数分布&#xff08;Markov&#xff0c;负指数分布具有无记忆性&#xff09;&#xff0c;即顾客到达是随机的&#xff0c;且到达时间间隔服从指数分…

Linux驱动学习(四)--字符设备注册

上一节讲到的字符设备注册与销毁是通过cdev_init、cdev_add、cdev_del等函数分步执行的&#xff0c;本小节用一种更简单的方式&#xff0c;来注册字符设备 register_chrdev 如果major为0&#xff0c;该函数将动态的分配一个主设备号并且返回对应的值如果major > 0&#xff…

30天开发操作系统 第24天 -- 窗口操作

一、窗口切换 1.0 前天开始我们的应用程序可以显示自己的窗口了&#xff0c;现在画面上到处都是窗口&#xff0c;我们急需能够 切换窗口顺序的功能&#xff0c;使得在需要的时候可以查 看最下面的窗口的内容。这个功能看起来不难&#xff0c;我们马上来实现它。 不过&#xf…

Visual Studio 中 C/C++ 函数不安全警告(C4996)终极解决方案:分场景实战指南

问题描述 在 Visual Studio 中编写 C/C 代码时&#xff0c;使用 scanf、strcpy、fopen 等传统函数会触发以下警告&#xff1a; C4996: xxx: This function or variable may be unsafe. Consider using xxx_s instead. 根本原因&#xff1a; 这些函数缺乏缓冲区溢出检查&#…

提升数据洞察力:五款报表软件助力企业智能决策

概述 随着数据量的激增和企业对决策支持需求的提升&#xff0c;报表软件已经成为现代企业管理中不可或缺的工具。这些软件能够帮助企业高效处理数据、生成报告&#xff0c;并将数据可视化&#xff0c;从而推动更智能的决策过程。 1. 山海鲸报表 概述&#xff1a; 山海鲸报表…

Materials Studio MS2020在linux系统上的安装包下载地址 支持centos Ubuntu rocky等系统

下载地址&#xff1a;MS2020-linux官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 Materials Studio 2020是一款功能强大的材料科学计算模拟软件&#xff0c;以下是其详细介绍&#xff1a; 核心模块功能 CASTEP模块&#xff1a;采用平面波赝势方法&#xff0c;适用于周…

【语音编解码】常用的基于神经网络的语音编解码方案对比

引言 随着实时通信与多媒体应用的爆炸式增长&#xff0c;传统语音编解码技术正面临带宽效率与音质保真的双重挑战。近年来&#xff0c;基于深度学习的神经编解码器突破性地将端到端架构、动态码率控制与可解释信号处理相结合&#xff0c;在3kbps以下超低码率场景仍能保持自然语…

DeepSeek行业应用实践报告-智灵动力【112页PPT全】

DeepSeek&#xff08;深度搜索&#xff09;近期引发广泛关注并成为众多企业/开发者争相接入的现象&#xff0c;主要源于其在技术突破、市场需求适配性及生态建设等方面的综合优势。以下是关键原因分析&#xff1a; 一、技术核心优势 开源与低成本 DeepSeek基于开源架构&#xf…

a_init: Unable to get log name. Retval:[-4]是什么故障

突然 接到监控告警 aix数据库内存使用超过阈值&#xff0c;请分析 先看内存使用吧 topas中能看到comp内存使用79%&#xff0c;非计算9% 看看哪个进程占用多呢 占用内存最高的20个进程(aix) ps aux |head -1 ; ps aux|sort -rn 4 |head -20看到rbal进程占用11%&#xff0c;比…

利用node.js搭配express框架写后端接口(一)

Node.js 凭借其高效的非阻塞 I/O 操作、事件驱动架构以及轻量级的特点&#xff0c;成为了开发高性能服务器应用的热门选择。Express 框架作为 Node.js 上最流行的 Web 应用框架之一&#xff0c;以其简洁的 API 和丰富的中间件生态系统&#xff0c;极大地简化了 Web 后端开发流程…

CentOS中shell脚本对多台机器执行下载安装

1.建立免密ssh连接 详情见这篇&#xff1a; CentOS建立ssh免密连接&#xff08;含流程剖析&#xff09;-CSDN博客 2.脚本编写 我这里只是简单写了个demo进行演示&#xff0c;如果服务器很多可以先暂存成文件再逐行读取host进行连接并执行命令 用node1去ssh连接node2和node…

深入剖析:自定义实现C语言中的atoi函数

在C语言的标准库中&#xff0c; atoi 函数是一个非常实用的工具&#xff0c;它能够将字符串形式的数字转换为对应的整数。然而&#xff0c;当我们深入探究其实现原理时&#xff0c;会发现其中蕴含着许多有趣的编程技巧和细节。本文将详细讲解如何自定义实现一个类似 atoi 功能的…

Flutter 学习之旅 之 flutter 在 Android 端读取相册图片显示

Flutter 学习之旅 之 flutter 在 Android 端读取相册图片显示 目录 Flutter 学习之旅 之 flutter 在 Android 端读取相册图片显示 一、简单介绍 二、简单介绍 image_picker 三、安装 image_picker 四、简单案例实现 五、关键代码 代码说明&#xff1a; 一、简单介绍 Fl…

数据结构秘籍(一)线性数据结构

1.数组 数组&#xff08;Array&#xff09;是一种很常见的数据结构。它由相同类型的元素&#xff08;element&#xff09;组成&#xff0c;并且是使用一块连续的内存来存储。 我们直接可以利用元素的索引&#xff08;index&#xff09;计算出该元素对应的存储地址。 数组的特…