OpenCV计算机视觉实战(14)——直方图均衡化
0. 前言
在图像处理与计算机视觉领域,直方图技术是最直观且高效的像素分布分析工具。无论是提升低对比度图像的细节表现,还是基于颜色特征进行目标定位与追踪,直方图均衡化与反向投影方法都能发挥关键作用。本文将从 CLAHE
自适应直方图均衡、反向投影的颜色热力图生成,到结合 CAMShift
构建实时颜色追踪系统,掌握直方图在图像增强与目标跟踪中的实战应用。
1. CLAHE 自适应均衡
传统直方图均衡化在光照不均的图像中容易引入过曝或欠曝。CLAHE
(Contrast Limited Adaptive Histogram Equalization
) 通过在局部区域内进行增强,并对对比度设限,有效避免过增强,提升图像细节。
1.1 应用场景
- 车载夜间摄像:不同路面反光、街灯位置不均,用全局均衡易引入过曝
- 医疗影像:
CT
射线图像局部对比度低,需在细小病灶区域提亮 - 文档扫描:老旧书页局部发黄不一,用
CLAHE
能避免过度白化
1.2 实现过程
- 将图像从
BGR
转为LAB
色彩空间(因为亮度分量L
更适合做增强) - 对
L
通道应用CLAHE
- 合并增强后的
L
与原始A
、B
通道 - 转回
BGR
显示效果
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('1.jpeg')
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
cl = clahe.apply(l)
limg = cv2.merge((cl, a, b))
final = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
# 绘制直方图对比
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
axes[0,0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)); axes[0,0].set_title('Original')
axes[0,1].hist(l.ravel(), bins=256); axes[0,1].set_title('Original L Histogram')
axes[1,0].imshow(cv2.cvtColor(final, cv2.COLOR_BGR2RGB)); axes[1,0].set_title('CLAHE Enhanced')
axes[1,1].hist(cl.ravel(), bins=256); axes[1,1].set_title('CLAHE L Histogram')
for ax in axes.flatten(): ax.axis('off')
plt.tight_layout()
plt.show()
关键函数解析:
cv2.createCLAHE()
:创建CLAHE
对象,clipLimit
控制对比度限制,tileGridSize
决定分块区域大小cv2.cvtColor(..., cv2.COLOR_BGR2LAB)
:转换至LAB
色彩空间,增强亮度L
分量clahe.apply()
:对灰度图或L
通道应用CLAHE
参数 | 含义 | 调参建议 |
---|---|---|
clipLimit |
对比度阈值,越大局部对比度增强越明显 | 2.0–4.0 之间,夜间图像可适当调高 |
tileGridSize |
划分网格数,(8,8)→(16,16)细节块越小 | 块越小细节增强越多,但噪声也随之增强 |
2. 直方图反向投影
直方图反向投影 (Back Projection
) 可理解为“颜色热力图”。给定某种颜色分布的感兴趣区域 (Region of Interest
, ROI
) (如皮肤颜色),它可以在整幅图像中估算每个像素属于该颜色的概率,常用于颜色追踪和皮肤检测。
2.1 应用场景
- 智能零售:检测货架上某品牌包装色块
- 安防监控:快速定位佩戴特定颜色制服的人员
- 增强现实:实时识别并跟踪特定颜色的虚拟标记
2.2 实现过程
1D vs 2D
直方图1D
(只用H
通道)运算快但易受光照影响2D
(H + S
通道)更稳健,建议对肤色、品牌色、球类追踪均使用2D
- 后处理
- 形态学闭运算:填补小孔洞,去除零星噪点
- 高斯模糊卷积:平滑概率图,再阈值化能获得更连续的候选区域
- 可视化建议
- 将反向投影结果与原图叠加,用伪彩(如
applyColorMap
) 突出高概率区域。
- 将反向投影结果与原图叠加,用伪彩(如
import cv2
import numpy as np
img = cv2.imread('1.jpeg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# ROI 取皮肤区域
x,y,w,h = 830,320,30,30
roi = hsv[y:y+h, x:x+w]
# 2D 直方图计算与归一化
roi_hist = cv2.calcHist([roi], [0,1], None, [180,256], [0,180,0,256])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
# 反向投影
dst = cv2.calcBackProject([hsv], [0,1], roi_hist, [0,180,0,256], 1)
# 形态学闭运算
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
dst = cv2.morphologyEx(dst, cv2.MORPH_CLOSE, kernel)
# 伪彩叠加
heat = cv2.applyColorMap(dst, cv2.COLORMAP_JET)
overlay = cv2.addWeighted(img, 0.6, heat, 0.4, 0)
cv2.imshow('Back Projection Overlay', overlay)
cv2.waitKey(0)
cv2.destroyAllWindows()
关键函数解析:
cv2.calcHist(images, channels, mask, histSize, ranges)
用于计算图像的直方图,在HSV
色彩空间中,通常使用二维直方图来分析色调 (Hue
) 和饱和度 (Saturation
) 的分布images
:输入图像,需以列表形式传入,例如[image]
channels
:用于计算直方图的通道索引列表,例如,在HSV
空间中,[0, 1]
表示使用H
和S
通道mask
:掩膜图像,若不使用掩膜,则为None
histSize
:每个通道的直方图大小(即bin
的数量),例如[180, 256]
表示H
通道有180
个bin
,S
通道有256
个bin
ranges
:每个通道的取值范围。例如[0, 180, 0, 256]
表示H
通道取值范围为0
到180
,S
通道为0
到256
dst = cv2.calcBackProject(images, channels, hist, ranges, scale)
用于执行直方图反向投影,它根据给定的直方图在目标图像中查找匹配区域,返回一个概率图,表示每个像素匹配该直方图的概率images
:输入图像,需以列表形式传入,例如[image]
channels
:用于计算反向投影的通道索引列表,需与直方图的通道一致hist
:用于反向投影的直方图,通常是目标对象的颜色直方图ranges
:每个通道的取值范围,需与计算直方图时使用的范围一致scale
:可选的缩放因子,通常设为1
cv2.normalize(src, dst, alpha, beta, norm_type)
用于将数组(如直方图)的值归一化到指定范围,这在进行直方图比较或反向投影前是一个重要的预处理步骤src
:输入数组,即待归一化的直方图dst
:输出数组,可以与src
相同alpha
:归一化后数组的最小值beta
:归一化后数组的最大值norm_type
:归一化类型,常用可选值包括cv2.NORM_MINMAX
(将值缩放到指定范围)和cv2.NORM_L1
(使数组的L1
范数为1
3. 基于颜色的目标追踪
在视频流或相机实时画面中,利用直方图反向投影与 CamShift
算法,实现对特定颜色(如球、手套)的连续追踪。
首先,读取视频流,手动框选或预设目标 ROI
,并计算其 HSV
直方图模型。然后在主循环中,对每帧图像进行 HSV
转换、反向投影,再使用 CamShift
算法更新目标窗口位置。最后可视化结果,绘制追踪窗口并显示。
import cv2
import numpy as np
def color_tracker(video_src=0):
"""
功能:基于 CamShift 的颜色追踪系统
参数:
video_src (int/str) - 视频设备索引或文件路径
"""
cap = cv2.VideoCapture(video_src)
ret, frame = cap.read()
# 1. 手动选定初始 ROI
x, y, w, h = cv2.selectROI("Select ROI", frame, False)
cv2.destroyWindow("Select ROI")
# 2. 计算 ROI 的 HSV 直方图模型
hsv_roi = cv2.cvtColor(frame[y:y+h, x:x+w], cv2.COLOR_BGR2HSV)
roi_hist = cv2.calcHist([hsv_roi], [0,1], None, [180,256], [0,180,0,256])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
track_window = (x, y, w, h)
# 3. CamShift 参数
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
while True:
ret, frame = cap.read()
if not ret:
break
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 4. 反向投影
backproj = cv2.calcBackProject([hsv], [0,1], roi_hist, [0,180,0,256], 1)
# 5. CamShift 更新窗口
ret_box, track_window = cv2.CamShift(backproj, track_window, term_crit)
# 6. 绘制结果
pts = cv2.boxPoints(ret_box)
pts = np.int0(pts)
cv2.polylines(frame, [pts], True, (0,255,0), 2)
cv2.imshow("Color Tracking", frame)
if cv2.waitKey(30) & 0xFF == 27: # 按 ESC 键退出
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
color_tracker(0)
关键函数解析:
cv2.selectROI(windowName, img, showCrosshair)
:交互式选取ROI
区域,返回(x, y, w, h)
cv2.CamShift(probImage, window, criteria)
:基于反向投影概率图进行自适应均值迁移,返回新的边界框ret_box
(旋转矩形)和更新后的搜索窗口track_window
cv2.boxPoints(rotatedRect)
:将CamShift
返回的旋转矩形参数转换为四个顶点坐标,便于绘制多边形
小结
在本文中,我们系统性地介绍了 CLAHE
自适应直方图均衡化、直方图反向投影以及基于颜色追踪的目标定位系统。这些方法不仅能显著增强图像的视觉质量,还能为实际工程项目提供稳健的技术支撑。CLAHE
通过分块与对比度限幅,有效提升了局部细节;反向投影利用颜色概率分布快速筛选目标区域;颜色追踪系统则将上述方法综合运用,实现了可扩展的动态目标跟踪方案。
系列链接
OpenCV计算机视觉实战(1)——计算机视觉简介
OpenCV计算机视觉实战(2)——环境搭建与OpenCV简介
OpenCV计算机视觉实战(3)——计算机图像处理基础
OpenCV计算机视觉实战(4)——计算机视觉核心技术全解析
OpenCV计算机视觉实战(5)——图像基础操作全解析
OpenCV计算机视觉实战(6)——经典计算机视觉算法
OpenCV计算机视觉实战(7)——色彩空间详解
OpenCV计算机视觉实战(8)——图像滤波详解
OpenCV计算机视觉实战(9)——阈值化技术详解
OpenCV计算机视觉实战(10)——形态学操作详解
OpenCV计算机视觉实战(11)——边缘检测详解
OpenCV计算机视觉实战(12)——图像金字塔与特征缩放
OpenCV计算机视觉实战(13)——轮廓检测详解