目录
1、霍夫直线变换(Hough Line Transform)
一、直方图均衡化
1、什么是直方图均衡化
(1)定义
直方图均衡化是一种用于增强图像对比度的图像处理技术。它通过重新分布图像的像素强度值(灰度值),使得像素值尽可能地均匀分布在 0~255 范围内,从而提升图像的视觉效果,尤其是当图像整体偏暗或偏亮时。
(2)原理
1、目标:
让图像的灰度级分布更“均匀”,增强对比度。
2、核心思想:
-
统计原始图像的灰度直方图(每个灰度值出现的频率)。
-
计算累积分布函数(CDF, Cumulative Distribution Function)。
-
利用 CDF 对原始像素值进行映射 → 得到新的像素值。
-
新图像的直方图趋于平坦(即各个灰度级使用更均衡)。
3、数学过程简述:
设图像大小为 M×N ,总像素数 S=M×N
-
直方图:h(k)= 灰度值为 k 的像素个数(k=0,1,...,255 )
-
概率分布:p(k)=Sh(k)
-
累积分布:cdf(k)=∑j=0kp(j)
-
映射函数:T(k)=round(255×cdf(k))
然后将原图中所有灰度值为 k 的像素替换为 T(k) 。
(3)直方图均衡化的作用
场景 | 效果 |
---|---|
图像过暗 | 提亮细节 |
图像过亮 | 增强阴影部分 |
对比度低 | 拉开灰度差距,使纹理更清晰 |
医疗影像、监控画面 | 常用于预处理以增强可读性 |
2、绘制直方图
代码示例:
import cv2 as cv
import numpy as np
# 读图
bg = cv.imread('../images/bg.png')
# 创建黑图,绘制直方图
black = np.zeros((256, 256, 3),np.uint8)
# 统计像素 cv.calcHist(图像,通道,掩膜, bins, ranges)
hist = cv.calcHist([bg], [1], None, [256], [0, 256])
print(hist)
# 获取直方图的最小值 ,以及最大值和最小值对应的索引 [列,行 ] (x,y)
minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(hist)
# 定义直方图的高
h_hist = np.int32(256)
# 循环拿像素的个数
for i in range(256):
# 获取像素的个数
l = int(hist[i].item() * h_hist/maxVal)
point1 = (i, 256 - l)
point2 = (i, 256)
cv.line(black , point1, point2, (255, 0, 0), 1)
cv.imshow('dst', black)
cv.waitKey(0)
cv.destroyAllWindows()
直方图展示:
可以看出这张图的 像素值分布非常不均衡,所以图像的对比度会很差,观察到的不清晰
3、直方图均衡化
(1)自适应直方图均衡化
OpenCV 提供了简单高效的函数来实现直方图均衡化。
dst = cv.equalizeHist(src)
src
:输入图像,必须是 单通道灰度图(Grayscale),类型uint8
dst
:输出图像,经过均衡化后的灰度图
代码示例:
import cv2 as cv
# 读图
img = cv.imread('../images/num.png', cv.IMREAD_GRAYSCALE)
# 直方图均衡化
dst = cv.equalizeHist(img)
cv.imshow("img", img)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()
结果展示:
可以看出经过直方图均衡化的图像更亮了,对比度更高了,但是我们能看到,人头像的脸部明显曝光严重,已经看不清轮廓了,这就是普通的自适应直方图均衡化的缺点,由此我们引入了对比度受限的自适应直方图均衡化。
(2)对比度受限的自适应直方图均衡化
普通直方图均衡化对整个图像进行处理,可能导致噪声放大或局部过增强。为此,OpenCV 提供了 CLAHE(Contrast Limited Adaptive Histogram Equalization)。
CLAHE 原理
将图像分成小块(称为 tiles)
对每个小块进行直方图均衡化
使用对比度限制(clip limit)防止噪声放大
最后通过双线性插值拼接块,避免块边界明显
API使用:
# 创建 CLAHE 对象
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
# 对灰度图应用 CLAHE
img_clahe = clahe.apply(img) # img 是灰度图
-
clipLimit
:对比度限制阈值,防止过增强(默认 2.0) -
tileGridSize
:分块大小,如 (8,8) 表示 8x8 个小块
代码示例:
import cv2 as cv
img = cv.imread('../images/zhifang.png', cv.IMREAD_GRAYSCALE)
# 创建CLAHE对象
clahe = cv.createCLAHE(clipLimit = 2.0, tileGridSize = (8, 8))
# 使用CLAHE调用apply()方法
cl1 = clahe.apply(img)
cv.imshow('img', img)
cv.imshow('cl1', cl1)
cv.waitKey(0)
cv.destroyAllWindows()
结果展示:
可以看出我们的CLAHE既能提升对比度,但也不会造成脸部曝光,效果更好。
注意事项:
输入图像必须是
uint8
类型彩色图像建议先转到 YUV/HSV 空间再处理亮度通道
CLAHE 的
tileGridSize
不宜太小,否则计算量大;不宜太大,否则失去局部性
clipLimit
过大会导致噪声增强,一般设为 2~4
二、模板匹配
1、什么是模板匹配
(1)定义
模板匹配(Template Matching) 是一种在大图像(源图像)中寻找与小图像(模板图像)最相似区域的图像处理技术。
-
模板图像(Template):你想找的小图像(比如一个图标、一个数字、一个按钮)。
-
源图像(Source):你要在其中搜索的大图像(比如整个屏幕截图)。
-
目标:找出模板在源图像中出现的位置(通常是左上角坐标)。
(2)基本思想
-
将模板图像在源图像上滑动(从左到右,从上到下)。
-
在每个位置,计算模板与当前区域的相似度。
-
相似度最高的位置,就是最可能匹配的位置。
(3)匹配方法
OpenCV 提供了多种匹配方法,通过 cv2.matchTemplate()
的 method
参数指定。常见的有:
方法 | 说明 |
---|---|
| 平方差匹配,值越小越匹配(最佳匹配处为0) |
| 归一化平方差,值越接近0越匹配 |
| 相关匹配,值越大越匹配 |
| 归一化相关匹配,值在 [0,1],越接近1越匹配 ✅(推荐) |
| 相关系数匹配,值越大越匹配 |
| 归一化相关系数匹配,值在 [-1,1],越接近1越匹配 ✅(最常用) |
✅ 推荐使用
TM_CCOEFF_NORMED
或TM_CCORR_NORMED
,因为它们对光照变化更鲁棒。
API使用:
result = cv2.matchTemplate (image, template, method, result=None, mask=None)
参数说明
参数 | 说明 |
---|---|
| 源图像(大图),灰度图或彩色图 |
| 模板图像(小图),必须比源图像小 |
| 匹配方法,如 |
| 输出匹配结果的矩阵(可选) |
| 掩码(仅用于 |
返回值
-
一个二维数组(热力图),每个位置的值表示该位置的匹配程度。
-
值越高(或越低,取决于方法)表示越匹配。
2、绘制轮廓
示例代码:
import cv2 as cv
import numpy as np
# 读图
img = cv.imread('../images/game.png')
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# print(gray_img.shape)
temp = cv.imread('../images/temp.png')
gray_temp = cv.cvtColor(temp, cv.COLOR_BGR2GRAY)
h, w = temp.shape[:2]
# print(gray_temp.shape)
# 模板匹配 拿到匹配结果矩阵
res = cv.matchTemplate(gray_img, gray_temp, cv.TM_CCOEFF_NORMED)
# print(res.shape)
# 设置阈值
thresh = 0.8
# 获取匹配上的结果的索引
loc = np.where(res >= thresh)
# print(len(loc))
# 解包,拿到成对的 x y 索引
for i in zip(*loc):
x, y = i[1], i[0]
cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 1)
cv.imshow('img', img)
cv.waitKey(0)
cv.destroyAllWindows()
展示结果:
匹配相应形状,绘出轮廓:
三、霍夫变换
霍夫变换(Hough Transform) 是一种用于检测图像中具有特定形状(如直线、圆等)的特征提取技术。它通过将图像空间中的点映射到参数空间进行投票,从而检测出这些几何形状。
为什么需要霍夫变换?
-
边缘检测(如 Canny)只能得到离散的边缘点。
-
霍夫变换可以将这些点 “连接” 起来,识别出直线、圆等几何结构。
-
对噪声和缺失部分具有较强的鲁棒性。
1、霍夫直线变换(Hough Line Transform)
(1)原理
在笛卡尔坐标系中,一条直线表示为:
y=mx+b
但当直线垂直时,斜率 m 会趋于无穷大,不方便处理。
因此,霍夫变换使用极坐标表示:
xcosθ+ysinθ=ρ
其中:
-
ρ :原点到直线的垂直距离
-
θ :该垂线与 x 轴的夹角
(2)变换过程:
-
对图像中的每一个边缘点 (x,y)
-
在参数空间 (ρ,θ) 中,绘制所有可能经过该点的直线(即曲线)
-
多个点共线时,这些曲线会在 (ρ,θ) 空间相交于一点
-
通过“投票”机制,统计交点的得票数,得票高的点即为检测到的直线
(3)API:cv2.HoughLines()
和 cv2.HoughLinesP()
标准霍夫变换:cv2.HoughLines()
lines = cv2.HoughLines(edges, rho, theta, threshold)
-
edges
:二值边缘图像(如 Canny 检测结果) -
rho
:ρ 的精度(像素单位,通常为 1) -
theta
:θ 的精度(弧度单位,通常为np.pi/180
) -
threshold
:投票阈值,只有得票数超过该值才认为是直线
返回值:lines
是一个数组,每个元素是 (rho, theta)
对
⚠️ 缺点:返回的是无限长的直线,且计算量大。
示例代码:
import cv2 as cv
import numpy as np
# 读图
img = cv.imread("../images/huofu.png")
# 灰度化
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 二值化
_, binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
# 边缘检测
edges = cv.Canny(binary, 30, 70)
# 霍夫变换 返回的是[r, theta]
lines = cv.HoughLines(edges, 0.8, np.pi/180, 90)
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)
cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), 1)
cv.imshow('img', img)
cv.waitKey(0)
cv.destroyAllWindows()
结果展示:
概率霍夫变换:cv2.HoughLinesP()
(推荐使用)
lines = cv2.HoughLinesP(edges, rho, theta, threshold, minLineLength=None, maxLineGap=None)
-
minLineLength
:最小线段长度(小于该值的线段被丢弃) -
maxLineGap
:允许将两条线段视为一条线的最大间隔
返回值:每条线段表示为 [x1, y1, x2, y2]
✅ 优点:直接返回线段端点,适合实际应用。
示例代码:
import cv2 as cv
import numpy as np
# 读图
img = cv.imread("../images/huofu.png")
# 灰度化
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 二值化
_, binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
# 边缘检测
edges = cv.Canny(binary, 30, 70)
lines = cv.HoughLinesP(binary, 0.8, np.pi/180, 90, minLineLength = 30, maxLineGap = 10)
for line in lines:
x1, y1, x2, y2 = line[0]
cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 1)
cv.imshow('img', img)
cv.waitKey(0)
cv.destroyAllWindows()
结果展示:
2、霍夫圆变换
(1)原理
圆的方程为:
参数空间为三维:圆心 (a,b) 和半径 r
霍夫圆变换对每个边缘点在 (a,b,r) 空间进行投票,找到峰值点。
由于计算量大,OpenCV 使用 霍夫梯度法(Hough Gradient Method),先利用边缘方向信息缩小搜索范围。
(2)API
circles = cv2.HoughCircles(image, method, dp, minDist,
param1=None, param2=None,
minRadius=0, maxRadius=0)
-
image
:灰度图像(必须是单通道) -
method
:目前只支持cv2.HOUGH_GRADIENT
-
dp
:累加器分辨率与图像分辨率的反比(如dp=1
表示相同分辨率,dp=2
表示一半) -
minDist
:检测到的圆心之间的最小距离 -
param1
:Canny 边缘检测的高阈值(低阈值自动为一半) -
param2
:累加器阈值,越小检测到的圆越多(通常 10~50) -
minRadius
,maxRadius
:圆的最小和最大半径
示例代码:
import cv2 as cv
import numpy as np
# 读图
img = cv.imread("../images/huofu.png")
# 灰度化
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 二值化
_, binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
# 边缘检测
edges = cv.Canny(binary, 30, 70)
# 霍夫圆变换 cv.HoughCircles(edges, method, dp, minDist, param1, param2, minRadius, maxRadius)
circles = cv.HoughCircles(edges, cv.HOUGH_GRADIENT, 1, 20, param2=20)
for circle in circles:
x, y, r = circle[0]
x, y, r = np.int_(np.round(x)), np.int_(np.round(y)), np.int_(np.round(r))
cv.circle(img, (x, y), r, (0, 255, 0), 2)
cv.imshow('img', img)
cv.waitKey(0)
cv.destroyAllWindows()
结果展示:
四、图像亮度调整
1、亮度变换
(1)什么是亮度?
亮度(Brightness)是指图像中像素的灰度值或颜色强度。对于灰度图像,亮度就是像素值(0~255);对于彩色图像,通常是对每个颜色通道(如 R、G、B)分别处理。
2、线性变换
代码示例:
import cv2 as cv
# 读图
cat = cv.imread('../images/cat1.png')
# 线性变换
dst = cv.addWeighted(cat, 1, cat, 0, -50)
cv.imshow('cat', cat)
cv.imshow('dst', dst)
cv.waitKey(0)
cv.destroyAllWindows()
结果展示:
3、直接像素值修改
这里我们采用一个有趣的滑动条来实现我们直观的像素值修改。
示例代码:
import cv2 as cv
import numpy as np
# 读图
img = cv.imread("../images/cat1.png")
# 创建窗口 用于实现滑条
window_name = "slide"
cv.namedWindow(window_name)
def change(p):
x = p/256*511-255
dst = np.uint8(np.clip(img + x, 0, 255))
cv.imshow("dst", dst)
print(x)
# 创建滑条
initial_value = 100
change(initial_value)
cv.createTrackbar("add_p", window_name, initial_value, 255, change)
cv.waitKey(0)
cv.destroyAllWindows()
结果展示:
五、形态学变换
形态学变换主要作用于二值图像(黑白图像),但也适用于灰度图像。其核心思想是使用一个称为结构元素(Structuring Element) 的小矩阵(也叫核)在图像上滑动,根据核的形状和位置对图像像素进行变换。
1、核
核(kernel)其实就是一个小区域,通常为3*3、5*5、7*7大小,有着其自己的结构,比如矩形结构、椭圆结构、十字形结构,通过不同的结构可以对不同特征的图像进行形态学操作的处理。
import cv2 as cv
import numpy as np
car = cv.imread("../images/cat1.png", cv.IMREAD_GRAYSCALE)
# 定义核
kernel = np.ones((5, 5), np.uint8)
2、腐蚀
-
原理:将图像中的前景物体“变小”,消除细小的白色噪点,分离相连的物体。
-
规则:只有当结构元素完全覆盖在前景区域(白色)时,中心像素才保留为白色,否则变为黑色。
-
效果:边界被“腐蚀”掉一层。
适用于去除小的亮斑噪声。
# 腐蚀
erosion = cv.erode(car, kernel, iterations = 1)
cv.imshow("car", car)
cv.imshow("erosion", erosion)
3、膨胀
-
原理:将图像中的前景物体“变大”,填充小的空洞,连接邻近物体。
-
规则:只要结构元素与前景区域有重叠,中心像素就变为白色。
-
效果:边界向外“膨胀”。
常用于连接断裂的边缘。
# 膨胀
dilation = cv.dilate(car, kernel, iterations = 1)
cv.imshow("dilation", dilation)
4、其他运算
(1)开运算(Opening)
-
定义:先腐蚀,再膨胀。
-
公式:
opening = dilation(erode(image))
-
作用:去除小的噪点,同时保持大物体的形状基本不变。
适合去除图像中的小亮点噪声。
# 开运算
opening = cv.morphologyEx(car, cv.MORPH_OPEN, kernel)
cv.imshow("opening", opening)
(2)闭运算(Closing)
-
定义:先膨胀,再腐蚀。
-
公式:
closing = erode(dilate(image))
-
作用:填充物体内部的小孔或裂缝,连接邻近的物体。
适合去除小的暗点(空洞)。
# 闭运算
closing = cv.morphologyEx(car, cv.MORPH_CLOSE, kernel)
cv.imshow("closing", closing)
(3)形态学梯度(Morphological Gradient)
-
定义:膨胀图减去腐蚀图。
-
公式:
gradient = dilate(image) - erode(image)
-
作用:得到物体的轮廓(边缘)。
# 形态学梯度 膨胀与腐蚀的差
gradient = cv.morphologyEx(car, cv.MORPH_GRADIENT, kernel)
cv.imshow("gradient", gradient)
(4)顶帽(Top Hat)
-
定义:原图减去开运算结果。
-
公式:
tophat = image - opening
-
作用:突出比结构元素小的亮区域(如细小文字、亮点)。
# 顶帽运算 原图与开运算的差
tophat = cv.morphologyEx(car, cv.MORPH_TOPHAT, kernel)
cv.imshow("tophat", tophat)
(5)黑帽(Black Hat)
-
定义:闭运算结果减去原图。
-
公式:
blackhat = closing - image
-
作用:突出比结构元素小的暗区域(如小孔、暗斑)。
# 黑帽运算 闭运算与原图之间的差
blackhat = cv.morphologyEx(car, cv.MORPH_BLACKHAT, kernel)
cv.imshow("blackhat", blackhat)
cv.imshow("car", car)
六、总结
撒花!!以上内容就是我们opencv剩下的全部内容了,掌握了这些我们就已经可以实现大部分简单的图像处理操作了!有任何问题都可以在评论区留下评论或者私信作者哦!