Python机器视觉第二阶段补充:OpenCV核心图像处理技术详解
在上一篇文章中,我们学习了OpenCV的基础操作,包括图像读写、摄像头调用、鼠标事件等。但图像处理的真正核心在于。本文将详细讲解这些技术,并提供可直接运行的案例代码。本文所有代码均可直接复制运行,建议结合OpenCV官方教程边学边练。
·
【零基础入门】Python机器视觉第二阶段补充:OpenCV核心图像处理技术详解
在上一篇文章中,我们学习了OpenCV的基础操作,包括图像读写、摄像头调用、鼠标事件等。但图像处理的真正核心在于滤波、边缘检测、阈值分割、形态学操作和轮廓分析。本文将详细讲解这些技术,并提供可直接运行的案例代码。
本文所有代码均可直接复制运行,建议结合OpenCV官方教程边学边练。
一、图像滤波(去噪与平滑)
滤波的主要目的是去除图像中的噪声,同时尽可能保留图像细节。
1.1 均值滤波
用邻域内像素的平均值代替中心像素值,是最简单的平滑滤波器。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像并添加噪声(模拟)
img = cv2.imread('example.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 均值滤波
# ksize 是卷积核大小,必须是奇数
blur_3 = cv2.blur(img, (3, 3))
blur_5 = cv2.blur(img, (5, 5))
# 显示对比
plt.figure(figsize=(12,4))
plt.subplot(1,3,1), plt.imshow(img_rgb), plt.title('Original')
plt.subplot(1,3,2), plt.imshow(cv2.cvtColor(blur_3, cv2.COLOR_BGR2RGB)), plt.title('Blur 3x3')
plt.subplot(1,3,3), plt.imshow(cv2.cvtColor(blur_5, cv2.COLOR_BGR2RGB)), plt.title('Blur 5x5')
plt.show()
1.2 高斯滤波
高斯滤波考虑了邻域像素距离中心点的权重(距离越近权重越大),能更好地保留边缘。
# 高斯滤波
# (5,5) 是核大小,sigmaX 是标准差
gaussian = cv2.GaussianBlur(img, (5, 5), 0)
plt.imshow(cv2.cvtColor(gaussian, cv2.COLOR_BGR2RGB))
plt.title('Gaussian Blur')
plt.show()
1.3 中值滤波
用邻域内像素的中值代替中心像素值,对椒盐噪声特别有效。
# 中值滤波
# 核大小必须是奇数
median = cv2.medianBlur(img, 5)
plt.imshow(cv2.cvtColor(median, cv2.COLOR_BGR2RGB))
plt.title('Median Blur')
plt.show()
1.4 案例:对噪声图像进行降噪
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 生成带噪声的图像
img = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
h, w = img.shape
# 添加椒盐噪声
noise = np.random.randint(0, 255, (h, w))
noisy_img = img.copy()
noisy_img[noise > 250] = 255 # 随机白点
noisy_img[noise < 5] = 0 # 随机黑点
# 应用不同滤波
blur = cv2.blur(noisy_img, (3, 3))
gaussian = cv2.GaussianBlur(noisy_img, (3, 3), 0)
median = cv2.medianBlur(noisy_img, 3)
plt.figure(figsize=(15,5))
plt.subplot(1,4,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,4,2), plt.imshow(noisy_img, cmap='gray'), plt.title('Noisy')
plt.subplot(1,4,3), plt.imshow(blur, cmap='gray'), plt.title('Mean Filter')
plt.subplot(1,4,4), plt.imshow(median, cmap='gray'), plt.title('Median Filter')
plt.show()
二、边缘检测(Canny)
边缘检测是找出图像中亮度变化剧烈的点,Canny算法是最常用的边缘检测器。
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# Canny边缘检测
# 参数:图像,低阈值,高阈值
edges = cv2.Canny(img, 50, 150)
plt.figure(figsize=(10,5))
plt.subplot(1,2,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,2,2), plt.imshow(edges, cmap='gray'), plt.title('Canny Edges')
plt.show()
阈值调整技巧:
- 低阈值越小,检测到的边缘越多(但也可能引入噪声)
- 高阈值与低阈值的比值通常在2:1到3:1之间
三、图像阈值分割
3.1 全局阈值
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 全局阈值
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
plt.figure(figsize=(12,8))
plt.subplot(2,3,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(2,3,2), plt.imshow(thresh1, cmap='gray'), plt.title('Binary')
plt.subplot(2,3,3), plt.imshow(thresh2, cmap='gray'), plt.title('Binary Inv')
plt.subplot(2,3,4), plt.imshow(thresh3, cmap='gray'), plt.title('Trunc')
plt.subplot(2,3,5), plt.imshow(thresh4, cmap='gray'), plt.title('Tozero')
plt.show()
3.2 自适应阈值(解决光照不均)
# 自适应阈值
thresh_mean = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
thresh_gaussian = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
plt.figure(figsize=(12,4))
plt.subplot(1,3,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,3,2), plt.imshow(thresh_mean, cmap='gray'), plt.title('Mean Adaptive')
plt.subplot(1,3,3), plt.imshow(thresh_gaussian, cmap='gray'), plt.title('Gaussian Adaptive')
plt.show()
3.3 Otsu大津法(自动确定最佳阈值)
# Otsu自动阈值
ret, thresh_otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(f"Otsu计算的阈值: {ret}")
plt.figure(figsize=(10,5))
plt.subplot(1,2,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,2,2), plt.imshow(thresh_otsu, cmap='gray'), plt.title('Otsu')
plt.show()
四、形态学操作
形态学操作主要针对二值图像,用于处理形状。
4.1 腐蚀与膨胀
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 创建二值图像
img = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 定义结构元素
kernel = np.ones((5,5), np.uint8)
# 腐蚀
erosion = cv2.erode(binary, kernel, iterations=1)
# 膨胀
dilation = cv2.dilate(binary, kernel, iterations=1)
plt.figure(figsize=(15,5))
plt.subplot(1,3,1), plt.imshow(binary, cmap='gray'), plt.title('Binary')
plt.subplot(1,3,2), plt.imshow(erosion, cmap='gray'), plt.title('Erosion')
plt.subplot(1,3,3), plt.imshow(dilation, cmap='gray'), plt.title('Dilation')
plt.show()
4.2 开运算与闭运算
- 开运算:先腐蚀后膨胀,用于去除小白点(噪声)
- 闭运算:先膨胀后腐蚀,用于填补小黑洞
# 开运算
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 闭运算
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
plt.figure(figsize=(15,5))
plt.subplot(1,3,1), plt.imshow(binary, cmap='gray'), plt.title('Binary')
plt.subplot(1,3,2), plt.imshow(opening, cmap='gray'), plt.title('Opening')
plt.subplot(1,3,3), plt.imshow(closing, cmap='gray'), plt.title('Closing')
plt.show()
4.3 形态学梯度
膨胀减去腐蚀,得到物体的轮廓。
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)
plt.imshow(gradient, cmap='gray'), plt.title('Gradient')
plt.show()
五、轮廓查找与绘制
轮廓查找可以识别出图像中物体的边界。
5.1 基本轮廓查找
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像并转为灰度
img = cv2.imread('coins.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 阈值分割
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓
img_contours = img.copy()
cv2.drawContours(img_contours, contours, -1, (0,255,0), 2)
print(f"检测到 {len(contours)} 个轮廓")
plt.figure(figsize=(15,5))
plt.subplot(1,3,1), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('Original')
plt.subplot(1,3,2), plt.imshow(binary, cmap='gray'), plt.title('Binary')
plt.subplot(1,3,3), plt.imshow(cv2.cvtColor(img_contours, cv2.COLOR_BGR2RGB)), plt.title('Contours')
plt.show()
5.2 轮廓特征(面积、周长、外接矩形)
for i, contour in enumerate(contours):
area = cv2.contourArea(contour)
perimeter = cv2.arcLength(contour, True)
x, y, w, h = cv2.boundingRect(contour)
print(f"轮廓 {i}: 面积={area:.1f}, 周长={perimeter:.1f}, 外接矩形=({x},{y},{w},{h})")
5.3 案例:统计硬币数量并圈出
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('coins.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 中值滤波去噪
gray = cv2.medianBlur(gray, 5)
# Otsu阈值
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 形态学开运算去除小噪声
kernel = np.ones((3,3), np.uint8)
binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=2)
# 查找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 筛选面积合适的轮廓(过滤太小或太大的)
min_area = 1000
max_area = 50000
coin_contours = []
for contour in contours:
area = cv2.contourArea(contour)
if min_area < area < max_area:
coin_contours.append(contour)
# 绘制结果
result = img.copy()
cv2.drawContours(result, coin_contours, -1, (0,255,0), 2)
# 在每个硬币上标数字
for i, contour in enumerate(coin_contours):
x, y, w, h = cv2.boundingRect(contour)
cv2.putText(result, str(i+1), (x, y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,0,255), 2)
plt.figure(figsize=(10,10))
plt.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
plt.title(f'检测到 {len(coin_contours)} 枚硬币')
plt.show()
六、练习:肤色分割与最大轮廓提取
用摄像头实时抓取手部区域,通过HSV颜色范围进行分割,并画出最大轮廓。
import cv2
import numpy as np
# HSV肤色范围(可调整)
lower_skin = np.array([0, 20, 70])
upper_skin = np.array([20, 255, 255])
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
# 高斯模糊
blurred = cv2.GaussianBlur(frame, (5,5), 0)
# 转为HSV
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
# 生成肤色掩膜
mask = cv2.inRange(hsv, lower_skin, upper_skin)
# 形态学操作去除噪声
kernel = np.ones((3,3), np.uint8)
mask = cv2.erode(mask, kernel, iterations=1)
mask = cv2.dilate(mask, kernel, iterations=2)
# 查找轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
# 找到最大轮廓
max_contour = max(contours, key=cv2.contourArea)
# 绘制最大轮廓
cv2.drawContours(frame, [max_contour], -1, (0,255,0), 2)
# 获取外接矩形
x, y, w, h = cv2.boundingRect(max_contour)
cv2.rectangle(frame, (x,y), (x+w, y+h), (255,0,0), 2)
cv2.imshow('Skin Detection', frame)
cv2.imshow('Mask', mask)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
📚 核心图像处理技术参考文档
- OpenCV官方教程:图像处理模块 —— 包含滤波、边缘检测、阈值、形态学、轮廓等全部内容
- OpenCV图像平滑 —— 各种滤波详解
- OpenCV边缘检测 —— Canny、Sobel等
- OpenCV图像阈值 —— 全局阈值、自适应阈值、Otsu
- OpenCV形态学转换 —— 腐蚀、膨胀、开闭运算、梯度
- OpenCV轮廓检测 —— 轮廓查找、绘制、特征分析
如果有任何疑问或需要更详细的案例,欢迎随时交流!
更多推荐
所有评论(0)