本文针对图像处理初学者,详细解析OpenCV核心预处理技术,包含概念解释、可视化示例和关键代码片段,帮助您建立系统的图像处理知识体系。
一、图像轮廓特征查找:对象的几何描述
轮廓是连接图像中所有连续边界点的曲线,用于描述物体的形状特征。在OpenCV中,轮廓查找通常需要先进行二值化处理:
import cv2
import numpy as np
# 读取图像并转为灰度图
img = cv2.imread('object.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化处理
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
外接矩形(Bounding Rect)
- 能完全包围轮廓的正矩形,边与坐标轴平行(不旋转)
- 特点:计算简单快速
- 应用:物体定位、碰撞检测
cv2.boundingRect()
返回矩形左上角坐标和宽高- 数学表示:
(x, y, width, height)
x, y, w, h = cv2.boundingRect(contour) cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
最小外接矩形(Rotated Rect)
- 可旋转的矩形,面积最小且完全包围轮廓
- 特点:更紧密包围对象
- 应用:物体方向检测、精密测量
cv2.minAreaRect()
返回中心点、尺寸和旋转角度- 数学表示:
(center_x, center_y), (width, height), angle
rect = cv2.minAreaRect(contour) box = cv2.boxPoints(rect) # 获取四个顶点 box = np.int0(box) cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
- 完整示例
import cv2 import numpy as np # 读图 num = cv2.imread('../images/num.png') # 转灰度 gray = cv2.cvtColor(num, cv2.COLOR_BGR2GRAY) # 二值化处理 _,thresh = cv2.threshold(gray,200,255,cv2.THRESH_BINARY_INV) # 查找轮廓 contours,hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 绘制轮廓 cv2.drawContours(num,contours,-1,(255,0,0),2) # 循环遍历每一条轮廓 for cnt in contours: # 图像轮廓特征查找:最小外接矩形 (x , y) w,h angle result = cv2.minAreaRect(cnt) # print(result) # 处理点坐标 points = cv2.boxPoints(result).astype(np.int32) # 绘制最小外接矩形 cv2.drawContours(num,[points],0,(0,0,255),2) # 绘制外接矩形 x,y,w,h = cv2.boundingRect(cnt) cv2.rectangle(num,(x,y),(x+w,y+h),(0,255,0),2) # 显示 cv2.imshow('num',num) cv2.waitKey(0) cv2.destroyAllWindows()
最小外接圆(Min Enclosing Circle)
- 完全包围轮廓的最小圆形
- 特点:对不规则形状更有效
- 应用:细胞分析、粒子检测
cv2.minEnclosingCircle()
返回圆心和半径- 数学表示:
(center_x, center_y), radius
(x, y), radius = cv2.minEnclosingCircle(contour) center = (int(x), int(y)) radius = int(radius) cv2.circle(img, center, radius, (255, 0, 0), 2)
- 完整示例
import cv2 import numpy as np # 读图 num = cv2.imread('../images/num.png') # 转灰度 gray = cv2.cvtColor(num, cv2.COLOR_BGR2GRAY) # 二值化 _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY_INV) # 轮廓查找 contours,hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 循环遍历轮廓 for cnt in contours: # 获取最小外接圆 (x,y),radius = cv2.minEnclosingCircle(cnt) # 处理点坐标 (x, y) = np.int32((x,y)) radius = np.int32(radius) # 绘制圆 cv2.circle(num,(x,y),radius,(0,0,255),2) # 显示 cv2.imshow('num',num) cv2.waitKey(0) cv2.destroyAllWindows()
应用场景:物体尺寸测量、方向识别、目标定位
二、直方图均衡化:提升图像对比度
直方图是图像像素强度的统计分布图:
- X轴:像素强度值(0-255)
- Y轴:具有该强度的像素数量
- 应用:分析图像亮度分布、指导增强处理
- 完整示例(绘制直方图)
import cv2
import numpy as np
# 读图
bg = cv2.imread('../images/bg.png')
# 创建一个黑色背景图,绘制直方图
black = np.zeros((256,256,3), np.uint8)
# 统计像素
hist = cv2.calcHist([bg], [1], None, [256], [0, 256])
# print(hist)
# print(hist[241,0])
# 获取直方图的最小值、最大值及其对应的最小值的位置索引、最大值的位置索引
minval, maxval, minloc, maxloc = cv2.minMaxLoc(hist)
# print(minval, maxval, minloc, maxloc)
# 定义直方图的高
h_hist = np.int32(256)
# 循环拿像素的个数
for i in range(256): # [num]
l = np.int32(hist[i].item() * h_hist / maxval)
point1 = (i,h_hist - l)
point2 = (i,h_hist)
cv2.line(black, point1, point2, (0,255,0), 2)
# 显示
cv2.imshow('black', black)
cv2.waitKey(0)
cv2.destroyAllWindows()
直方图均衡化(HE)
直方图均衡化通过重新分布像素强度来增强对比度:
- 原理:将集中分布的强度值扩展到整个范围
- 效果:增强图像细节,特别是暗区和亮区,增强整体对比度但可能放大噪声
- 数学基础:累积分布函数(CDF)
import cv2 # 读图 读为灰度图 img = cv2.imread('../images/zhifang.png',cv2.IMREAD_GRAYSCALE) # 直方图均衡化处理 dst = cv2.equalizeHist(img) cv2.imshow('img',img) cv2.imshow('dst',dst) cv2.waitKey(0) cv2.destroyAllWindows()
自适应直方图均衡化(AHE)
- 将图像分块后单独均衡化
- 增强局部对比度但放大噪声
对比度受限自适应均衡化(CLAHE)
- 限制局部对比度增强幅度
- 最佳效果:增强细节同时抑制噪声
import cv2 # 读图 img = cv2.imread('../images/zhifang.png',cv2.IMREAD_GRAYSCALE) # 创建一个clahe对象 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) # 使用clahe 对象去调用apply()方法 cl1 = clahe.apply(img) cv2.imshow('img', img) cv2.imshow('cl1', cl1) cv2.waitKey(0) cv2.destroyAllWindows()
三、模板匹配:在图像中查找特定模式
在输入图像中滑动搜索与模板最相似的区域:
- 原理:滑动模板图像,计算相似度
- 应用:目标检测、工业质检、OCR预处理
import cv2
import numpy as np
# 读图
img = cv2.imread('../images/game.png')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
temp = cv2.imread('../images/temp.png')
gray_temp = cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY)
print(gray_img.shape)
print(gray_temp.shape)
# 获取 temp 的 h, w
h, w = gray_temp.shape
# 模板匹配 拿到匹配结果矩阵
#
res = cv2.matchTemplate(gray_img,gray_temp,cv2.TM_CCOEFF_NORMED)
# print(res.shape)
# 设置阈值
threshold = 0.8
# 获取匹配上的结果的索引
loc = np.where(res >= threshold)
# print(loc)
# 解包,拿到成对的(x, y) 的索引
# print(zip(*loc))
for i in zip(*loc):
# print(i)
x,y = i[1],i[0]
# print(x,y)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),1)
# 显示
cv2.imshow('temp',temp)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
匹配方法对比:
六种主要相似度计算方法:
方法 | 公式 | 特点 | 适用场景 | 最佳匹配位置 |
---|---|---|---|---|
平方差匹配(TM_SQDIFF) | R=∑(T−I)2R = \sum(T-I)^2R=∑(T−I)2 | 值越小越匹配 | 精确匹配 | 最小值 |
归一化平方差(TM_SQDIFF_NORMED) | R=∑(T−I)2∑T2∑I2R = \frac{\sum(T-I)^2}{\sqrt{\sum T^2 \sum I^2}}R=∑T2∑I2∑(T−I)2 | 不受亮度影响 | 光照变化 | 最小值 |
相关匹配(TM_CCORR) | R=∑(T×I)R = \sum(T \times I)R=∑(T×I) | 值越大越匹配 | 快速匹配 | 最大值 |
归一化相关(TM_CCORR_NORMED) | R=∑(T×I)∑T2∑I2R = \frac{\sum(T \times I)}{\sqrt{\sum T^2 \sum I^2}}R=∑T2∑I2∑(T×I) | 最常用 | 通用场景 | 最大值 |
相关系数(TM_CCOEFF) | R=∑(T′×I′)∑T′2∑I′2R = \frac{\sum(T' \times I')}{\sqrt{\sum T'^2 \sum I'^2}}R=∑T′2∑I′2∑(T′×I′) | 消除均值影响 | 纹理匹配 | 最大值 |
归一化相关系数(TM_CCOEFF_NORMED) | 同上,归一化 | 最鲁棒 | 复杂场景 | 最大值 |
四、霍夫变换:检测几何形状
霍夫变换原理
霍夫变换将图像空间中的形状映射到参数空间:
- 核心思想:“投票机制” - 图像中的点投票支持可能的形状
- 优势:对噪声和部分遮挡鲁棒
霍夫直线变换
检测图像中的直线(计算量大):
- 参数空间:(ρ,θ)(\rho, \theta)(ρ,θ)
- ρ\rhoρ:原点到直线的距离
- θ\thetaθ:直线与x轴的夹角
import cv2 import numpy as np # 读图 img = cv2.imread('../images/huofu.png') # 灰度化 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # 二值化 _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 边缘检测 edges = cv2.Canny(thresh,100,200) # 霍夫变换 返回的是[r, theta] lines = cv2.HoughLines(edges,0.8,np.pi/180,90) # print(lines) for line in lines: r, theta = line[0] sin_theta = np.sin(theta) cos_theta = np.cos(theta) x1,x2 = 0, img.shape[1] y1 = int((r - x1 *cos_theta) / sin_theta) y2 = int((r - x2 *cos_theta) / sin_theta) cv2.line(img,(x1,y1),(x2,y2),(255,0,0),2) cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()
- 参数空间:(ρ,θ)(\rho, \theta)(ρ,θ)
统计概率霍夫变换(HoughLinesP)
改进版直线检测:
- 优点:只检测线段,直接返回线段端点
- 效率:比标准霍夫变换更快
import cv2 import numpy as np # 读图 img = cv2.imread('../images/huofu.png') # 灰度化 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # 二值化 _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 边缘检测 edges = cv2.Canny(thresh,100,200) lines = cv2.HoughLinesP(edges,0.8,np.pi/180,90,minLineLength =50,maxLineGap=100) print(lines) for line in lines: x1,y1,x2,y2 = line[0] cv2.line(img,(x1,y1),(x2,y2),(255,0,0),2) cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()
霍夫圆变换
检测图像中的圆形:
- 参数空间:(x,y,r)(x, y, r)(x,y,r)
- 原理:三维累加器投票
import cv2 import numpy as np # 读图 img = cv2.imread('../images/huofu.png') # 灰度化 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # 二值化 _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 边缘检测 edges = cv2.Canny(thresh,100,200) # 霍夫圆变换 circles = cv2.HoughCircles(edges,cv2.HOUGH_GRADIENT,1,10,param2 = 20) # print(circles) for circle in circles: x,y,radius = np.int32(circle[0]) # print(x,y,radius) cv2.circle(img,(x,y),radius,(0,255,0),2) cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()
五、图像亮度变换:像素值调整
亮度变换基础
调整图像整体或局部区域的亮度:
- 全局调整:影响整个图像
- 局部调整:影响特定区域
线性变换
最基础的亮度调整方法:
- 公式:g(x,y)=α⋅f(x,y)+βg(x,y) = \alpha \cdot f(x,y) + \betag(x,y)=α⋅f(x,y)+β
- α\alphaα:控制对比度(>1增加,<1减小)
- β\betaβ:控制亮度(正数变亮,负数变暗)
import cv2 # 读图 cat = cv2.imread('../images/cat1.png') # 线性变换 alpha = 1.5 # 对比度增益 beta = 1 # 亮度增量 dst = cv2.addWeighted(cat, alpha, cat, beta, 0) # 显示 cv2.imshow('cat', cat) cv2.imshow('dst', dst) cv2.waitKey(0) cv2.destroyAllWindows()
直接像素修改
通过遍历像素直接修改值:
- 应用:创建自定义滤镜、选择性调整
- 注意:效率较低,应谨慎使用
bright_img = np.zeros_like(img) for y in range(img.shape[0]): for x in range(img.shape[1]): bright_img[y,x] = min(255, img[y,x] + 30) # 增加亮度
滑条控制亮度
import cv2
import numpy as np
# 读图
cat = cv2.imread('../images/cat1.png')
#创建窗口 用于实现滑条
window_name = "slide"
cv2.namedWindow(window_name,cv2.WINDOW_AUTOSIZE)\
img = cv2.imread('../images/cat1.png')
def change(p):
x = p/256*511-255
dst = np.uint8(np.clip(img + x,0,255))
cv2.imshow('dst',dst)
print(x)
# 创建一个滑条
initial_value = 100
cv2.createTrackbar("add_p","slide",initial_value,255,change)
cv2.waitKey(0)
cv2.destroyAllWindows()
实际应用:使用向量化操作提高效率
bright_img = cv2.add(img, 30)
六、形态学变换:形状处理的基础
结构元素(核)
形态学操作的核心组件:
- 作用:定义邻域大小和形状
- 类型:矩形、椭圆、十字形
- 大小:奇数尺寸(3×3、5×5等)
基于结构元素(核) 的形状操作:
kernel = np.ones((5,5), np.uint8) # 5x5正方形核
基本操作
操作 | 函数 | 效果 | 可视化 | 原理 | 应用场景 |
---|---|---|---|---|---|
腐蚀 | cv2.erode() |
缩小物体边界,消除小点 | ![]() |
用核的最小值替换锚点 | 去除噪声 |
膨胀 | cv2.dilate() |
扩大物体边界,填补空洞 | ![]() |
用核的最大值替换锚点 | 连接断裂 |
开运算 | cv2.morphologyEx(MORPH_OPEN) |
先腐蚀后膨胀 → 去噪 | ![]() |
先腐蚀后膨胀 | 背景分割 |
闭运算 | cv2.morphologyEx(MORPH_CLOSE) |
先膨胀后腐蚀 → 补洞 | ![]() |
先膨胀后腐蚀 | 前景完整 |
礼帽 | cv2.morphologyEx(MORPH_TOPHAT) |
原图 - 开运算 → 突出亮区域 | ![]() |
原图 - 开运算 | 背景均匀的亮特征 |
黑帽 | cv2.morphologyEx(MORPH_BLACKHAT) |
闭运算 - 原图 → 突出暗区域 | ![]() |
闭运算 - 原图 | 背景均匀的暗特征 |
形态学梯度 | cv2.morphologyEx(MORPH_GRADIENT) |
膨胀图 - 腐蚀图 → 提取边缘 | ![]() |
膨胀 - 腐蚀 | 边缘检测 |
import cv2
import numpy as np
# 读图
car = cv2.imread('../images/car.png',cv2.IMREAD_GRAYSCALE)
# 定义核
kernel = np.ones((5,5),np.uint8)
# 腐蚀
erosion = cv2.erode(car,kernel,iterations = 1)
# 膨胀
dilation = cv2.dilate(car,kernel,iterations = 1)
# 开运算
opening = cv2.morphologyEx(car,cv2.MORPH_OPEN,kernel)
# 闭运算
close = cv2.morphologyEx(car,cv2.MORPH_CLOSE,kernel)
# 礼帽运算
tophat = cv2.morphologyEx(car,cv2.MORPH_TOPHAT,kernel)
# 黑帽运算
blackhat = cv2.morphologyEx(car,cv2.MORPH_BLACKHAT,kernel)
# 形态学梯度 膨胀和腐蚀差
gradient = cv2.morphologyEx(car,cv2.MORPH_GRADIENT,kernel)
cv2.imshow('car',car)
cv2.imshow('erosion',erosion)
cv2.imshow('dilation',dilation)
cv2.imshow('opening',opening)
cv2.imshow('close',close)
cv2.imshow('tophat',tophat)
cv2.imshow('blackhat',blackhat)
cv2.imshow('gradient',gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()
核形状选择:
cv2.MORPH_RECT
矩形核(默认)cv2.MORPH_ELLIPSE
椭圆核cv2.MORPH_CROSS
十字形核
形态学操作可视化
原始图像: [1 1 1 0 0]
[1 1 1 0 0]
[1 1 1 0 0]
[0 0 0 0 0]
腐蚀后: [1 1 0 0 0]
[1 1 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
膨胀后: [1 1 1 1 0]
[1 1 1 1 0]
[1 1 1 0 0]
[0 0 0 0 0]
技术组合应用示例
# 完整的预处理流程
img = cv2.imread('industrial_part.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 1. 亮度调整
adjusted = cv2.convertScaleAbs(gray, alpha=1.2, beta=20)
# 2. CLAHE增强对比度
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_img = clahe.apply(adjusted)
# 3. 中值滤波去噪
blurred = cv2.medianBlur(clahe_img, 5)
# 4. 形态学开运算去噪
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
opened = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)
# 5. Canny边缘检测
edges = cv2.Canny(opened, 50, 150)
# 6. 查找轮廓并绘制最小外接矩形
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0,0,255), 2)
cv2.imshow('Result', img)
cv2.waitKey(0)