一、颜色空间
1.1 颜色空间转换
颜色空间是一种用于表示颜色的数学模型。不同的颜色空间提供了不同的方式来表示颜色,并且在不同的应用中有不同的用途。以下是一些常见的颜色空间:
- RGB 颜色空间是最常见的颜色表示方式,它使用红色(R)、绿色(G)和蓝色(B)三个通道来表示颜色。每个通道的取值范围通常是0到255,其中0表示最小强度(没有颜色),255表示最大强度
- BGR 颜色空间与 RGB 类似,只是通道的顺序不同。在 OpenCV 中,读取图像时通常使用 BGR 格式
- HSV 颜色空间将颜色表示为色相(H)、饱和度(S)和亮度(V)。色相表示颜色的类型,饱和度表示颜色的纯度,而亮度表示颜色的亮暗程度。HSV 颜色空间在图像处理中常用于颜色的分割和对象跟踪
- LAB 颜色空间是一种与人眼感知更为接近的颜色空间。它包含亮度(L)通道和两个色度通道 a 和 b。在 LAB 颜色空间中,可以更容易地计算颜色之间的感知差异
- 灰度图是一种特殊的颜色空间,它是一种单通道的图像,其中每个像素的值表示图像中对应位置的灰度强度。在灰度图中,像素的取值范围通常是0到255,其中0表示黑色(最暗),255表示白色(最亮),而中间的值表示不同的灰度级别
可以使用 OpenCV 库中的函数cv2.cvtColor()来进行颜色空间的转换
import cv2
# 读取彩色图像
image = cv2.imread('cat.jpg')
# 指定缩放比例
scale_percent = 20 # 缩放比例为50%,可以根据需要修改
# 计算新的图像尺寸
width = int(image.shape[1] * scale_percent / 100)
height = int(image.shape[0] * scale_percent / 100)
# 缩放图像
resized_image = cv2.resize(image, (width, height))
# 将彩色图像转换为灰度图像
gray_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2GRAY)
# 将彩色图像转换为 HSV
hsv_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2HSV)
# 将彩色图像转换为 RGB
rgb_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2RGB)
# 显示灰度图像
cv2.imshow('Gray Image', gray_image)
# 显示 HSV 图像
cv2.imshow('HSV Image', hsv_image)
# 显示 RGB 图像
cv2.imshow('RGB Image', rgb_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.2 物体跟踪
使用 HSV(色相、饱和度、亮度)颜色空间来检测和追踪特定颜色的物体。这种方法在特定场景下非常有效,尤其适用于追踪具有明显颜色差异的物体。图片跟踪是指在单张静态图片中检测和追踪特定物体。在 OpenCV 中,我们可以使用 HSV 方法来实现图片跟踪
import cv2
import numpy as np
def track_object_hsv(image_path, lower_hsv, upper_hsv):
# 读取图像
image = cv2.imread(image_path)
# 将图像转换到 HSV 颜色空间
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 创建 HSV 颜色阈值范围
lower_color = np.array(lower_hsv, np.uint8)
upper_color = np.array(upper_hsv, np.uint8)
# 根据 HSV 阈值范围,提取物体的掩码
mask = cv2.inRange(hsv_image, lower_color, upper_color)
# 对掩码进行形态学操作以去除噪声
kernel = np.ones((5, 5), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# 查找物体的轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
for contour in contours:
if cv2.contourArea(contour) > 20000: # 根据需要调整阈值
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('Object Tracking', image)
cv2.imwrite('object_tracking.jpg', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'strawberry.jpg' # 替换为你的图片路径
# 设置要追踪的颜色范围(在 HSV 颜色空间中)
lower_hsv = [0, 50, 50] # 低阈值
upper_hsv = [10, 255, 255] # 高阈值
track_object_hsv(image_path, lower_hsv, upper_hsv)
对于视频中的HSV跟踪,我们可以使用类似图片的HSV方法,获取图片的每一帧上,对每一帧进行HSV跟踪,提取想要跟踪物体的掩码,查找轮廓并绘制在原始帧上
二、几何变换
2.1 缩放
缩放功能我们在之前以及用过很多次了,可以通过cv2.resize()函数进行缩放
import cv2
# 读取图像
image = cv2.imread('input_image.jpg')
# 定义缩放比例
scale_percent = 50 # 缩放比例为50%,可以根据需要修改
# 计算新的图像尺寸
width = int(image.shape[1] * scale_percent / 100)
height = int(image.shape[0] * scale_percent / 100)
# 缩放图像
resized_image = cv2.resize(image, (width, height))
# 显示缩放后的图像
cv2.imshow('Resized Image', resized_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.2 平移
使用cv2.warpAffine()函数来实现平移。平移是指将图像在水平和垂直方向上按照给定的偏移量进行移动
import cv2
import numpy as np
# 读取图像
image = cv2.imread('cat.jpg')
# 定义平移矩阵
tx, ty = 50, 100 # 水平和垂直方向的平移像素数
M = np.array([[1, 0, tx], [0, 1, ty]], dtype=np.float32)
# 应用平移变换
translated_image = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
# 显示平移后的图像
cv2.imshow('Translated Image', translated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.3 旋转
使用cv2.getRotationMatrix2D()函数计算旋转矩阵,并使用cv2.warpAffine()函数来实现旋转
import cv2
# 读取图像
image = cv2.imread('cat.jpg')
# 定义旋转中心和角度
center = (image.shape[1] // 2, image.shape[0] // 2)
angle = 45 # 旋转角度(逆时针为正)
# 计算旋转矩阵
M = cv2.getRotationMatrix2D(center, angle, 1.0)
# 应用旋转变换
rotated_image = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
# 显示旋转后的图像
cv2.imshow('Rotated Image', rotated_image)
cv2.imwrite('rotated_image.jpg', rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.4 仿射变换
仿射变换是一种线性变换,它可以保持直线在图像中的直线性和平行性。仿射变换需要至少三个点的对应关系来计算仿射变换矩阵。可以使用cv2.getAffineTransform()函数计算仿射变换矩阵,并使用cv2.warpAffine()函数应用该变换
import cv2
import numpy as np
# 读取图像
image = cv2.imread('cat.jpg')
# 定义原图像上的三个点和目标图像上对应的三个点
original_points = np.float32([[50, 50], [200, 50], [50, 200]])
target_points = np.float32([[30, 100], [250, 50], [100, 250]])
# 计算仿射变换矩阵
M = cv2.getAffineTransform(original_points, target_points)
# 应用仿射变换
affine_transformed_image = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
# 显示仿射变换后的图像
cv2.imshow('Affine Transformed Image', affine_transformed_image)
cv2.imwrite('affine_transformed_image.jpg', affine_transformed_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.5 透视变换
透视变换(Perspective Transformation)是一种几何变换,它可以改变图像中的透视关系,从而实现对图像的投影和变换。透视变换通常用于校正图像的透视畸变或者将图像投影到一个新的视角上。在 OpenCV 中,可以使用cv2.getPerspectiveTransform()函数计算透视变换矩阵,并使用cv2.warpPerspective()函数应用透视变换
import cv2
import numpy as np
def perspective_transform(image_path):
# 读取图像
image = cv2.imread(image_path)
# 定义原图像上的四个点和目标图像上对应的四个点
original_points = np.float32([[200, 100], [450, 100], [50, 300], [600, 300]])
target_points = np.float32([[0, 0], [300, 0], [0, 400], [300, 400]])
# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(original_points, target_points)
# 应用透视变换
perspective_transformed_image = cv2.warpPerspective(image, M, (300, 400))
# 显示透视变换后的图像
cv2.imshow('Perspective Transformed Image', perspective_transformed_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cat.jpg' # 替换为你的图片路径
perspective_transform(image_path)
三、图像阈值
图像阈值处理是一种常用的图像处理技术,用于将图像转换为二值图像(只包含两种像素值:黑色和白色)。阈值处理的基本思想是将图像中的像素值与一个预先定义好的阈值进行比较,然后根据比较结果将像素设置为黑色或白色。这种方法在图像分割、边缘检测等应用中非常有用
3.1 简单阈值
简单阈值是图像阈值处理中的一种基本方法,它将图像中的每个像素与预先定义的阈值进行比较,并根据比较结果将像素设置为黑色或白色。简单阈值处理可以分为两类:
- 二进制阈值(Binary Thresholding):将像素值大于阈值的像素设为一个特定的值(通常为最大像素值),而像素值小于等于阈值的像素设为另一个特定的值(通常为0)。
- 反二进制阈值(Inverse Binary Thresholding):与二进制阈值相反,将像素值大于阈值的像素设为0,而像素值小于等于阈值的像素设为最大像素值
可以使用cv2.threshold()函数来实现简单阈值处理。该函数接受输入图像、阈值、最大像素值和阈值类型作为参数,并返回阈值化后的图像
import cv2
def binary_threshold(image_path, threshold_value):
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 进行二进制阈值处理
_, binary_thresholded_image = cv2.threshold(image, threshold_value, 255, cv2.THRESH_BINARY)
# 显示原始图像和阈值化后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Binary Thresholded Image', binary_thresholded_image)
cv2.imwrite('binary_thresholded_image.jpg', binary_thresholded_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
def inverse_binary_threshold(image_path, threshold_value):
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 进行反二进制阈值处理
_, inverse_binary_thresholded_image = cv2.threshold(image, threshold_value, 255, cv2.THRESH_BINARY_INV)
# 显示原始图像和阈值化后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Inverse Binary Thresholded Image', inverse_binary_thresholded_image)
cv2.imwrite('inverse_binary_thresholded_image.jpg', inverse_binary_thresholded_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cat.jpg' # 替换为你的图片路径
threshold_value = 128 # 阈值
binary_threshold(image_path, threshold_value)
inverse_binary_threshold(image_path, threshold_value)
3.2 自适应阈值
简单阈值是一种全局阈值,这种方法并不适用于所有情况,自适应阈值处理是一种在图像局部区域内自动计算阈值的方法,它可以解决图像中光照不均匀或者背景复杂的情况下的阈值处理问题。自适应阈值处理会根据图像的局部区域计算不同的阈值,并将每个像素与其所在区域的阈值进行比较。这样可以更好地适应图像中的不同区域,提高阈值处理的准确性。
在 OpenCV 中,可以使用cv2.adaptiveThreshold()函数进行自适应阈值处理。该函数接受输入图像、最大像素值、自适应阈值计算方法、阈值类型、邻域大小和常数C等参数,并返回自适应阈值化后的图像
import cv2
def adaptive_threshold(image_path, max_value, adaptive_method, threshold_type, block_size, constant):
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 进行自适应阈值处理
adaptive_thresholded_image = cv2.adaptiveThreshold(image, max_value, adaptive_method, threshold_type, block_size, constant)
# 显示原始图像和自适应阈值化后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Adaptive Thresholded Image', adaptive_thresholded_image)
cv2.imwrite('Adaptive Thresholded Image.jpg', adaptive_thresholded_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cat.jpg' # 替换为你的图片路径
max_value = 255 # 最大像素值
adaptive_method = cv2.ADAPTIVE_THRESH_MEAN_C # 自适应阈值计算方法,使用均值
threshold_type = cv2.THRESH_BINARY # 阈值类型,这里使用二进制化
block_size = 11 # 邻域大小,必须是奇数
constant = 2 # 常数C
adaptive_threshold(image_path, max_value, adaptive_method, threshold_type, block_size, constant)
四、图像模糊
图像模糊(Image Blurring)是一种图像处理技术,用于减少图像中的噪声和细节,从而使图像变得更加平滑。模糊操作可以在图像上应用一个滤波器,将每个像素的值替换为其周围像素的加权平均值。这样可以减少图像中的高频细节,从而达到模糊的效果
4.1 平均模糊
平均模糊(Average Blurring)是一种图像模糊方法,它会用一个指定大小的卷积核对图像进行滤波,计算每个像素周围邻域像素的平均值,然后将该平均值作为新的像素值。这样可以减少图像中的噪声和细节,从而使图像变得更加平滑
4.2 高斯模糊
高斯模糊(Gaussian Blurring)是一种常用的图像模糊方法,它使用高斯核对图像进行滤波,通过计算每个像素周围邻域像素的加权平均值来实现模糊效果。高斯模糊对图像进行平滑处理,减少噪声,并保留更多的图像细节
4.3 中值模糊
中值模糊(Median Blurring)是一种非常常用的图像模糊方法,它使用一个指定大小的滑动窗口覆盖图像,然后将窗口内像素的中值作为中心像素的值,从而实现模糊效果。中值模糊可以有效地去除图像中的椒盐噪声或者斑点噪声,而不会对图像的边缘和细节进行模糊处理
import cv2
def image_blur(image_path, kernel_size):
# 读取图像
image = cv2.imread(image_path)
# 进行平均模糊
average_blur = cv2.blur(image, (kernel_size, kernel_size))
# 进行高斯模糊
gaussian_blur = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
# 进行中值模糊
median_blur = cv2.medianBlur(image, kernel_size)
# 显示原始图像和模糊后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Average Blurred Image', average_blur)
cv2.imshow('Gaussian Blurred Image', gaussian_blur)
cv2.imshow('Median Blurred Image', median_blur)
cv2.imwrite('Average Blurred Image.jpg', average_blur)
cv2.imwrite('Gaussian Blurred Image.jpg', gaussian_blur)
cv2.imwrite('Median Blurred Image.jpg', median_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cat.jpg' # 替换为你的图片路径
kernel_size = 5 # 卷积核大小,必须是奇数
image_blur(image_path, kernel_size)
五、掩码
5.1 位操作
位操作(Bitwise Operations)是计算机图像处理中常用的操作,它允许在像素级别上对图像进行一系列位级别的操作,如按位与、按位或、按位异或和按位取反等。位操作通常用于图像掩码、图像融合、图像分割和图像特征提取等任务
- 按位与(Bitwise AND):使用cv2.bitwise_and()函数对两幅图像或图像与掩码进行按位与操作,将对应位置上的像素值相与
- 按位或(Bitwise OR):使用cv2.bitwise_or()函数对两幅图像或图像与掩码进行按位或操作,将对应位置上的像素值相或
- 按位异或(Bitwise XOR):使用cv2.bitwise_xor()函数对两幅图像或图像与掩码进行按位异或操作,将对应位置上的像素值进行异或
- 按位取反(Bitwise NOT):使用cv2.bitwise_not()函数对图像进行按位取反操作,将图像中的每个像素值取反
5.2 掩码
在图像处理中,掩码(Mask)是一个二值图像,用于指示哪些像素需要进行特定的操作,哪些像素应该被忽略。掩码可以理解为一个过滤器,它只保留感兴趣区域(ROI,Region of Interest)的部分图像,而将其他区域置为黑色或忽略掉
import cv2
import numpy as np
def apply_mask(image_path, mask_path):
# 读取原始图像和掩码
image = cv2.imread(image_path)
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
# 对原始图像应用掩码
masked_image = cv2.bitwise_and(image, image, mask=mask)
# 显示原始图像、掩码和应用掩码后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Mask', mask)
cv2.imshow('Masked Image', masked_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'path/to/your/image.jpg' # 替换为你的图片路径
mask_path = 'path/to/your/mask.jpg' # 替换为你的掩码图片路径
apply_mask(image_path, mask_path)
六、直方图
直方图是一种用于可视化图像像素值分布的图形表示方法。它显示了图像中每个像素值的数量或占比,通常用于了解图像的亮度分布、对比度、颜色分布等信息,在图像直方图中,横坐标表示像素值,纵坐标表示该像素值在图像中出现的频次(数量)或占比。直方图可以帮助我们了解图像的整体亮度情况,以及各个亮度级别的像素数量或占比,从而有助于进行图像的分析和处理
6.1 绘制直方图
在 OpenCV 中,可以使用cv2.calcHist()函数来计算图像的直方图。该函数接受输入图像和一些参数,返回表示直方图的数组。然后,你可以使用 Matplotlib 等库将直方图绘制出来
import cv2
import matplotlib.pyplot as plt
def plot_histogram(image_path):
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 计算直方图
histogram = cv2.calcHist([image], [0], None, [256], [0, 256])
# 绘制直方图
plt.plot(histogram, color='black')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.title('Image Histogram')
plt.show()
# 使用示例
if __name__ == "__main__":
image_path = 'cat.jpg' # 替换为你的图片路径
plot_histogram(image_path)
6.2 直方图均衡化
直方图均衡化(Histogram Equalization)是一种图像增强技术,用于增强图像的对比度和亮度分布。它通过重新分配图像的像素值,使得图像的直方图在整个像素值范围内均匀分布,从而增加了图像的视觉效果和细节
在 OpenCV 中,可以使用cv2.equalizeHist()函数来进行直方图均衡化。该函数接受输入灰度图像,并返回均衡化后的图像
import cv2
import matplotlib.pyplot as plt
def histogram_equalization(image_path):
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 进行直方图均衡化
equalized_image = cv2.equalizeHist(image)
histogram = cv2.calcHist([equalized_image], [0], None, [256], [0, 256])
# 绘制直方图
plt.plot(histogram, color='black')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.title('Image Histogram')
plt.show()
# 绘制原始图像和均衡化后的图像
plt.subplot(1, 2, 1)
plt.imshow(image, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(equalized_image, cmap='gray')
plt.title('Equalized Image')
plt.axis('off')
plt.show()
# 使用示例
if __name__ == "__main__":
image_path = 'cat.jpg' # 替换为你的图片路径
histogram_equalization(image_path)
其中用到了subplot函数,帮助我们将多幅图像绘制在一个框中,plt.subplot() 函数接受三个参数:plt.subplot(rows, columns, index),分别表示子图的行数、列数和当前子图的索引。
七、形态学操作
形态学操作是数字图像处理中一类常用的图像处理技术,它主要用于对二值图像(灰度图像也可以)进行图像增强、图像分割和图像去噪等操作。形态学操作基于结构元素(Kernel)和集合论中的形态学原理,通过滑动结构元素在图像上的位置,对图像进行像素级别的运算
7.1 腐蚀
腐蚀运算(Erosion)是形态学操作中的一种,用于缩小图像中的前景区域(白色区域)或去除前景噪声。腐蚀操作通过在结构元素覆盖的区域内查找像素值全为1的区域,如果找到则将中心像素置为1,否则置为0。这样就能将前景区域逐渐缩小,去除细小的前景区域和噪声。
可以根据实际需求调整结构元素的大小 kernel_size,从而改变腐蚀运算的程度。较大的kernel_size 将导致更多的腐蚀,进一步缩小前景区域,而较小的 kernel_size 则保留更多的前景细节
7.2 膨胀
膨胀与腐蚀相反,用于扩大图像中的前景区域或填充前景区域中的小洞。膨胀操作通过在结构元素覆盖的区域内查找像素值为1的区域,如果找到则将中心像素置为1,否则置为0。这样就能将前景区域逐渐扩大,填充前景区域中的小洞。
import cv2
import numpy as np
import matplotlib.pyplot as plt
def morphological_operations(image_path):
# 读取灰度图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 定义结构元素(20*20的正方形)
kernel = np.ones((20, 20), np.uint8)
# 腐蚀操作
erosion_image = cv2.erode(image, kernel, iterations=1)
# 膨胀操作
dilation_image = cv2.dilate(image, kernel, iterations=1)
# 绘制原始图像
plt.subplot(3, 1, 1)
plt.imshow(image, cmap='gray')
plt.title('original_image')
plt.axis('off')
# 绘制原始图像和腐蚀后的图像
plt.subplot(3, 1, 2)
plt.imshow(erosion_image, cmap='gray')
plt.title('erosion_image')
plt.axis('off')
# 绘制原始图像和膨胀后的图像
plt.subplot(3, 1, 3)
plt.imshow(dilation_image, cmap='gray')
plt.title('dilation_image')
plt.axis('off')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cv.png' # 替换为你的图片路径
morphological_operations(image_path)
7.3 开运算
先进行腐蚀运算,再进行膨胀运算。开运算通常用于去除图像中的小白噪声或细小的前景区域,使用cv2.morphologyEx()函数,并指定 cv2.MORPH_OPEN 参数来进行开运算
import cv2
import numpy as np
import matplotlib.pyplot as plt
def opening(image_path, kernel_size):
# 读取灰度图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 定义结构元素
kernel = np.ones((kernel_size, kernel_size), np.uint8)
# 开运算
opened_image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
# 绘制原始图像
plt.subplot(1, 2, 1)
plt.imshow(image, cmap='gray')
plt.title('original_image')
plt.axis('off')
# 绘制原始图像和腐蚀后的图像
plt.subplot(1, 2, 2)
plt.imshow(opened_image, cmap='gray')
plt.title('opened_image')
plt.axis('off')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'kai.png' # 替换为你的图片路径
kernel_size = 15 # 定义结构元素的大小,必须是奇数
opening(image_path, kernel_size)
7.4 闭运算
先进行膨胀运算,再进行腐蚀运算。闭运算通常用于填充图像中的小黑洞或连接断开的前景区域,用cv2.morphologyEx()函数,并指定 cv2.MORPH_CLOSE 参数来进行闭运算
# 闭运算
closed_image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
7.5 形态学梯度
形态学梯度(Morphological Gradient)是形态学操作中的一种,它用于检测图像中物体的边缘或轮廓。形态学梯度计算的结果是图像中物体的边界信息。 形态学梯度可以通过膨胀和腐蚀两种形态学操作的组合来实现。具体做法是,首先对图像进行膨胀操作,然后对膨胀后的图像进行腐蚀操作,最后将两幅图像相减,得到形态学梯度图像。
import cv2
import numpy as np
import matplotlib.pyplot as plt
def morphological_gradient(image_path, kernel_size):
# 读取灰度图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 定义结构元素
kernel = np.ones((kernel_size, kernel_size), np.uint8)
# 膨胀操作
dilation_image = cv2.dilate(image, kernel, iterations=1)
# 腐蚀操作
erosion_image = cv2.erode(image, kernel, iterations=1)
# 形态学梯度
gradient_image = dilation_image - erosion_image
# 绘制原始图像
plt.subplot(2, 1, 1)
plt.imshow(image, cmap='gray')
plt.title('original_image')
plt.axis('off')
# 绘制原始图像和腐蚀后的图像
plt.subplot(2, 1, 2)
plt.imshow(gradient_image, cmap='gray')
plt.title('gradient_image')
plt.axis('off')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cv.png' # 替换为你的图片路径
kernel_size = 3 # 定义结构元素的大小,必须是奇数
morphological_gradient(image_path, kernel_size)
八、边界检测
边界检测是图像处理中的一种常用技术,用于检测图像中物体的边界或轮廓。边界检测的目标是找到图像中物体和背景之间的边界或分界线,以便进行物体分割、目标识别等应用
8.1 canny检测
Canny 边缘检测是一种广泛使用的边缘检测方法,它使用多步骤的算法来找到图像中的边缘。Canny 边缘检测具有高灵敏度和低错误率的优点,因此在实际应用中被广泛采用,可以使用cv2.Canny()函数来进行 Canny 边缘检测
8.2 sobel算子
Sobel 算子是一种常用的梯度算子,它通过卷积运算来计算图像中每个像素的梯度。Sobel 算子可以分别计算图像在水平和垂直方向上的梯度,并结合这两个方向上的梯度来得到图像的总体梯度
import cv2
import numpy as np
import matplotlib.pyplot as plt
def edge_detection(image_path):
# 读取灰度图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 使用 Canny 边缘检测
edges_canny = cv2.Canny(image, 100, 200)
# 使用 Sobel 算子边缘检测
sobelx = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
edges_sobel = np.uint8(np.sqrt(sobelx**2 + sobely**2))
# 绘制原始图像
plt.subplot(4, 1, 1)
plt.imshow(image, cmap='gray')
plt.title('original_image')
plt.axis('off')
# 绘制canny边缘检测后的图像
plt.subplot(4, 1, 2)
plt.imshow(edges_canny, cmap='gray')
plt.title('edges_canny')
plt.axis('off')
# 绘制sobelx边缘检测后的图像
plt.subplot(4, 1, 3)
plt.imshow(sobelx, cmap='gray')
plt.title('sobelx')
plt.axis('off')
# 绘制sobely边缘检测后的图像
plt.subplot(4, 1, 4)
plt.imshow(sobely, cmap='gray')
plt.title('sobely')
plt.axis('off')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cv.png' # 替换为你的图片路径
edge_detection(image_path)
九、轮廓检测
9.1 轮廓基础
轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用,跟轮廓相关的函数有:
-
轮廓检测函数:用于在图像中查找轮廓。
cv2.findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
参数:image
:输入图像(必须为二值图像,通常进行阈值分割后得到)。mode
:轮廓检测模式,指定轮廓的检测模式。常用模式有:cv2.RETR_EXTERNAL
:只检测外部轮廓。cv2.RETR_LIST
:检测所有轮廓,但不建立层级关系。cv2.RETR_TREE
:检测所有轮廓,并建立层级关系。
method
:轮廓逼近方法,指定轮廓的逼近方法。常用方法有:cv2.CHAIN_APPROX_NONE
:存储所有的轮廓点。cv2.CHAIN_APPROX_SIMPLE
:压缩水平、垂直和对角线段,只保留端点。
contours
:检测到的轮廓,返回一个列表,每个轮廓是一个数组。hierarchy
:层级信息,返回每个轮廓的层级信息。offset
:可选参数,表示轮廓点的偏移量。
-
轮廓绘制函数:用于在图像上绘制检测到的轮廓。
cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
参数:image
:输入图像。contours
:轮廓列表。contourIdx
:指定绘制哪个轮廓,当为负数时表示绘制所有轮廓。color
:轮廓的颜色,可以是一个三元组表示颜色(B, G, R)。thickness
:轮廓线的宽度,为负数时表示填充轮廓。lineType
:线条类型,默认为 8 连接线。hierarchy
:层级信息,用于选择绘制特定层级的轮廓。maxLevel
:绘制轮廓的最大层级。offset
:可选参数,表示轮廓点的偏移量。
-
轮廓特征函数:用于计算轮廓的特征,例如面积、周长、重心等。
cv2.contourArea(contour)
:计算轮廓的面积。cv2.arcLength(contour, closed)
:计算轮廓的周长。cv2.moments(contour)
:计算轮廓的矩和中心矩,可以用来计算重心。- 其他轮廓特征函数,如
cv2.boundingRect()
、cv2.minAreaRect()
等
import cv2
import matplotlib.pyplot as plt
def contour_detection(image_path):
# 读取灰度图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 二值化处理(使用阈值分割,也可以使用其他图像分割方法)
_, binary_image = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, hierarchy = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
color_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
cv2.drawContours(color_image, contours, -1, (0, 255, 0), 2)
# 绘制原图像
plt.subplot(1, 2, 1)
plt.imshow(binary_image, cmap='gray')
plt.title('binary_image')
plt.axis('off')
# 绘制轮廓
plt.subplot(1, 2, 2)
plt.imshow(color_image, cmap='gray')
plt.title('color_image')
plt.axis('off')
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'opencv.jpg' # 替换为你的图片路径
contour_detection(image_path)
9.2 轮廓特征
-
cv2.contourArea(contour)
: 计算轮廓的面积。contour
: 输入的单个轮廓。
-
cv2.arcLength(contour, closed)
: 计算轮廓的周长。contour
: 输入的单个轮廓。closed
: 布尔值,指示轮廓是否闭合。如果闭合为 True,否则为 False。
-
cv2.moments(contour)
: 计算轮廓的矩和中心矩,可用于计算重心。contour
: 输入的单个轮廓。
-
cv2.boundingRect(contour)
: 计算轮廓的外接矩形。contour
: 输入的单个轮廓。
-
cv2.minAreaRect(contour)
: 计算轮廓的最小外接矩形。contour
: 输入的单个轮廓。
-
cv2.fitEllipse(contour)
: 计算轮廓的最小外接椭圆。contour
: 输入的单个轮廓。
-
cv2.convexHull(contour)
: 计算轮廓的凸包。contour
: 输入的单个轮廓。
-
cv2.isContourConvex(contour)
: 检查轮廓是否为凸性的。contour
: 输入的单个轮廓。
-
cv2.pointPolygonTest(contour, point, measureDist)
: 测试一个点是否在轮廓内部。contour
: 输入的单个轮廓。point
: 要测试的点的坐标。measureDist
: 是否返回点到轮廓的距离。
这些函数可以用于获取轮廓的基本信息、几何特征以及外接形状等,帮助进行轮廓的进一步分析和处理。在使用这些函数时,需要将轮廓信息正确传递给函数,通常通过轮廓检测函数 cv2.findContours()
得到轮廓列表,然后遍历每个轮廓进行性质计算
9.3 轮廓的层级关系
在 OpenCV 中,cv2.findContours()
函数会返回轮廓的层次信息,以便进行轮廓的组织和分析。在函数返回的层次信息中,每个轮廓对应一个四元组 (next, prev, first_child, parent)
,它们的含义如下:
next
:同一层级下的下一个轮廓的索引。prev
:同一层级下的前一个轮廓的索引。first_child
:当前轮廓的第一个子轮廓的索引。parent
:当前轮廓的父轮廓的索引。
import cv2
import numpy as np
def contour_hierarchy(image_path):
# 读取灰度图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 二值化处理(使用阈值分割,也可以使用其他图像分割方法)
_, binary_image = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY)
# 查找轮廓及层次信息
contours, hierarchy = cv2.findContours(binary_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓及层次信息
color_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
cv2.drawContours(color_image, contours, -1, (0, 255, 0), 2)
cv2.imshow('Contours with Hierarchy', color_image)
print("Hierarchy: \n", hierarchy)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
image_path = 'cv.png' # 替换为你的图片路径
contour_hierarchy(image_path)
Hierarchy:
[[[ 1 -1 -1 -1]
[ 3 0 2 -1]
[-1 -1 -1 1]
[ 5 1 4 -1]
[-1 -1 -1 3]
[ 6 3 -1 -1]
[ 7 5 -1 -1]
[-1 6 8 -1]
[-1 -1 -1 7]]]