OpenCV学习笔记

37k 词

一、基础操作

读取显示图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 导入 OpenCV 库
import cv2

# 1. 读取图像
# 替换为实际的图像路径,这里是当前目录下的 "bird.jpg"
image_path = "./bird.jpg"
image = cv2.imread(image_path)

# 检查图像是否成功读取
if image is None:
print("错误:无法加载图像,请检查路径是否正确。")
exit()

# 2. 显示图像
# 创建一个名为 "Display Image" 的窗口,并在其中显示图像
cv2.imshow("Display Image", image)

# 3. 等待用户按键
# 参数 0 表示无限等待,直到用户按下任意键
key = cv2.waitKey(0)

# 4. 根据用户按键执行操作
if key == ord('s'): # 如果按下 's' 键
# 保存图像
output_path = "saved_image.jpg"
cv2.imwrite(output_path, image)
print(f"图像已保存为 {output_path}")
else: # 如果按下其他键
print("图像未保存。")

# 5. 关闭所有窗口
cv2.destroyAllWindows()

读取图像:cv2.imread(image_path)

在窗口中显示图像:cv2.imshow("Display Image", image)

保存图像: cv2.imwrite(output_path, image)

关闭窗口:cv2.destroyAllWindows()

二、图像处理基础

图像的基本属性:

  • 图像的尺寸(Width, Height):可以通过 img.shape 获取。
  • 颜色通道(Channels):通常为 RGB(三个通道),也可以是灰度图(单通道)。
  • 数据类型(Data type):常见的有 uint8(0-255 范围),也可以是 float32 或其他。

读取图像:

1
2
import cv2
img = cv2.imread('image.jpg')
  • cv2.imread() 读取图像文件,返回一个 NumPy 数组。
  • 如果图像路径错误或文件不存在,返回 None

显示图像:

1
2
3
4
5
6
7
8
# 显示图像
cv2.imshow("Display Window", image)

# 等待按键输入
cv2.waitKey(0)

# 关闭所有窗口
cv2.destroyAllWindows()

cv2.imshow() 用于显示图像,cv2.waitKey(0) 等待按键事件,cv2.destroyAllWindows() 关闭所有窗口。

保存图像:

1
2
# 保存图像
cv2.imwrite("output_image.jpg", image)

cv2.imwrite() 将图像保存到指定路径。

2.1图像基本操作

1、访问和修改像素值

1
2
3
4
5
# 获取像素值 (BGR 格式)
pixel_value = image[100, 100] # 获取 (100, 100) 处的像素值

# 修改像素值
image[100, 100] = [255, 255, 255] # 将 (100, 100) 处的像素设置为白色

2、图像 ROI(Region of Interest)

1
2
3
4
5
# 获取 ROI
roi = image[50:150, 50:150] # 获取 (50,50) 到 (150,150) 的区域

# 修改 ROI
image[50:150, 50:150] = [0, 255, 0] # 将 ROI 区域设置为绿色

3、图像通道分离与合并

1
2
3
4
5
# 分离通道
b, g, r = cv2.split(image)

# 合并通道
merged_image = cv2.merge([b, g, r])

4、图像缩放、旋转、平移、翻转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 缩放
resized_image = cv2.resize(image, (new_width, new_height))

# 旋转
(h, w) = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0) # 旋转 45 度
rotated_img = cv2.warpAffine(img, M, (w, h))

rotation_matrix = cv2.getRotationMatrix2D((center_x, center_y), angle, scale)
rotated_image = cv2.warpAffine(image, rotation_matrix, (width, height))

# 平移
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]]) # tx, ty 为平移距离
translated_image = cv2.warpAffine(image, translation_matrix, (width, height))

# 翻转
flipped_image = cv2.flip(image, flip_code) # flip_code: 0 (垂直翻转), 1 (水平翻转), -1 (双向翻转)

2.2图像算术运算

2.2.1基础运算

1、图像加法

1
result = cv2.add(image1, image2)

注意:如果像素值相加后超过 255,OpenCV 会自动将其截断为 255。

2、图像减法

1
result = cv2.subtract(image1, image2)

注意:如果像素值相减后小于 0,OpenCV 会自动将其截断为 0。

3、图像乘法

1
result = cv2.multiply(img1, img2)

4.图像除法

1
result = cv2.divide(img1, img2)

2.2.2图像位运算 (AND, OR, NOT, XOR)

位运算是对图像的每个像素进行二进制位操作。

函数 功能 应用场景
cv2.bitwise_and() 按位与操作 掩码操作、图像分割
cv2.bitwise_or() 按位或操作 图像叠加
cv2.bitwise_not() 按位取反操作 图像反色
cv2.bitwise_xor() 按位异或操作 图像差异检测

1.位与运算 (AND)

位与运算是对两幅图像的每个像素进行按位与操作。

1
result = cv2.bitwise_and(img1, img2)

2.位或运算 (OR)

位或运算是对两幅图像的每个像素进行按位或操作。

1
result = cv2.bitwise_or(img1, img2)

3.位非运算 (NOT)

位非运算是对图像的每个像素进行按位取反操作。

1
result = cv2.bitwise_not(img1)

4.位异或运算 (XOR)

位异或运算是对两幅图像的每个像素进行按位异或操作。

1
result = cv2.bitwise_xor(img1, img2)

2.2.3图像混合

在 OpenCV 中,可以使用 cv2.addWeighted() 函数来实现图像混合。

1
2
3
4
5
6
7
8
9
10
11
# 图像混合
alpha = 0.7 # 第一幅图像的权重
beta = 0.3 # 第二幅图像的权重
gamma = 0 # 可选的标量值

result = cv2.addWeighted(img1, alpha, img2, beta, gamma)

# 显示结果
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

参数说明

  • alpha:第一幅图像的权重。
  • beta:第二幅图像的权重。
  • gamma:可选的标量值,通常设置为 0。

公式

1
result = img1 * alpha + img2 * beta + gamma

2.3图像阈值处理

2.3.1简单阈值处理

OpenCV 提供了 cv2.threshold() 函数来实现这一功能。

函数原型

1
retval, dst = cv2.threshold(src, thresh, maxval, type)

参数说明

  • src: 输入图像,通常为灰度图像。

  • thresh: 设定的阈值。

  • maxval: 当像素值超过(或小于,根据类型)阈值时,赋予的新值。

  • type : 阈值处理的类型,常见的类型有:

    • cv2.THRESH_BINARY: 如果像素值大于阈值,则赋予 maxval,否则赋予 0
    • cv2.THRESH_BINARY_INV: 与 cv2.THRESH_BINARY 相反,如果像素值大于阈值,则赋予 0,否则赋予 maxval
    • cv2.THRESH_TRUNC: 如果像素值大于阈值,则赋予阈值,否则保持不变。
    • cv2.THRESH_TOZERO: 如果像素值大于阈值,则保持不变,否则赋予 0
    • cv2.THRESH_TOZERO_INV: 与 cv2.THRESH_TOZERO 相反,如果像素值大于阈值,则赋予 0,否则保持不变。

返回值

  • retval: 实际使用的阈值(在某些情况下可能与设定的阈值不同)。
  • dst: 处理后的图像。

2.3.2自适应阈值处理

在某些情况下,图像的亮度分布不均匀,使用固定的阈值可能无法得到理想的效果。自适应阈值处理通过为图像的不同区域计算不同的阈值,从而更好地处理这种情况。

函数原型

1
dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)

参数说明

  • src: 输入图像,通常为灰度图像。

  • maxValue: 当像素值超过(或小于,根据类型)阈值时,赋予的新值。

  • adaptiveMethod : 自适应阈值计算方法,常见的类型有:

    • cv2.ADAPTIVE_THRESH_MEAN_C: 阈值是邻域的平均值减去常数 C
    • cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 阈值是邻域的加权平均值减去常数 C,权重由高斯函数确定。
  • thresholdType: 阈值处理的类型,通常为 cv2.THRESH_BINARYcv2.THRESH_BINARY_INV

  • blockSize: 计算阈值时使用的邻域大小,必须为奇数。

  • C: 从平均值或加权平均值中减去的常数。

返回值

  • dst: 处理后的图像。

2.3.3Otsu’s 二值化

Otsu’s 二值化是一种自动确定阈值的方法。它通过最大化类间方差来找到最佳的全局阈值,适用于双峰图像(即图像直方图有两个明显的峰值)。

函数原型

1
retval, dst = cv2.threshold(src, thresh, maxval, type)

参数说明

  • src: 输入图像,通常为灰度图像。
  • thresh: 由于 Otsu’s 方法会自动确定阈值,因此该参数通常设置为 0
  • maxval: 当像素值超过(或小于,根据类型)阈值时,赋予的新值。
  • type: 阈值处理的类型,通常为 cv2.THRESH_BINARYcv2.THRESH_BINARY_INV,并加上 cv2.THRESH_OTSU

返回值

  • retval: 自动确定的阈值。
  • dst: 处理后的图像。

2.4图像平滑处理

2.4.1均值滤波

均值滤波是最简单的平滑处理方法之一。

均值滤波的原理是将图像中每个像素的值替换为其周围像素的平均值。

均值滤波可以有效地去除噪声,但可能会导致图像变得模糊。

1
blurred_image = cv2.blur(image, (kernel_size, kernel_size))

cv2.blur(image, (5, 5)) 参数说明**

  • image: 输入的图像。

  • (kernel_size, kernel_size): 滤波核的大小,表示在水平和垂直方向上取平均值的范围。

    均值滤波适用于去除图像中的随机噪声,但可能会导致图像边缘变得模糊。

2.4.2高斯滤波

高斯滤波是一种基于高斯函数的平滑处理方法。与均值滤波不同,高斯滤波在计算像素平均值时,会给中心像素赋予更高的权重,而给边缘像素赋予较低的权重。

高斯滤波在去除噪声的同时,能够更好地保留图像的边缘信息。

1
blurred_image = cv2.GaussianBlur(image, (kernel_size, kernel_size), sigmaX)

cv2.GaussianBlur(image, (5, 5), 0) 参数说明

  • image: 输入的图像。
  • (5, 5): 滤波核的大小。
  • 0: 高斯核的标准差,如果为0,则根据核大小自动计算。

高斯滤波适用于去除图像中的高斯噪声,并且在保留图像边缘信息方面表现较好。

2.4.3中值滤波

中值滤波是一种非线性平滑处理方法。它的原理是将图像中每个像素的值替换为其周围像素的中值。

中值滤波在去除椒盐噪声(即图像中随机出现的黑白点)时非常有效。

1
blurred_image = cv2.medianBlur(image, kernel_size)

cv2.medianBlur(image, 5) 参数说明

  • image: 输入的图像。
  • 5: 滤波核的大小,必须是奇数。

2.4.4双边滤波

双边滤波是一种非线性的平滑处理方法,它结合了空间邻近度和像素值相似度。

与高斯滤波不同,双边滤波在平滑图像的同时,能够保留图像的边缘信息。这是因为双边滤波不仅考虑像素之间的空间距离,还考虑像素值之间的差异。

1
blurred_image = cv2.bilateralFilter(image, d, sigmaColor, sigmaSpace)

cv2.bilateralFilter(image, 9, 75, 75) 参数说明

  • image: 输入的图像。
  • 9: 滤波核的大小。
  • 75: 颜色空间的标准差,控制像素值相似度的权重。
  • 75: 坐标空间的标准差,控制空间距离的权重。

2.5图像的颜色空间与转换

OpenCV 支持多种颜色空间的转换,如从 RGB 到灰度图、HSV 等。

图像的颜色空间转换在许多图像处理任务中非常重要。

从 RGB 转换为灰度图:

1
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

从 BGR 转换为 HSV:

1
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

从 RGB 转换为 YUV:

1
yuv_img = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)

颜色空间转换是图像处理中的基础操作,用于不同的算法和视觉效果。

2.6图像的大小调整与裁剪

OpenCV 提供了多种方式来调整图像大小和裁剪图像区域。

调整图像大小:

1
resized_img = cv2.resize(img, (width, height))

裁剪图像:

1
cropped_img = img[y1:y2, x1:x2]

可以通过切片操作裁剪图像的特定区域。

2.7图像平滑与去噪(模糊处理)

图像平滑可以减少噪声,常用的模糊算法有高斯模糊、均值模糊等。

高斯模糊:

1
blurred_img = cv2.GaussianBlur(img, (5, 5), 0)

均值模糊:

1
blurred_img = cv2.blur(img, (5, 5))

中值模糊:

1
blurred_img = cv2.medianBlur(img, 5)

2.8图像边缘检测

边缘检测是一种常用的图像处理技术,用于检测图像中的边缘,最常用的方法是 Canny 边缘检测。

函数 算法 说明 适用场景
cv2.Canny() Canny 边缘检测 多阶段算法,检测效果较好,噪声抑制能力强。 通用边缘检测,适合大多数场景。
cv2.Sobel() Sobel 算子 基于一阶导数的边缘检测,可以检测水平和垂直边缘。 检测水平和垂直边缘。
cv2.Scharr() Scharr 算子 Sobel 算子的改进版本,对边缘的响应更强。 检测细微的边缘。
cv2.Laplacian() Laplacian 算子 基于二阶导数的边缘检测,对噪声敏感。 检测边缘和角点。

2.8.1.Canny 边缘检测

2.8.1.1 Canny 边缘检测的步骤

Canny 边缘检测算法主要包括以下几个步骤:

  1. 噪声抑制:使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。
  2. 计算梯度:使用 Sobel 算子计算图像的梯度幅值和方向。
  3. 非极大值抑制:沿着梯度方向,保留局部梯度最大的像素点,抑制其他像素点。
  4. 双阈值检测:使用两个阈值(低阈值和高阈值)来确定真正的边缘。高于高阈值的像素点被认为是强边缘,低于低阈值的像素点被抑制,介于两者之间的像素点如果与强边缘相连则保留。
  5. 边缘连接:通过滞后阈值处理,将弱边缘与强边缘连接起来,形成完整的边缘。

2.8.1.2 使用 OpenCV 实现 Canny 边缘检测

在 OpenCV 中,可以使用 cv2.Canny() 函数来实现 Canny 边缘检测。

该函数的原型如下:

1
edges = cv2.Canny(image, threshold1, threshold2, apertureSize=3, L2gradient=False)
  • image:输入图像,必须是单通道的灰度图像。
  • threshold1:低阈值。
  • threshold2:高阈值。
  • apertureSize:Sobel 算子的孔径大小,默认为 3。
  • L2gradient:是否使用 L2 范数计算梯度幅值,默认为 False(使用 L1 范数)。
1
2
3
4
5
6
7
8
9
10
11
12
13
import cv2
import numpy as np

# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 应用 Canny 边缘检测
edges = cv2.Canny(image, 100, 200)

# 显示结果
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.8.2Sobel 算子

Sobel 算子是一种基于梯度的边缘检测算子,它通过计算图像在水平和垂直方向上的梯度来检测边缘。

Sobel 算子结合了高斯平滑和微分操作,因此对噪声具有一定的抑制作用。

2.8.2.1 Sobel 算子的原理

Sobel 算子使用两个 3x3 的卷积核分别计算图像在水平和垂直方向上的梯度:

  • 水平方向的卷积核:

    1
    2
    3
    [-1, 0, 1]
    [-2, 0, 2]
    [-1, 0, 1]
  • 垂直方向的卷积核:

    1
    2
    3
    [-1, -2, -1]
    [ 0, 0, 0]
    [ 1, 2, 1]

通过这两个卷积核,可以分别得到图像在水平和垂直方向上的梯度 GxGy。最终的梯度幅值可以通过以下公式计算:

1
G = sqrt(Gx^2 + Gy^2)

2.8.2.2 使用 OpenCV 实现 Sobel 算子

在 OpenCV 中,可以使用 cv2.Sobel() 函数来计算图像的梯度。该函数的原型如下:

1
dst = cv2.Sobel(src, ddepth, dx, dy, ksize=3, scale=1, delta=0, borderType=cv2.BORDER_DEFAULT)
  • src:输入图像。
  • ddepth:输出图像的深度,通常使用 cv2.CV_64F
  • dx:x 方向上的导数阶数。
  • dy:y 方向上的导数阶数。
  • ksize:Sobel 核的大小,默认为 3。
  • scale:缩放因子,默认为 1。
  • delta:可选的 delta 值,默认为 0。
  • borderType:边界填充类型,默认为 cv2.BORDER_DEFAULT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import cv2
import numpy as np

# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 计算 x 方向的梯度
sobel_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)

# 计算 y 方向的梯度
sobel_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)

# 计算梯度幅值
sobel_combined = np.sqrt(sobel_x**2 + sobel_y**2)

# 显示结果
cv2.imshow('Sobel X', sobel_x)
cv2.imshow('Sobel Y', sobel_y)
cv2.imshow('Sobel Combined', sobel_combined)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.8.3 Laplacian 算子

Laplacian 算子是一种二阶微分算子,它通过计算图像的二阶导数来检测边缘。Laplacian 算子对噪声比较敏感,因此通常在使用之前会对图像进行高斯平滑处理。

2.8.3.1 Laplacian 算子的原理

Laplacian 算子使用以下卷积核来计算图像的二阶导数:

1
2
3
[ 0,  1,  0]
[ 1, -4, 1]
[ 0, 1, 0]

通过这个卷积核,可以得到图像的 Laplacian 值。Laplacian 值较大的区域通常对应于图像的边缘。

2.8.3.2 使用 OpenCV 实现 Laplacian 算子

在 OpenCV 中,可以使用 cv2.Laplacian() 函数来计算图像的 Laplacian 值。

该函数的原型如下:

1
dst = cv2.Laplacian(src, ddepth, ksize=1, scale=1, delta=0, borderType=cv2.BORDER_DEFAULT)
  • src:输入图像。
  • ddepth:输出图像的深度,通常使用 cv2.CV_64F
  • ksize:Laplacian 核的大小,默认为 1。
  • scale:缩放因子,默认为 1。
  • delta:可选的 delta 值,默认为 0。
  • borderType:边界填充类型,默认为 cv2.BORDER_DEFAULT
1
2
3
4
5
6
7
8
9
10
11
12
13
import cv2
import numpy as np

# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 应用 Laplacian 算子
laplacian = cv2.Laplacian(image, cv2.CV_64F)

# 显示结果
cv2.imshow('Laplacian', laplacian)
cv2.waitKey(0)
cv2.destroyAllWindows()

常用边缘检测函数对比

以下是 OpenCV 中常用边缘检测函数的对比:

函数 算法 优点 缺点 适用场景
cv2.Canny() Canny 边缘检测 噪声抑制能力强,边缘检测效果好。 参数调节较为复杂。 通用边缘检测,适合大多数场景。
cv2.Sobel() Sobel 算子 计算简单,适合检测水平和垂直边缘。 对噪声敏感,边缘检测效果一般。 检测水平和垂直边缘。
cv2.Scharr() Scharr 算子 对边缘的响应更强,适合检测细微边缘。 对噪声敏感。 检测细微的边缘。
cv2.Laplacian() Laplacian 算子 可以检测边缘和角点。 对噪声非常敏感。 检测边缘和角点

2.9形态学操作

操作 函数 说明 应用场景
腐蚀 cv2.erode() 用结构元素扫描图像,如果结构元素覆盖的区域全是前景,则保留中心像素。 去除噪声、分离物体。
膨胀 cv2.dilate() 用结构元素扫描图像,如果结构元素覆盖的区域存在前景,则保留中心像素。 连接断裂的物体、填充空洞。
开运算 cv2.morphologyEx() 先腐蚀后膨胀。 去除小物体、平滑物体边界。
闭运算 cv2.morphologyEx() 先膨胀后腐蚀。 填充小孔洞、连接邻近物体。
形态学梯度 cv2.morphologyEx() 膨胀图减去腐蚀图。 提取物体边缘。
顶帽运算 cv2.morphologyEx() 原图减去开运算结果。 提取比背景亮的细小物体。
黑帽运算 cv2.morphologyEx() 闭运算结果减去原图。 提取比背景暗的细小物体。

2.9.1腐蚀

腐蚀操作是一种缩小图像中前景对象的过程。

腐蚀操作通过将结构元素与图像进行卷积,只有当结构元素完全覆盖图像中的前景像素时,中心像素才会被保留,否则会被腐蚀掉。

1
cv2.erode(src, kernel, iterations=1)
  • src: 输入图像,通常是二值图像。
  • kernel: 结构元素,可以自定义或使用 cv2.getStructuringElement() 生成。
  • iterations: 腐蚀操作的次数,默认为1。

腐蚀操作会使图像中的前景对象变小,边缘被腐蚀掉,常用于去除噪声或分离连接的对象。

2.9.2膨胀

膨胀操作与腐蚀相反,它是一种扩大图像中前景对象的过程。

膨胀操作通过将结构元素与图像进行卷积,只要结构元素与图像中的前景像素有重叠,中心像素就会被保留。

1
cv2.dilate(src, kernel, iterations=1)
  • src: 输入图像,通常是二值图像。
  • kernel: 结构元素,可以自定义或使用 cv2.getStructuringElement() 生成。
  • iterations: 膨胀操作的次数,默认为1。

膨胀操作会使图像中的前景对象变大,边缘被扩展,常用于填补前景对象中的空洞或连接断裂的对象。

2.9.3开运算

1
cv2.morphologyEx(src, op, kernel)
  • src: 输入图像,通常是二值图像。
  • op: 形态学操作类型,开运算使用 cv2.MORPH_OPEN
  • kernel: 结构元素,可以自定义或使用 cv2.getStructuringElement() 生成。

开运算可以去除图像中的小噪声,同时保留图像中的主要前景对象。

2.9.4闭运算

闭运算是先膨胀后腐蚀的组合操作。

闭运算主要用于填补前景对象中的小孔或连接断裂的对象。

1
cv2.morphologyEx(src, op, kernel)
  • src: 输入图像,通常是二值图像。
  • op: 形态学操作类型,闭运算使用 cv2.MORPH_CLOSE
  • kernel: 结构元素,可以自定义或使用 cv2.getStructuringElement() 生成。

闭运算可以填补前景对象中的小孔,同时保留图像中的主要前景对象。

2.9.5形态学梯度

形态学梯度是膨胀图像与腐蚀图像的差值。

形态学梯度主要用于提取图像中前景对象的边缘。

1
cv2.morphologyEx(src, op, kernel)
  • src: 输入图像,通常是二值图像。
  • op: 形态学操作类型,形态学梯度使用 cv2.MORPH_GRADIENT
  • kernel: 结构元素,可以自定义或使用 cv2.getStructuringElement() 生成。

2.10图像轮廓检测

2.10.1轮廓检测的基本概念

  • 轮廓: 图像中物体的边界,由一系列点组成。
  • 轮廓层次结构: 轮廓之间的嵌套关系,例如一个轮廓是否包含另一个轮廓。
  • 轮廓特征: 轮廓的面积、周长、边界矩形、最小外接矩形、最小外接圆等。

2.10.2轮廓检测常用函数

函数名称 功能描述
cv2.findContours() 查找图像中的轮廓
cv2.drawContours() 在图像上绘制轮廓
cv2.contourArea() 计算轮廓的面积
cv2.arcLength() 计算轮廓的周长或弧长
cv2.boundingRect() 计算轮廓的边界矩形
cv2.minAreaRect() 计算轮廓的最小外接矩形
cv2.minEnclosingCircle() 计算轮廓的最小外接圆
cv2.approxPolyDP() 对轮廓进行多边形近似

2.10.3 cv2.findContours()

功能描述:
该函数用于在二值图像中查找轮廓。轮廓是图像中具有相同颜色或强度的连续点的曲线。

函数定义:

1
contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

参数说明:

  • image: 输入的二值图像(通常为经过阈值处理或边缘检测后的图像)。

  • mode: 轮廓检索模式,常用的有:

    • cv2.RETR_EXTERNAL: 只检测最外层轮廓。
    • cv2.RETR_LIST: 检测所有轮廓,但不建立层次关系。
    • cv2.RETR_TREE: 检测所有轮廓,并建立完整的层次结构。
  • method: 轮廓近似方法,常用的有:

    • cv2.CHAIN_APPROX_NONE: 存储所有的轮廓点。
    • cv2.CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留端点。
  • contours: 输出的轮廓列表,每个轮廓是一个点集。

  • hierarchy: 输出的层次结构信息。

  • offset: 可选参数,轮廓点的偏移量。

返回值:

  • contours: 检测到的轮廓列表。
  • hierarchy: 轮廓的层次结构信息。

2.10.4 cv2.drawContours()

功能描述:
该函数用于在图像上绘制检测到的轮廓。

函数定义:

1
cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])

参数说明:

  • image: 要绘制轮廓的图像。
  • contours: 轮廓列表。
  • contourIdx: 要绘制的轮廓索引,如果为负数,则绘制所有轮廓。
  • color: 轮廓的颜色。
  • thickness: 轮廓线的厚度,如果为负数,则填充轮廓内部。
  • lineType: 线型。
  • hierarchy: 轮廓的层次结构信息。
  • maxLevel: 绘制的最大层次深度。
  • offset: 轮廓点的偏移量。

返回值:
无返回值,直接在输入图像上绘制轮廓。

2.10.5 cv2.contourArea()

功能描述:
该函数用于计算轮廓的面积。

函数定义:

1
area = cv2.contourArea(contour[, oriented])

参数说明:

  • contour: 输入的轮廓点集。
  • oriented: 可选参数,如果为True,返回有符号的面积。

返回值:
轮廓的面积。

2.10.6 cv2.arcLength()

功能描述:
该函数用于计算轮廓的周长或弧长。

函数定义:

1
length = cv2.arcLength(curve, closed)

参数说明:

  • curve: 输入的轮廓点集。
  • closed: 布尔值,表示轮廓是否闭合。

返回值:
轮廓的周长或弧长。

2.10.7 cv2.boundingRect()

功能描述:
该函数用于计算轮廓的边界矩形。

函数定义:

1
x, y, w, h = cv2.boundingRect(points)

参数说明:

  • points: 输入的轮廓点集。

返回值:
边界矩形的左上角坐标 (x, y) 和宽度 w、高度 h

2.10.8 cv2.minAreaRect()

功能描述:
该函数用于计算轮廓的最小外接矩形(旋转矩形)。

函数定义:

1
rect = cv2.minAreaRect(points)

参数说明:

  • points: 输入的轮廓点集。

返回值:
返回一个旋转矩形,包含中心点 (x, y)、宽度、高度和旋转角度。

2.10.9 cv2.minEnclosingCircle()

功能描述:
该函数用于计算轮廓的最小外接圆。

函数定义:

1
(center, radius) = cv2.minEnclosingCircle(points)

参数说明:

  • points: 输入的轮廓点集。

返回值:
返回圆心 (x, y) 和半径 radius

2.10.10 cv2.approxPolyDP()

功能描述:
该函数用于对轮廓进行多边形近似。

函数定义:

1
approx = cv2.approxPolyDP(curve, epsilon, closed)

参数说明:

  • curve: 输入的轮廓点集。
  • epsilon: 近似精度,值越小,近似越精确。
  • closed: 布尔值,表示轮廓是否闭合。

返回值:
返回近似后的多边形点集。

2.10.11轮廓检测的应用

  • 物体识别: 通过轮廓检测可以识别图像中的物体,例如检测圆形、矩形等。
  • 形状分析: 通过计算轮廓的特征(如面积、周长、边界矩形等),可以分析物体的形状。
  • 目标跟踪: 在视频中,可以通过轮廓检测跟踪运动的目标。
  • 图像分割: 通过轮廓检测可以将图像中的物体分割出来。

OpenCV 提供了强大的轮廓检测功能,可以用于提取图像中物体的边界,并计算轮廓的特征。

以下是轮廓检测的主要步骤和函数:

步骤 函数 说明
图像预处理 cv2.cvtColor() 将图像转换为灰度图。
二值化处理 cv2.threshold() 将灰度图转换为二值图像。
查找轮廓 cv2.findContours() 查找图像中的轮廓。
绘制轮廓 cv2.drawContours() 绘制检测到的轮廓。
计算轮廓面积 cv2.contourArea() 计算轮廓的面积。
计算轮廓周长 cv2.arcLength() 计算轮廓的周长。
计算边界矩形 cv2.boundingRect() 计算轮廓的边界矩形。
计算最小外接矩形 cv2.minAreaRect() 计算轮廓的最小外接矩形。
计算最小外接圆 cv2.minEnclosingCircle() 计算轮廓的最小外接圆。
多边形逼近 cv2.approxPolyDP() 对轮廓进行多边形逼近。

以下是一个完整的轮廓检测示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import cv2

# 读取图像
image = cv2.imread("path/to/image")

# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 二值化处理
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 绘制轮廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)

# 显示结果
cv2.imshow("Contours", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.11图像直方图

2.11.1什么是图像直方图?

图像直方图是图像像素强度分布的图形表示,对于灰度图像,直方图显示了每个灰度级(0到255)在图像中出现的频率,对于彩色图像,我们可以分别计算每个通道(如R、G、B)的直方图。

直方图可以帮助我们了解图像的亮度、对比度等信息。例如,如果直方图集中在低灰度区域,说明图像偏暗;如果直方图分布均匀,说明图像对比度较好。

  • 直方图: 表示图像中像素强度的分布情况,横轴表示像素强度值,纵轴表示该强度值的像素数量。
  • 灰度直方图: 针对灰度图像的直方图,表示每个灰度级的像素数量。
  • 颜色直方图: 针对彩色图像的直方图,分别表示每个颜色通道(如 BGR)的像素强度分布。

OpenCV 提供了丰富的直方图计算和操作函数:

功能 函数 说明
计算直方图 cv2.calcHist() 计算图像的直方图。
直方图均衡化 cv2.equalizeHist() 增强图像的对比度。
直方图比较 cv2.compareHist() 比较两个直方图的相似度。
绘制直方图 matplotlib.pyplot.plot() 使用 Matplotlib 绘制直方图。

2.11.2OpenCV 中的直方图计算函数

在 OpenCV 中,我们可以使用 cv2.calcHist() 函数来计算图像的直方图。

cv2.calcHist() 函数语法

1
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])

参数说明

  • images: 输入的图像列表,通常是一个包含单通道或多通道图像的列表。例如 [img]
  • channels: 需要计算直方图的通道索引。对于灰度图像,使用 [0];对于彩色图像,可以使用 [0][1][2] 分别计算蓝色、绿色和红色通道的直方图。
  • mask: 掩码图像。如果指定了掩码,则只计算掩码区域内的像素。如果不需要掩码,可以传入 None
  • histSize: 直方图的 bin 数量。对于灰度图像,通常设置为 [256],表示将灰度级分为 256 个 bin。
  • ranges: 像素值的范围。对于灰度图像,通常设置为 [0, 256],表示像素值的范围是 0 到 255。
  • hist: 输出的直方图数组。
  • accumulate: 是否累积直方图。如果设置为 True,则直方图不会被清零,而是在每次调用时累积。

假设我们有一张灰度图像 img,我们可以使用以下代码计算其直方图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取图像
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 计算直方图
hist = cv2.calcHist([img], [0], None, [256], [0, 256])

# 绘制直方图
plt.plot(hist)
plt.title('Grayscale Histogram')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.show()

2.11.3直方图均衡化

直方图均衡化是一种增强图像对比度的方法,通过重新分配像素强度值,使直方图更加均匀。

语法:

1
equalized_image = cv2.equalizeHist(image)
1
2
3
4
5
6
7
# 直方图均衡化
equalized_image = cv2.equalizeHist(image)

# 显示结果
cv2.imshow("Equalized Image", equalized_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.11.4颜色直方图

计算颜色直方图

对于彩色图像,可以分别计算每个颜色通道的直方图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 读取彩色图像
image = cv2.imread("path/to/image")

# 计算 BGR 各通道的直方图
colors = ('b', 'g', 'r')
for i, color in enumerate(colors):
hist = cv2.calcHist([image], [i], None, [256], [0, 256])
plt.plot(hist, color=color)

# 绘制直方图
plt.title("Color Histogram")
plt.xlabel("Pixel Intensity")
plt.ylabel("Pixel Count")
plt.show()

颜色直方图均衡化

对于彩色图像,可以对每个通道分别进行直方图均衡化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 分离通道
b, g, r = cv2.split(image)

# 对每个通道进行直方图均衡化
b_eq = cv2.equalizeHist(b)
g_eq = cv2.equalizeHist(g)
r_eq = cv2.equalizeHist(r)

# 合并通道
equalized_image = cv2.merge([b_eq, g_eq, r_eq])

# 显示结果
cv2.imshow("Equalized Color Image", equalized_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

直方图比较

OpenCV 提供了 cv2.compareHist() 函数,用于比较两个直方图的相似度。

语法:

1
similarity = cv2.compareHist(hist1, hist2, method)
  • hist1: 第一个直方图。
  • hist2: 第二个直方图。
  • method: 比较方法,例如 cv2.HISTCMP_CORREL(相关性比较)。
1
2
3
4
5
6
7
# 计算两个图像的直方图
hist1 = cv2.calcHist([image1], [0], None, [256], [0, 256])
hist2 = cv2.calcHist([image2], [0], None, [256], [0, 256])

# 比较直方图
similarity = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
print("Histogram Similarity:", similarity)

直方图的应用

  • 图像增强: 通过直方图均衡化,可以增强图像的对比度,使细节更加清晰。
  • 图像分割: 过分析直方图,可以确定阈值,用于图像分割。
  • 图像匹配: 通过比较直方图,可以判断两幅图像的相似度,用于图像匹配和检索。
  • 颜色分析: 通过颜色直方图,可以分析图像的颜色分布,用于颜色校正和风格化处理。

以下是一个完整的直方图计算和均衡化示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取灰度图像
image = cv2.imread("path/to/image", cv2.IMREAD_GRAYSCALE)

# 计算灰度直方图
hist = cv2.calcHist([image], [0], None, [256], [0, 256])

# 绘制直方图
plt.plot(hist)
plt.title("Grayscale Histogram")
plt.xlabel("Pixel Intensity")
plt.ylabel("Pixel Count")
plt.show()

# 直方图均衡化
equalized_image = cv2.equalizeHist(image)

# 显示结果
cv2.imshow("Equalized Image", equalized_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

三、视频处理

3.1视频基础操作

OpenCV 也支持视频的处理,可以读取视频文件、捕捉视频流并进行实时处理。

3.1.1视频读取与播放

读取视频文件

要读取视频文件,首先需要创建一个 cv2.VideoCapture 对象,并指定视频文件的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import cv2

# 创建 VideoCapture 对象,读取视频文件
cap = cv2.VideoCapture('example.mp4')

# 检查视频是否成功打开
if not cap.isOpened():
print("Error: Could not open video.")
exit()

# 读取视频帧
while True:
ret, frame = cap.read()

# 如果读取到最后一帧,退出循环
if not ret:
break

# 显示当前帧
cv2.imshow('Video', frame)

# 按下 'q' 键退出
if cv2.waitKey(25) & 0xFF == ord('q'):
break

# 释放资源
cap.release()
cv2.destroyAllWindows()

读取摄像头视频

除了读取视频文件,OpenCV 还可以直接从摄像头读取视频,只需将 cv2.VideoCapture 的参数设置为摄像头的索引(通常为0)即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import cv2

# 创建 VideoCapture 对象,读取摄像头视频
cap = cv2.VideoCapture(0)

# 检查摄像头是否成功打开
if not cap.isOpened():
print("Error: Could not open camera.")
exit()

# 读取视频帧
while True:
ret, frame = cap.read()

# 如果读取到最后一帧,退出循环
if not ret:
break

# 显示当前帧
cv2.imshow('Camera', frame)

# 按下 'q' 键退出
if cv2.waitKey(25) & 0xFF == ord('q'):
break

# 释放资源
cap.release()
cv2.destroyAllWindows()

3.1.2帧的基本操作

在读取视频帧后,可以对每一帧进行各种图像处理操作。

例如,可以将帧转换为灰度图像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import cv2

cap = cv2.VideoCapture('example.mp4')

while True:
ret, frame = cap.read()

if not ret:
break

# 将帧转换为灰度图像
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# 显示灰度帧
cv2.imshow('Gray Video', gray_frame)

if cv2.waitKey(25) & 0xFF == ord('q'):
break

cap.release()
cv2.destroyAllWindows()

3.1.3帧的保存

在处理视频帧时,有时需要将处理后的帧保存为新的视频文件。

可以使用 cv2.VideoWriter 类来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import cv2

cap = cv2.VideoCapture('example.mp4')

# 获取视频的帧率和尺寸
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 创建 VideoWriter 对象,保存处理后的视频
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, fps, (width, height))

while True:
ret, frame = cap.read()

if not ret:
break

# 将帧转换为灰度图像
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# 将灰度帧写入输出视频
out.write(cv2.cvtColor(gray_frame, cv2.COLOR_GRAY2BGR))

# 显示灰度帧
cv2.imshow('Gray Video', gray_frame)

if cv2.waitKey(25) & 0xFF == ord('q'):
break

cap.release()
out.release()
cv2.destroyAllWindows()

3.2视频高级操作

3.2.1视频中的物体检测

OpenCV 提供了多种物体检测算法,如 Haar 特征分类器、HOG + SVM 等。

以下是一个使用 Haar 特征分类器进行人脸检测的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import cv2

# 加载 Haar 特征分类器
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

cap = cv2.VideoCapture('example.mp4')

while True:
ret, frame = cap.read()

if not ret:
break

# 将帧转换为灰度图像
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# 检测人脸
faces = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

# 在帧上绘制矩形框标记人脸
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)

# 显示带有人脸标记的帧
cv2.imshow('Face Detection', frame)

if cv2.waitKey(25) & 0xFF == ord('q'):
break

cap.release()
cv2.destroyAllWindows()

3.2.2视频中的运动检测

运动检测是视频处理中的一个重要应用。可以通过计算帧之间的差异来检测运动物体。

以下是一个简单的运动检测示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import cv2

cap = cv2.VideoCapture('example.mp4')

# 读取第一帧
ret, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)

while True:
ret, frame = cap.read()

if not ret:
break

# 将当前帧转换为灰度图像
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

# 计算当前帧与前一帧的差异
frame_diff = cv2.absdiff(prev_gray, gray_frame)

# 对差异图像进行二值化处理
_, thresh = cv2.threshold(frame_diff, 30, 255, cv2.THRESH_BINARY)

# 显示运动检测结果
cv2.imshow('Motion Detection', thresh)

# 更新前一帧
prev_gray = gray_frame

if cv2.waitKey(25) & 0xFF == ord('q'):
break

cap.release()
cv2.destroyAllWindows()

3.2.3常用函数

功能 函数/方法 说明
读取视频 cv2.VideoCapture() 读取视频文件或摄像头。
逐帧读取视频 cap.read() 逐帧读取视频。
获取视频属性 cap.get(propId) 获取视频的属性(如宽度、高度、帧率等)。
保存视频 cv2.VideoWriter() 创建视频写入对象并保存视频。
视频帧处理 图像处理函数(如 cv2.cvtColor() 对视频帧进行图像处理。
目标跟踪 cv2.TrackerKCF_create() 使用目标跟踪算法跟踪视频中的物体。
运动检测 cv2.createBackgroundSubtractorMOG2() 使用背景减除算法检测视频中的运动物体。

cv2.VideoCapture

定义:
cv2.VideoCapture 用于从视频文件或摄像头中捕获视频帧。

语法:

1
cv2.VideoCapture(source)

参数说明:

  • source: 视频文件路径或摄像头索引(通常为0表示默认摄像头)。

cv2.VideoWriter

定义:
cv2.VideoWriter 用于将视频帧写入视频文件。

语法:

1
cv2.VideoWriter(filename, fourcc, fps, frameSize)

参数说明:

  • filename: 输出视频文件名。
  • fourcc: 视频编码器(如 cv2.VideoWriter_fourcc(*'XVID'))。
  • fps: 帧率。
  • frameSize: 帧大小(宽度, 高度)。

cv2.cvtColor

定义:
cv2.cvtColor 用于将图像从一种颜色空间转换为另一种颜色空间。

语法:

1
cv2.cvtColor(src, code)

参数说明:

  • src: 输入图像。
  • code: 颜色空间转换代码(如 cv2.COLOR_BGR2GRAY)。

cv2.resize

定义:
cv2.resize 用于调整图像大小。

语法:

1
cv2.resize(src, dsize)

参数说明:

  • src: 输入图像。
  • dsize: 输出图像大小(宽度, 高度)。

cv2.Canny

定义:
cv2.Canny 用于边缘检测。

语法:

1
cv2.Canny(image, threshold1, threshold2)

参数说明:

  • image: 输入图像。
  • threshold1: 第一个阈值。
  • threshold2: 第二个阈值。

cv2.findContours

定义:
cv2.findContours 用于查找图像中的轮廓。

语法:

1
cv2.findContours(image, mode, method)

参数说明:

  • image: 输入图像。
  • mode: 轮廓检索模式(如 cv2.RETR_TREE)。
  • method: 轮廓近似方法(如 cv2.CHAIN_APPROX_SIMPLE)。

cv2.drawContours

定义:
cv2.drawContours 用于绘制图像中的轮廓。

语法:

1
cv2.drawContours(image, contours, contourIdx, color, thickness)

参数说明:

  • image: 输入图像。
  • contours: 轮廓列表。
  • contourIdx: 轮廓索引(-1表示绘制所有轮廓)。
  • color: 轮廓颜色。
  • thickness: 轮廓线宽。

cv2.putText

定义:
cv2.putText 用于在图像上绘制文本。

语法:

1
cv2.putText(image, text, org, fontFace, fontScale, color, thickness)

参数说明:

  • image: 输入图像。
  • text: 要绘制的文本。
  • org: 文本左下角坐标。
  • fontFace: 字体类型(如 cv2.FONT_HERSHEY_SIMPLEX)。
  • fontScale: 字体缩放比例。
  • color: 文本颜色。
  • thickness: 文本线宽。

3.3视频目标追踪

3.3.1MeanShift 算法

算法原理

MeanShift(均值漂移)算法是一种基于密度的非参数化聚类算法,最初用于图像分割,后来被引入到目标跟踪领域。其核心思想是通过迭代计算目标区域的质心,并将窗口中心移动到质心位置,从而实现目标的跟踪。

MeanShift 算法的基本步骤如下:

  1. 初始化窗口:在视频的第一帧中,手动或自动选择一个目标区域,作为初始窗口。
  2. 计算质心:在当前窗口中,计算目标区域的质心(即像素点的均值)。
  3. 移动窗口:将窗口中心移动到质心位置。
  4. 迭代:重复步骤 2 和 3,直到窗口中心不再变化或达到最大迭代次数。

OpenCV 中的实现

在 OpenCV 中,MeanShift 算法通过 cv2.meanShift() 函数实现。以下是一个简单的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import cv2
import numpy as np

# 读取视频
cap = cv2.VideoCapture('video.mp4')

# 读取第一帧
ret, frame = cap.read()

# 设置初始窗口 (x, y, width, height)
x, y, w, h = 300, 200, 100, 50
track_window = (x, y, w, h)

# 设置 ROI (Region of Interest)
roi = frame[y:y+h, x:x+w]

# 转换为 HSV 颜色空间
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

# 创建掩膜并计算直方图
mask = cv2.inRange(hsv_roi, np.array((0., 60., 32.)), np.array((180., 255., 255.)))
roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0, 180])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)

# 设置终止条件
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)

while True:
ret, frame = cap.read()
if not ret:
break

# 转换为 HSV 颜色空间
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

# 计算反向投影
dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)

# 应用 MeanShift 算法
ret, track_window = cv2.meanShift(dst, track_window, term_crit)

# 绘制跟踪结果
x, y, w, h = track_window
img2 = cv2.rectangle(frame, (x, y), (x+w, y+h), 255, 2)
cv2.imshow('MeanShift Tracking', img2)

if cv2.waitKey(30) & 0xFF == 27:
break

cap.release()
cv2.destroyAllWindows()

优缺点

优点

  • 简单易实现,计算效率高。
  • 对目标的形状和大小变化不敏感。

缺点

  • 对目标的快速运动或遮挡处理能力较差。
  • 窗口大小固定,无法自适应目标大小的变化。

3.3.2CamShift 算法

算法原理

CamShift(Continuously Adaptive MeanShift)算法是 MeanShift 的改进版本,它通过自适应调整窗口大小来更好地跟踪目标。CamShift 算法在 MeanShift 的基础上增加了窗口大小和方向的调整,使其能够适应目标在视频中的尺寸和旋转变化。

CamShift 算法的基本步骤如下:

  1. 初始化窗口:与 MeanShift 相同,在视频的第一帧中选择初始窗口。
  2. 计算质心:在当前窗口中,计算目标区域的质心。
  3. 移动窗口:将窗口中心移动到质心位置。
  4. 调整窗口大小和方向:根据目标的尺寸和方向调整窗口。
  5. 迭代:重复步骤 2 到 4,直到窗口中心不再变化或达到最大迭代次数。

OpenCV 中的实现

在 OpenCV 中,CamShift 算法通过 cv2.CamShift() 函数实现。以下是一个简单的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import cv2
import numpy as np

# 读取视频
cap = cv2.VideoCapture('video.mp4')

# 读取第一帧
ret, frame = cap.read()

# 设置初始窗口 (x, y, width, height)
x, y, w, h = 300, 200, 100, 50
track_window = (x, y, w, h)

# 设置 ROI (Region of Interest)
roi = frame[y:y+h, x:x+w]

# 转换为 HSV 颜色空间
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

# 创建掩膜并计算直方图
mask = cv2.inRange(hsv_roi, np.array((0., 60., 32.)), np.array((180., 255., 255.)))
roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0, 180])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)

# 设置终止条件
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)

while True:
ret, frame = cap.read()
if not ret:
break

# 转换为 HSV 颜色空间
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

# 计算反向投影
dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)

# 应用 CamShift 算法
ret, track_window = cv2.CamShift(dst, track_window, term_crit)

# 绘制跟踪结果
pts = cv2.boxPoints(ret)
pts = np.int0(pts)
img2 = cv2.polylines(frame, [pts], True, 255, 2)
cv2.imshow('CamShift Tracking', img2)

if cv2.waitKey(30) & 0xFF == 27:
break

cap.release()
cv2.destroyAllWindows()

优缺点

优点

  • 能够自适应目标的大小和方向变化。
  • 对目标的形状变化和旋转具有较好的鲁棒性。

缺点

  • 对目标的快速运动或遮挡处理能力仍然有限。
  • 计算复杂度略高于 MeanShift。

3.3.3MeanShift 与 CamShift 的对比

MeanShift 和 CamShift 是两种经典的目标跟踪算法,它们在 OpenCV 中都有现成的实现。

MeanShift 算法简单高效,适用于目标尺寸和方向变化不大的场景,而 CamShift 算法通过自适应调整窗口大小和方向,能够更好地处理目标尺寸和方向的变化。在实际应用中,可以根据具体需求选择合适的算法。

特性 MeanShift CamShift
窗口大小 固定大小 自适应调整大小和方向
适用场景 目标大小固定的场景 目标大小和方向变化的场景
计算复杂度 较低 较高
实时性 较好 稍差

3.4视频背景消除

背景减除的基本概念

背景减除是一种用于视频分析的技术,主要用于检测视频中的运动对象。其基本流程如下:

  1. 背景建模:通过分析视频序列中的多帧图像,建立一个背景模型。
  2. 前景检测:将当前帧与背景模型进行比较,找出与背景差异较大的区域,这些区域即为前景对象。
  3. 背景更新:随着时间的推移,背景可能会发生变化(如光照变化、背景物体的移动等),因此需要不断更新背景模型。

3.4.1MOG(Mixture of Gaussians)算法

原理

MOG 算法是一种基于高斯混合模型(Gaussian Mixture Model, GMM)的背景减除方法。其核心思想是使用多个高斯分布来建模背景中的像素值。每个像素的值被看作是一个随机变量,其分布由多个高斯分布组成。通过这种方式,MOG 能够处理背景中的复杂变化,如光照变化、阴影等。

算法步骤

  1. 初始化:为每个像素初始化多个高斯分布。
  2. 模型更新:对于每一帧图像,更新每个像素的高斯分布参数(均值、方差、权重)。
  3. 前景检测:将当前帧的像素值与背景模型中的高斯分布进行比较,如果像素值不在任何高斯分布的范围内,则将其标记为前景。

OpenCV 中的实现

在 OpenCV 中,MOG 算法可以通过 cv2.bgsegm.createBackgroundSubtractorMOG() 函数来创建背景减除器。以下是一个简单的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import cv2

# 创建 MOG 背景减除器
mog = cv2.bgsegm.createBackgroundSubtractorMOG()

# 读取视频
cap = cv2.VideoCapture('video.mp4')

while True:
ret, frame = cap.read()
if not ret:
break

# 应用背景减除
fg_mask = mog.apply(frame)

# 显示结果
cv2.imshow('Frame', frame)
cv2.imshow('FG Mask', fg_mask)

if cv2.waitKey(30) & 0xFF == ord('q'):
break

cap.release()
cv2.destroyAllWindows()

3.4.2MOG2(Mixture of Gaussians Version 2)算法

原理

MOG2 是 MOG 的改进版本,主要区别在于它能够自动选择高斯分布的数量,并且能够更好地适应背景的变化。MOG2 通过动态调整高斯分布的数量和参数,能够更准确地建模背景,从而提高前景检测的准确性。

算法步骤

  1. 初始化:为每个像素初始化多个高斯分布。
  2. 模型更新:对于每一帧图像,更新每个像素的高斯分布参数,并根据需要增加或减少高斯分布的数量。
  3. 前景检测:将当前帧的像素值与背景模型中的高斯分布进行比较,如果像素值不在任何高斯分布的范围内,则将其标记为前景。

OpenCV 中的实现

在 OpenCV 中,MOG2 算法可以通过 cv2.createBackgroundSubtractorMOG2() 函数来创建背景减除器。以下是一个简单的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import cv2

# 创建 MOG2 背景减除器
mog2 = cv2.createBackgroundSubtractorMOG2()

# 读取视频
cap = cv2.VideoCapture('video.mp4')

while True:
ret, frame = cap.read()
if not ret:
break

# 应用背景减除
fg_mask = mog2.apply(frame)

# 显示结果
cv2.imshow('Frame', frame)
cv2.imshow('FG Mask', fg_mask)

if cv2.waitKey(30) & 0xFF == ord('q'):
break

cap.release()
cv2.destroyAllWindows()

背景减除的应用

  • 视频监控: 用于检测监控视频中的移动目标,如行人、车辆等。
  • 运动分析: 用于分析视频中目标的运动轨迹和行为。
  • 人机交互: 用于检测用户的手势或面部,实现人机交互。

以下是一个完整的 MOG2 背景减除示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import cv2

# 读取视频
cap = cv2.VideoCapture("path/to/video.mp4")

# 创建 MOG2 背景减除器
fgbg = cv2.createBackgroundSubtractorMOG2()

while True:
ret, frame = cap.read()
if not ret:
break

# 应用背景减除器
fgmask = fgbg.apply(frame)

# 显示结果
cv2.imshow("MOG2 Background Subtraction", fgmask)

# 按下 'q' 键退出
if cv2.waitKey(30) & 0xFF == ord('q'):
break

# 释放资源
cap.release()
cv2.destroyAllWindows()

3.4.3MOG 与 MOG2 的比较

背景减除是视频分析中的重要技术,MOG 和 MOG2 是 OpenCV 中常用的两种背景减除算法。

MOG 算法通过固定数量的高斯分布来建模背景,适用于背景变化较少的场景,而 MOG2 算法通过动态调整高斯分布的数量和参数,能够更好地适应背景的变化,适用于背景变化较多的场景。

特性 MOG MOG2
高斯分布数量 固定 动态调整
背景更新速度 较慢 较快
适应背景变化能力 较弱 较强
计算复杂度 较低 较高
适用场景 背景变化较少的场景 背景变化较多的场景

3.5人脸检测

人脸检测是计算机视觉中的一个经典问题,而 OpenCV 提供了基于 Haar 特征分类器的人脸检测方法,简单易用且效果显著。

本文将详细介绍如何使用 OpenCV 中的 cv2.CascadeClassifier() 进行人脸检测。

Haar 特征分类器简介

Haar 特征分类器是一种基于 Haar-like 特征的机器学习方法,由 Paul Viola 和 Michael Jones 在 2001 年提出。它通过提取图像中的 Haar-like 特征,并使用 AdaBoost 算法进行训练,最终生成一个分类器,用于检测图像中的目标(如人脸)。

Haar-like 特征是一种简单的矩形特征,通过计算图像中不同区域的像素值差异来提取特征。例如,一个 Haar-like 特征可以是两个相邻矩形的像素值之和的差值。这些特征能够捕捉到图像中的边缘、线条等结构信息。

OpenCV 中的 Haar 特征分类器

OpenCV 提供了预训练的 Haar 特征分类器,可以直接用于人脸检测。这些分类器以 XML 文件的形式存储,包含了训练好的模型参数。

OpenCV 中的 cv2.CascadeClassifier() 类用于加载和使用这些分类器。

人脸检测的实现步骤

  1. 加载 Haar 特征分类器模型: 使用 cv2.CascadeClassifier() 加载预训练的人脸检测模型。
  2. 读取图像: 使用 cv2.imread() 读取待检测的图像。
  3. 转换为灰度图: 将图像转换为灰度图,因为 Haar 特征分类器在灰度图上运行更快。
  4. 检测人脸: 使用 detectMultiScale() 方法检测图像中的人脸。
  5. 绘制检测结果: 在图像中绘制检测到的人脸矩形框。
  6. 显示结果: 显示检测结果。

加载 Haar 特征分类器

在使用 Haar 特征分类器之前,首先需要加载预训练的分类器模型。OpenCV 提供了多个预训练的分类器,如用于人脸检测的 haarcascade_frontalface_default.xml

1
2
3
4
import cv2

# 加载 Haar 特征分类器
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

读取图像

在进行人脸检测之前,需要读取待检测的图像。OpenCV 提供了 cv2.imread() 函数来读取图像。

1
2
# 读取图像
image = cv2.imread('image.jpg')

转换为灰度图像

Haar 特征分类器通常在灰度图像上进行检测,因此需要将彩色图像转换为灰度图像。

1
2
# 转换为灰度图像
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

进行人脸检测

使用 cv2.CascadeClassifier.detectMultiScale() 方法进行人脸检测。该方法返回检测到的人脸区域的矩形框(x, y, w, h),其中 (x, y) 是矩形框的左上角坐标,w 和 h 分别是矩形框的宽度和高度。

1
2
# 进行人脸检测
faces = face_cascade.detectMultiScale(gray_image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
  • scaleFactor: 表示每次图像尺寸减小的比例,用于构建图像金字塔。默认值为 1.1。
  • minNeighbors: 表示每个候选矩形框应该保留的邻居数量。默认值为 5。
  • minSize: 表示检测目标的最小尺寸。默认值为 (30, 30)。

绘制检测结果

在检测到人脸后,可以使用 cv2.rectangle() 方法在图像上绘制矩形框,标记出人脸的位置。

1
2
3
# 绘制检测结果
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x+w, y+h), (255, 0, 0), 2)

显示结果

最后,使用 cv2.imshow() 方法显示检测结果。

1
2
3
4
# 显示结果
cv2.imshow('Detected Faces', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

完整代码示例

以下是一个完整的 OpenCV 人脸检测代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cv2

# 加载 Haar 特征分类器
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

# 读取图像
image = cv2.imread('image.jpg')

# 转换为灰度图像
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 进行人脸检测
faces = face_cascade.detectMultiScale(gray_image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

# 绘制检测结果
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x+w, y+h), (255, 0, 0), 2)

# 显示结果
cv2.imshow('Detected Faces', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.6物体识别

本文将详细介绍如何使用 OpenCV 中的模板匹配方法(cv2.matchTemplate())来进行物体识别。

什么是模板匹配

模板匹配是一种在图像中寻找与给定模板图像最相似区域的技术。

简单来说,模板匹配就是在一幅大图像中寻找与模板图像(即我们想要识别的物体)最匹配的部分,这种方法适用于物体在图像中的大小、方向和形状基本不变的情况。

  • 模板图像: 目标物体的图像片段。
  • 搜索图像: 待检测的图像。
  • 匹配结果: 表示模板图像在搜索图像中的相似度分布。

模板匹配的基本原理

模板匹配的基本原理是通过滑动模板图像在目标图像上移动,计算每个位置的相似度,并找到相似度最高的位置。OpenCV 提供了多种相似度计算方法,如平方差匹配(cv2.TM_SQDIFF)、归一化平方差匹配(cv2.TM_SQDIFF_NORMED)、相关匹配(cv2.TM_CCORR)、归一化相关匹配(cv2.TM_CCORR_NORMED)、相关系数匹配(cv2.TM_CCOEFF)和归一化相关系数匹配(cv2.TM_CCOEFF_NORMED)。

应用场景

  • 物体识别: 用于在图像中定位特定物体,如标志、图标等。
  • 目标跟踪: 用于在视频中跟踪目标物体。
  • 图像配准: 用于将两幅图像对齐。

模板匹配的实现步骤

  1. 加载图像: 读取搜索图像和模板图像。
  2. 模板匹配: 使用 cv2.matchTemplate() 在搜索图像中查找模板图像。
  3. 获取匹配结果: 使用 cv2.minMaxLoc() 获取最佳匹配位置。
  4. 绘制匹配结果: 在搜索图像中绘制匹配区域。
  5. 显示结果: 显示匹配结果。

匹配方法

OpenCV 提供了多种模板匹配方法,可以通过 cv2.matchTemplate() 的第三个参数指定:

方法 说明
cv2.TM_SQDIFF 平方差匹配,值越小匹配度越高。
cv2.TM_SQDIFF_NORMED 归一化平方差匹配,值越小匹配度越高。
cv2.TM_CCORR 相关匹配,值越大匹配度越高。
cv2.TM_CCORR_NORMED 归一化相关匹配,值越大匹配度越高。
cv2.TM_CCOEFF 相关系数匹配,值越大匹配度越高。
cv2.TM_CCOEFF_NORMED 归一化相关系数匹配,值越大匹配度越高。

3.6.1使用 cv2.matchTemplate() 进行物体识别

  1. 导入必要的库

首先,我们需要导入 OpenCV 和 NumPy 库。

NumPy 是 Python 中用于科学计算的基础库,OpenCV 使用 NumPy 数组来存储图像数据。

1
2
import cv2
import numpy as np
  1. 加载图像和模板

接下来,我们需要加载目标图像和模板图像。

目标图像是我们要在其中寻找物体的图像,模板图像是我们想要识别的物体。

1
2
3
# 加载目标图像和模板图像
img = cv2.imread('target_image.jpg', 0)
template = cv2.imread('template_image.jpg', 0)
  1. 获取模板图像的尺寸

为了在目标图像中滑动模板图像,我们需要知道模板图像的宽度和高度。

1
2
# 获取模板图像的尺寸
w, h = template.shape[::-1]
  1. 进行模板匹配

使用 cv2.matchTemplate() 函数进行模板匹配。

该函数返回一个结果矩阵,其中每个元素表示目标图像中对应位置与模板图像的相似度。

1
2
# 进行模板匹配
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
  1. 设置匹配阈值并找到匹配位置

我们可以设置一个阈值来确定匹配是否成功。

然后,使用 cv2.minMaxLoc() 函数找到结果矩阵中的最大值和最小值的位置。

1
2
3
4
5
# 设置匹配阈值
threshold = 0.8

# 找到匹配位置
loc = np.where(res >= threshold)
  1. 在目标图像中标记匹配位置

最后,我们可以在目标图像中标记出与模板匹配的位置。

通常,我们使用矩形框来标记匹配区域。

1
2
3
4
5
6
7
8
# 在目标图像中标记匹配位置
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 255, 0), 2)

# 显示结果图像
cv2.imshow('Matched Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

完整代码

以下是完整的代码示例,展示了如何使用 cv2.matchTemplate() 进行物体识别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import cv2
import numpy as np

# 加载目标图像和模板图像
img = cv2.imread('target_image.jpg', 0)
template = cv2.imread('template_image.jpg', 0)

# 获取模板图像的尺寸
w, h = template.shape[::-1]

# 进行模板匹配
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)

# 设置匹配阈值
threshold = 0.8

# 找到匹配位置
loc = np.where(res >= threshold)

# 在目标图像中标记匹配位置
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 255, 0), 2)

# 显示结果图像
cv2.imshow('Matched Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.7图像拼接

图像拼接是计算机视觉中的一个重要应用,它可以将多张有重叠区域的图像拼接成一张更大的图像。

常见的应用场景包括全景图生成、卫星图像拼接等。

OpenCV 是一个强大的计算机视觉库,提供了丰富的工具来实现图像拼接。

本文将详细介绍如何使用 OpenCV 进行图像拼接,重点讲解特征点检测和匹配的技术。

应用场景

  • 全景图生成: 将多幅图像拼接成一幅全景图。
  • 地图拼接: 将多幅地图图像拼接成一幅更大的地图。
  • 医学图像处理: 将多幅医学图像拼接成一幅完整的图像。

图像拼接的基本流程

图像拼接的基本流程可以分为以下几个步骤:

  1. 图像读取:读取需要拼接的图像。
  2. 特征点检测:在每张图像中检测出关键点(特征点)。
  3. 特征点匹配:在不同图像之间匹配这些特征点。
  4. 计算变换矩阵:根据匹配的特征点计算图像之间的变换矩阵。
  5. 图像融合:将图像按照变换矩阵进行拼接,并进行融合处理以消除拼接痕迹。

接下来,我们将详细讲解每个步骤的实现。

  1. 图像读取

首先,我们需要读取需要拼接的图像。OpenCV 提供了 cv2.imread() 函数来读取图像。

1
2
3
4
5
6
7
8
9
10
import cv2

# 读取图像
image1 = cv2.imread('image1.jpg')
image2 = cv2.imread('image2.jpg')

# 检查图像是否成功读取
if image1 is None or image2 is None:
print("Error: 无法读取图像")
exit()
  1. 特征点检测

特征点检测是图像拼接的关键步骤。OpenCV 提供了多种特征点检测算法,如 SIFT、SURF、ORB 等。这里我们以 SIFT 为例进行讲解。

1
2
3
4
5
6
# 创建 SIFT 检测器
sift = cv2.SIFT_create()

# 检测特征点和描述符
keypoints1, descriptors1 = sift.detectAndCompute(image1, None)
keypoints2, descriptors2 = sift.detectAndCompute(image2, None)

detectAndCompute() 函数会返回两个值:关键点(keypoints)和描述符(descriptors)。关键点是图像中的显著点,描述符是对这些关键点的描述,用于后续的匹配。


  1. 特征点匹配

在检测到特征点后,我们需要在不同图像之间匹配这些特征点。OpenCV 提供了 BFMatcherFlannBasedMatcher 来进行特征点匹配。

1
2
3
4
5
6
7
8
9
10
11
# 创建 BFMatcher 对象
bf = cv2.BFMatcher()

# 使用 KNN 匹配
matches = bf.knnMatch(descriptors1, descriptors2, k=2)

# 应用比率测试,筛选出好的匹配
good_matches = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good_matches.append(m)

knnMatch() 函数会返回每个特征点的两个最佳匹配。我们通过比率测试(Lowe’s ratio test)来筛选出好的匹配点。


  1. 计算变换矩阵

在得到好的匹配点后,我们可以使用这些点来计算图像之间的变换矩阵。常用的变换矩阵有单应性矩阵(Homography),它可以将一张图像中的点映射到另一张图像中。

1
2
3
4
5
6
# 提取匹配点的坐标
src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)

# 计算单应性矩阵
H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)

findHomography() 函数会返回一个 3x3 的单应性矩阵 H,它可以将 image1 中的点映射到 image2 中。


  1. 图像融合

最后,我们使用计算出的单应性矩阵将图像进行拼接,并进行融合处理以消除拼接痕迹。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 获取图像尺寸
h1, w1 = image1.shape[:2]
h2, w2 = image2.shape[:2]

# 计算拼接后图像的尺寸
pts = np.float32([[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
dst = cv2.perspectiveTransform(pts, H)
[x_min, y_min] = np.int32(dst.min(axis=0).ravel() - 0.5)
[x_max, y_max] = np.int32(dst.max(axis=0).ravel() + 0.5)

# 计算平移矩阵
translation_matrix = np.array([[1, 0, -x_min], [0, 1, -y_min], [0, 0, 1]])

# 应用平移矩阵进行图像拼接
result = cv2.warpPerspective(image1, translation_matrix.dot(H), (x_max - x_min, y_max - y_min))
result[-y_min:h2 - y_min, -x_min:w2 - x_min] = image2

# 显示拼接结果
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

warpPerspective() 函数会根据单应性矩阵 Himage1 进行透视变换,并将其与 image2 进行拼接。


应用实现

以下是使用特征点检测和匹配进行图像拼接的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import cv2
import numpy as np

# 1. 加载图像
image1 = cv2.imread("path/to/image1.jpg")
image2 = cv2.imread("path/to/image2.jpg")

# 2. 转换为灰度图
gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

# 3. 特征点检测
sift = cv2.SIFT_create()
keypoints1, descriptors1 = sift.detectAndCompute(gray1, None)
keypoints2, descriptors2 = sift.detectAndCompute(gray2, None)

# 4. 特征点匹配
matcher = cv2.BFMatcher()
matches = matcher.knnMatch(descriptors1, descriptors2, k=2)

# 5. 筛选匹配点
good_matches = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good_matches.append(m)

# 6. 计算单应性矩阵
if len(good_matches) > 10:
src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
else:
print("Not enough matches found.")
exit()

# 7. 图像变换
height1, width1 = image1.shape[:2]
height2, width2 = image2.shape[:2]
warped_image = cv2.warpPerspective(image1, H, (width1 + width2, height1))

# 8. 图像拼接
warped_image[0:height2, 0:width2] = image2

# 9. 显示结果
cv2.imshow("Stitched Image", warped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.8简单滤镜效果

以下是主要滤镜效果:

滤镜效果 实现方法
灰度滤镜 cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
怀旧滤镜 通过调整色彩通道的权重,模拟老照片效果。
浮雕滤镜 使用卷积核 [[-2, -1, 0], [-1, 1, 1], [0, 1, 2]] 进行卷积操作。
模糊滤镜 cv2.GaussianBlur(image, (15, 15), 0)
锐化滤镜 使用卷积核 [[0, -1, 0], [-1, 5, -1], [0, -1, 0]] 进行卷积操作。
边缘检测滤镜 cv2.Canny(gray_image, 100, 200)

1、灰度滤镜

灰度滤镜是最简单的滤镜之一,它将彩色图像转换为灰度图像。

灰度图像只有一个通道,每个像素的值表示亮度。

实现步骤

  1. 读取图像。

  2. 使用 cv2.cvtColor() 函数将图像从 BGR 颜色空间转换为灰度颜色空间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import cv2

    # 读取图像
    image = cv2.imread('input.jpg')

    # 转换为灰度图像
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 保存灰度图像
    cv2.imwrite('gray_output.jpg', gray_image)

    # 显示灰度图像
    cv2.imshow('Gray Image', gray_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    代码解析:

    • cv2.imread('input.jpg'):读取图像文件。
    • cv2.cvtColor(image, cv2.COLOR_BGR2GRAY):将图像从 BGR 颜色空间转换为灰度颜色空间。
    • cv2.imwrite('gray_output.jpg', gray_image):保存灰度图像。
    • cv2.imshow('Gray Image', gray_image):显示灰度图像。

    2、怀旧滤镜

    怀旧滤镜通过调整图像的色彩通道,使图像呈现出一种复古的效果。

    通常,怀旧滤镜会增加红色和绿色通道的强度,同时减少蓝色通道的强度。

    实现步骤

    1. 读取图像。
    2. 分离图像的 BGR 通道。
    3. 调整各个通道的强度。
    4. 合并通道,生成怀旧效果的图像。
留言