Here’s a comprehensive guide on image processing with Python OpenCV:
使用 Python OpenCV 进行图像处理:全面指南
OpenCV (Open Source Computer Vision Library) 是一个功能强大的开源计算机视觉和机器学习软件库。它广泛应用于图像和视频分析、人脸识别、对象检测、姿态估计、运动跟踪、机器人等领域。结合 Python,OpenCV 提供了一个易于使用且高效的图像处理环境。
本指南将详细介绍如何使用 Python OpenCV 进行图像处理,涵盖从基础操作到一些高级应用。
1. 环境搭建
首先,你需要安装 Python 和 OpenCV 库。
- 安装 Python: 访问 python.org 下载并安装最新版本的 Python。
-
安装 OpenCV: 使用 pip 安装
opencv-python。bash
pip install opencv-python numpy matplotlibnumpy是 OpenCV 图像处理的基础,因为图像在 OpenCV 中表示为 NumPy 数组。matplotlib可用于图像显示。
2. 图像的读取、显示与保存
读取图像: 使用 cv2.imread() 函数。
“`python
import cv2
import matplotlib.pyplot as plt
读取图像。第二个参数可选:
cv2.IMREAD_COLOR (默认): 彩色图像,忽略透明度
cv2.IMREAD_GRAYSCALE: 灰度图像
cv2.IMREAD_UNCHANGED: 包含 alpha 通道的原始图像
image_path = ‘path/to/your/image.jpg’ # 替换为你的图片路径
img = cv2.imread(image_path)
if img is None:
print(f”Error: Could not open or find the image at {image_path}”)
else:
print(f”Image loaded successfully. Shape: {img.shape}, Data type: {img.dtype}”)
# img.shape 返回 (高度, 宽度, 通道数)
# img.dtype 返回图像数据类型,通常为 uint8
“`
显示图像: 使用 cv2.imshow() 和 cv2.waitKey()。cv2.imshow() 会弹出一个窗口显示图像,但需要 cv2.waitKey() 来保持窗口打开并处理键盘事件。
“`python
显示图像
cv2.imshow(‘Original Image’, img)
等待按键,0 表示无限等待,否则等待指定毫秒数
cv2.waitKey(0)
销毁所有 OpenCV 窗口
cv2.destroyAllWindows()
“`
使用 Matplotlib 显示: OpenCV 默认使用 BGR 格式,而 Matplotlib 使用 RGB。因此,显示彩色图像时需要转换通道顺序。
“`python
将 BGR 转换为 RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.title(‘Image with Matplotlib’)
plt.axis(‘off’) # 不显示坐标轴
plt.show()
“`
保存图像: 使用 cv2.imwrite()。文件扩展名决定了保存的格式。
python
cv2.imwrite('new_image.png', img)
print("Image saved as new_image.png")
3. 图像的基本属性与操作
图像属性:
img.shape: (高度, 宽度, 通道数) 或 (高度, 宽度) 对于灰度图。img.size: 像素总数 (高度 * 宽度 * 通道数)。img.dtype: 图像的数据类型 (例如uint8)。
像素操作:
“`python
获取像素值 (BGR 格式)
对于彩色图像:(行, 列) 或 (y, x)
pixel = img[100, 100] # 获取 (100, 100) 处的像素值 (BGR)
print(f”Pixel at (100, 100): {pixel}”)
修改像素值
img[100, 100] = [255, 0, 0] # 将像素设为蓝色 (BGR)
注意:直接修改大区域像素效率不高,通常使用 NumPy 切片操作
“`
图像区域 (ROI – Region of Interest):
“`python
裁剪图像:[起始行:结束行, 起始列:结束列]
roi = img[50:150, 120:220] # 裁剪从 (120, 50) 到 (220, 150) 的区域
cv2.imshow(‘ROI’, roi)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
通道分离与合并:
“`python
分离 B, G, R 通道
b, g, r = cv2.split(img)
合并通道
merged_img = cv2.merge([b, g, r])
也可以直接使用 NumPy 索引
blue_channel = img[:, :, 0]
green_channel = img[:, :, 1]
red_channel = img[:, :, 2]
“`
4. 图像的几何变换
缩放 (Scaling): 使用 cv2.resize()。
“`python
缩小到原来的一半
smaller_img = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
放大到原来的两倍
larger_img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
指定输出尺寸 (宽度, 高度)
desired_width = 300
desired_height = 200
resized_img = cv2.resize(img, (desired_width, desired_height), interpolation=cv2.INTER_LINEAR)
cv2.imshow(‘Smaller’, smaller_img)
cv2.imshow(‘Larger’, larger_img)
cv2.imshow(‘Fixed Size’, resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
interpolation参数:cv2.INTER_AREA: 缩小图像时效果最好。cv2.INTER_CUBIC: 放大图像时效果最好 (较慢)。cv2.INTER_LINEAR: 默认,放大和缩小都适用 (速度快)。
平移 (Translation): 需要构建一个 2×3 的平移矩阵 M。
“`python
import numpy as np
rows, cols, _ = img.shape
M = [[1, 0, tx], [0, 1, ty]]
M_translation = np.float32([[1, 0, 100], [0, 1, 50]]) # 向右平移 100 像素,向下平移 50 像素
translated_img = cv2.warpAffine(img, M_translation, (cols, rows))
cv2.imshow(‘Translated’, translated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
旋转 (Rotation): 使用 cv2.getRotationMatrix2D() 获取旋转矩阵,再用 cv2.warpAffine()。
“`python
center: 旋转中心
angle: 旋转角度 (逆时针为正)
scale: 缩放因子
M_rotation = cv2.getRotationMatrix2D((cols/2, rows/2), 45, 1) # 围绕中心旋转 45 度,不缩放
rotated_img = cv2.warpAffine(img, M_rotation, (cols, rows))
cv2.imshow(‘Rotated’, rotated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
5. 图像的算术运算与逻辑运算
加法:
cv2.add() 用于像素值相加,并进行饱和操作 (超过 255 的值会截断为 255)。
直接使用 NumPy 的 + 操作会进行模运算 (250 + 10 = 4)。
“`python
img1 = cv2.imread(‘image1.jpg’)
img2 = cv2.imread(‘image2.jpg’)
确保两张图片大小相同
if img1.shape == img2.shape:
added_img_cv = cv2.add(img1, img2) # 饱和加法
added_img_np = img1 + img2 # 模加法
cv2.imshow(‘CV Add’, added_img_cv)
cv2.imshow(‘NumPy Add’, added_img_np)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(“Images must have the same size for addition.”)
“`
加权和 (图像混合): cv2.addWeighted() 用于按权重混合两张图像。
“`python
dst = alpha * img1 + beta * img2 + gamma
blended_img = cv2.addWeighted(img1, 0.7, img2, 0.3, 0) # 70% img1, 30% img2
cv2.imshow(‘Blended Image’, blended_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
位运算 (AND, OR, NOT, XOR): 用于创建遮罩、提取特定区域等。
“`python
创建一个黑色图像作为背景
black_img = np.zeros((rows, cols, 3), np.uint8)
在黑色图像上画一个白色矩形 (作为遮罩)
mask = cv2.rectangle(black_img.copy(), (100, 100), (400, 300), (255, 255, 255), -1)
对原始图像应用遮罩 (按位与操作)
masked_img = cv2.bitwise_and(img, img, mask=mask[:,:,0]) # mask 必须是灰度图
cv2.imshow(‘Original’, img)
cv2.imshow(‘Mask’, mask)
cv2.imshow(‘Masked Image’, masked_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
6. 图像滤波 (平滑与模糊)
图像滤波用于去除噪声、平滑图像、突出边缘等。
均值模糊 (Averaging Blur): cv2.blur() 用核窗口内的像素平均值替换中心像素。
python
blurred_img = cv2.blur(img, (5, 5)) # 5x5 的核
cv2.imshow('Blurred (Average)', blurred_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
高斯模糊 (Gaussian Blur): cv2.GaussianBlur() 使用高斯核进行加权平均,离中心越近的像素权重越大。对去除高斯噪声特别有效。
python
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 0) # 5x5 核,0 表示根据核大小自动计算标准差
cv2.imshow('Gaussian Blur', gaussian_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
中值模糊 (Median Blur): cv2.medianBlur() 用核窗口内的像素中值替换中心像素。对椒盐噪声非常有效。
python
median_blur = cv2.medianBlur(img, 5) # 核大小必须是奇数
cv2.imshow('Median Blur', median_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
双边滤波 (Bilateral Filter): cv2.bilateralFilter() 在平滑图像的同时保留边缘。它同时考虑了空间距离和像素强度差异。
python
bilateral_filter = cv2.bilateralFilter(img, 9, 75, 75) # 直径为 9,颜色标准差 75,空间标准差 75
cv2.imshow('Bilateral Filter', bilateral_filter)
cv2.waitKey(0)
cv2.destroyAllWindows()
7. 形态学操作
形态学操作主要基于图像的形状进行处理,通常在二值图像上效果最佳。
- 腐蚀 (Erosion):
cv2.erode()缩小前景物体,用于去除图像中的小白点 (噪声) 或分离连接的物体。 - 膨胀 (Dilation):
cv2.dilate()扩大前景物体,用于连接断开的物体或填充前景物体中的小孔。 - 开运算 (Opening):
cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel),先腐蚀后膨胀。用于去除图像中的孤立噪声点。 - 闭运算 (Closing):
cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel),先膨胀后腐蚀。用于填充前景物体内部的小孔。 - 形态学梯度 (Morphological Gradient): 膨胀图像与腐蚀图像之差,可以得到物体的边缘。
- 顶帽 (Top Hat): 原始图像与开运算图像之差,用于提取图像中的亮斑。
- 黑帽 (Black Hat): 闭运算图像与原始图像之差,用于提取图像中的暗斑。
“`python
首先将图像转换为灰度图和二值图
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary_img = cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY)
定义一个核 (通常是矩形、椭圆形或十字形)
kernel = np.ones((5, 5), np.uint8)
腐蚀
eroded_img = cv2.erode(binary_img, kernel, iterations=1)
膨胀
dilated_img = cv2.dilate(binary_img, kernel, iterations=1)
开运算
opening_img = cv2.morphologyEx(binary_img, cv2.MORPH_OPEN, kernel)
闭运算
closing_img = cv2.morphologyEx(binary_img, cv2.MORPH_CLOSE, kernel)
cv2.imshow(‘Binary’, binary_img)
cv2.imshow(‘Erosion’, eroded_img)
cv2.imshow(‘Dilation’, dilated_img)
cv2.imshow(‘Opening’, opening_img)
cv2.imshow(‘Closing’, closing_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
8. 图像阈值处理
阈值处理将灰度图像转换为二值图像,通常用于图像分割。
简单阈值 (Simple Thresholding): cv2.threshold()。
“`python
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
第一个返回值是设定的阈值,第二个是结果图像
cv2.THRESH_BINARY: 大于阈值的设为 max_val,否则设为 0
cv2.THRESH_BINARY_INV: 大于阈值的设为 0,否则设为 max_val
cv2.THRESH_TRUNC: 大于阈值的设为阈值,否则不变
cv2.THRESH_TOZERO: 大于阈值的不变,否则设为 0
cv2.THRESH_TOZERO_INV: 大于阈值的设为 0,否则不变
ret, thresh1 = cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(gray_img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(gray_img, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(gray_img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = [‘Original Gray’, ‘BINARY’, ‘BINARY_INV’, ‘TRUNC’, ‘TOZERO’, ‘TOZERO_INV’]
images = [gray_img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i+1), plt.imshow(images[i], ‘gray’)
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
“`
自适应阈值 (Adaptive Thresholding): cv2.adaptiveThreshold() 根据图像局部区域计算阈值,对光照不均匀的图像效果更好。
“`python
cv2.ADAPTIVE_THRESH_MEAN_C: 局部均值作为阈值
cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 局部加权高斯均值作为阈值
adaptive_thresh_mean = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2) # 11×11 区域,C=2
adaptive_thresh_gaussian = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1), plt.imshow(adaptive_thresh_mean, ‘gray’), plt.title(‘Adaptive Mean’)
plt.subplot(1, 2, 2), plt.imshow(adaptive_thresh_gaussian, ‘gray’), plt.title(‘Adaptive Gaussian’)
plt.show()
“`
Otsu’s 二值化: cv2.threshold() 结合 cv2.THRESH_OTSU,自动计算最佳全局阈值。
python
ret, otsu_thresh = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(f"Otsu's Threshold: {ret}")
plt.imshow(otsu_thresh, 'gray'), plt.title("Otsu's Thresholding")
plt.show()
9. 图像梯度与边缘检测
Sobel 算子: cv2.Sobel() 计算图像在 X 或 Y 方向的导数 (梯度)。
“`python
sobelx = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0, ksize=5) # X 方向梯度
sobely = cv2.Sobel(gray_img, cv2.CV_64F, 0, 1, ksize=5) # Y 方向梯度
转换为 uint8 并合并梯度
sobelx_abs = cv2.convertScaleAbs(sobelx)
sobely_abs = cv2.convertScaleAbs(sobely)
sobel_combined = cv2.addWeighted(sobelx_abs, 0.5, sobely_abs, 0.5, 0)
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1), plt.imshow(sobelx_abs, ‘gray’), plt.title(‘Sobel X’)
plt.subplot(1, 3, 2), plt.imshow(sobely_abs, ‘gray’), plt.title(‘Sobel Y’)
plt.subplot(1, 3, 3), plt.imshow(sobel_combined, ‘gray’), plt.title(‘Sobel Combined’)
plt.show()
“`
Scharr 算子: cv2.Scharr() 类似于 Sobel,但对 3×3 核的精度更高。
Laplacian 算子: cv2.Laplacian() 计算二阶导数,用于检测图像中的孤立点或线。
“`python
laplacian = cv2.Laplacian(gray_img, cv2.CV_64F)
laplacian_abs = cv2.convertScaleAbs(laplacian)
plt.imshow(laplacian_abs, ‘gray’), plt.title(‘Laplacian’)
plt.show()
“`
Canny 边缘检测: cv2.Canny() 是一个多阶段的边缘检测算法,包括高斯模糊、计算梯度、非极大值抑制和双阈值处理。它是最常用的边缘检测算法之一。
“`python
low_threshold 和 high_threshold
如果像素梯度高于 high_threshold,则被视为强边缘。
如果像素梯度低于 low_threshold,则被拒绝。
如果像素梯度在两者之间,则只有当它连接到一个强边缘时才被视为边缘。
canny_edges = cv2.Canny(gray_img, 100, 200)
plt.imshow(canny_edges, ‘gray’), plt.title(‘Canny Edges’)
plt.show()
“`
10. 轮廓检测
轮廓是连接所有连续点 (沿着边界) 的曲线,具有相同的颜色或强度。轮廓检测在形状分析、对象识别和图像分割中非常有用。
“`python
首先进行边缘检测或阈值处理得到二值图像
edges = cv2.Canny(gray_img, 50, 150)
查找轮廓
cv2.RETR_EXTERNAL: 只检测外轮廓
cv2.RETR_LIST: 检测所有轮廓,不建立层次关系
cv2.RETR_CCOMP: 检测所有轮廓,并建立两层层次结构 (外轮廓和内孔)
cv2.RETR_TREE: 检测所有轮廓,并建立完整的层次结构
cv2.CHAIN_APPROX_NONE: 存储所有轮廓点
cv2.CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留端点
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
绘制轮廓
-1 表示绘制所有轮廓
(0, 255, 0) 是绿色 (BGR)
3 是轮廓线的粗细
contour_img = img.copy()
cv2.drawContours(contour_img, contours, -1, (0, 255, 0), 3)
cv2.imshow(‘Contours’, contour_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
遍历并分析单个轮廓
for i, contour in enumerate(contours):
area = cv2.contourArea(contour) # 计算轮廓面积
perimeter = cv2.arcLength(contour, True) # 计算轮廓周长 (True 表示闭合曲线)
x, y, w, h = cv2.boundingRect(contour) # 获取外接矩形
aspect_ratio = float(w) / h
print(f"Contour {i}: Area={area}, Perimeter={perimeter}, Bounding Box={(x, y, w, h)}")
# 可以在这里根据面积、长宽比等筛选或处理轮廓
“`
11. 颜色空间转换
OpenCV 支持多种颜色空间,如 BGR (默认)、RGB、HSV、Lab 等。
- HSV (Hue, Saturation, Value) 颜色空间对于基于颜色的对象分割非常有用,因为它将颜色信息 (色相 H) 与光照信息 (饱和度 S 和亮度 V) 分开。
- Lab 颜色空间与人眼感知颜色最接近。
“`python
将 BGR 转换为灰度
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
将 BGR 转换为 HSV
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
将 BGR 转换为 Lab
lab_img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
cv2.imshow(‘Gray’, gray_img)
cv2.imshow(‘HSV’, hsv_img)
cv2.imshow(‘Lab’, lab_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
示例:根据颜色范围检测物体 (例如,检测红色物体)
HSV 范围通常需要实验确定
lower_red = np.array([0, 100, 100])
upper_red = np.array([10, 255, 255])
mask1 = cv2.inRange(hsv_img, lower_red, upper_red)
lower_red = np.array([170, 100, 100])
upper_red = np.array([180, 255, 255])
mask2 = cv2.inRange(hsv_img, lower_red, upper_red)
红色在 HSV 中跨越 0 和 180 两个范围
full_red_mask = cv2.bitwise_or(mask1, mask2)
将遮罩应用于原始图像
red_objects = cv2.bitwise_and(img, img, mask=full_red_mask)
cv2.imshow(‘Red Mask’, full_red_mask)
cv2.imshow(‘Red Objects’, red_objects)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
12. 直方图
图像直方图显示了图像中像素强度分布情况。它在图像增强、对比度调整、阈值处理等方面非常有用。
“`python
计算灰度图像的直方图
hist_gray = cv2.calcHist([gray_img], [0], None, [256], [0, 256])
计算彩色图像每个通道的直方图
color = (‘b’, ‘g’, ‘r’)
for i, col in enumerate(color):
hist = cv2.calcHist([img], [i], None, [256], [0, 256])
plt.plot(hist, color=col)
plt.xlim([0, 256])
plt.title(‘Color Histogram’)
plt.show()
直方图均衡化 (Histogram Equalization)
增强图像的对比度,对曝光不足或过曝的图像效果明显
equalized_gray = cv2.equalizeHist(gray_img)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1), plt.imshow(gray_img, ‘gray’), plt.title(‘Original Gray’)
plt.subplot(1, 2, 2), plt.imshow(equalized_gray, ‘gray’), plt.title(‘Equalized Gray’)
plt.show()
“`
13. 模板匹配
模板匹配是在较大图像中查找与模板图像相匹配的区域。
“`python
加载主图像和模板图像
main_img = cv2.imread(‘large_image.jpg’)
template = cv2.imread(‘template_image.jpg’)
if main_img is None or template is None:
print(“Error: Could not load main image or template image.”)
else:
main_gray = cv2.cvtColor(main_img, cv2.COLOR_BGR2GRAY)
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
w, h = template_gray.shape[::-1] # 模板的宽度和高度
# 执行模板匹配
# cv2.TM_CCOEFF_NORMED 通常效果最好
res = cv2.matchTemplate(main_gray, template_gray, cv2.TM_CCOEFF_NORMED)
# 找到最佳匹配位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 对于 cv2.TM_CCOEFF_NORMED,max_loc 是最佳匹配位置
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 在主图像上绘制匹配框
cv2.rectangle(main_img, top_left, bottom_right, (0, 0, 255), 2) # 红色框
cv2.imshow('Detected', main_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
总结
Python OpenCV 是一个功能极其丰富的库,本指南仅涵盖了其冰山一角。通过这些基础操作,你可以开始进行各种图像处理任务,例如:
- 图像增强: 调整亮度、对比度、色彩平衡。
- 图像去噪: 模糊、滤波。
- 图像分割: 阈值处理、边缘检测、轮廓分析。
- 对象检测: 模板匹配、特征点匹配 (如 SIFT, SURF, ORB)。
- 图像识别: 结合机器学习算法。
要深入学习,建议查阅 OpenCV 官方文档和教程,并多加实践。通过不断尝试和组合不同的函数,你将能够解决更复杂的图像处理问题。