使用 Python OpenCV 进行图像处理:全面指南 – wiki大全

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 库。

  1. 安装 Python: 访问 python.org 下载并安装最新版本的 Python。
  2. 安装 OpenCV: 使用 pip 安装 opencv-python

    bash
    pip install opencv-python numpy matplotlib

    numpy 是 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 官方文档和教程,并多加实践。通过不断尝试和组合不同的函数,你将能够解决更复杂的图像处理问题。

滚动至顶部