系列文章:
图像认知与OpenCV | Day1:图像基础-CSDN博客
图像认知与OpenCV | Day2:图像预处理(1)-CSDN博客
图像认知与OpenCV | Day3:图像预处理(2)-CSDN博客
图像认知与OpenCV | Day4:图像预处理(3)-CSDN博客
目录
[3.1 自适应直方图均衡化](#3.1 自适应直方图均衡化)
[3.2 对比度受限的自适应直方图均衡化](#3.2 对比度受限的自适应直方图均衡化)
[2.1 平方差匹配](#2.1 平方差匹配)
[2.2 归一化平方差匹配](#2.2 归一化平方差匹配)
[2.3 相关匹配](#2.3 相关匹配)
[2.4 归一化相关匹配](#2.4 归一化相关匹配)
[2.5 相关系数匹配](#2.5 相关系数匹配)
[2.6 归一化相关系数匹配](#2.6 归一化相关系数匹配)
一、直方图均衡化
1、概念
-
直方图是统计图像像素分布的图表,横坐标为像素值,纵坐标为该像素值出现的频率。
-
作用:分析图像亮度分布,判断是否偏暗、偏亮或对比度低。

2、绘制直方图
hist=cv2.calcHist(images, channels, mask, histSize, ranges)
参数解释:
images
:输入图像列表,可以是一幅或多幅图像(通常是灰度图像或者彩色图像的各个通道)。
channels
:一个包含整数的列表,指示在每个图像上计算直方图的通道编号。如果输入图像是灰度图,它的值就是 [0];如果是彩色图像的话,传入的参数可以是 [0],[1],[2] 它们分别对应着通道 B,G,R。
mask
(可选):一个与输入图像尺寸相同的二值掩模图像,其中非零元素标记了参与直方图计算的区域,None为全部计算。
histSize
:一个整数列表,也就是直方图的区间个数(BIN 的数目)。用中括号括起来,例如:[256]。
ranges
:每维数据的取值范围,它是一个二维列表,每一维对应一个通道的最小值和最大值,例如对灰度图像可能是[0, 256]
。
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)获取直方图的最小值、最大值及其对应最小值的位置索引、最大值的位置索
cv2.line(img, pt1, pt2, color, thickness)参数解释:
img:原始图像,即要在上面画线的numpy数组(一般为uint8类型)。
pt1 和 pt2 :分别为线段的起点和终点坐标,它们都是元组类型,例如
(x1, y1)
和(x2, y2)
分别代表线段两端的横纵坐标。color :线段的颜色,通常是一个包含三个元素的元组
(B, G, R)
表示BGR色彩空间的像素值,也可以是灰度图像的一个整数值。thickness:线段的宽度,默认值是1,如果设置为负数,则线宽会被填充。
示例代码:
import cv2 as cv
import numpy as np
# 读图
bg = cv.imread("../images/bg.png")
# 创建黑图,绘制直方图
black = np.zeros([256, 256, 3], np.uint8)
# 统计像素
hist = cv.calcHist(bg, [1], None, [256], [0, 256])
# 获取直方图的最小值、最大值及其对应最小值的位置索引、最大值的位置索引
minval, maxval, minloc, maxloc = cv.minMaxLoc(hist)
# print(minval, maxval, minloc, maxloc)
# 定义直方图的高
h_hist = np.int32(256)
# 循环拿像素的个数
for i in range(256):
l = int(hist[i] * h_hist / maxval)
point1 = (i, 256-l)
point2 = (i, 256)
cv.line(black, point1, point2, (0, 255, 0), 1)
cv.imshow("black", black)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果:

3、直方图均衡化
直方图均衡化作用:
-
增强对比度
-
提高图像质量

3.1 自适应直方图均衡化
该方法适用于图像的灰度分布不均匀,且灰度分布集中在更窄的范围,图像的细节不够清晰且对比度较低的情况,然而,传统的直方图均衡化方法会引入噪声,并导致图像中出现过度增强的区域。这是因为直方图均衡化方法没有考虑到图像的局部特征和全局对比度的差异。
API:
dst = cv.equalizeHist(imgGray)
imgGray为需要直方图均衡化的灰度图,返回值为处理后的图
示例代码:
python
import cv2 as cv
# 读图
img = cv.imread("../images/zhifang.png", cv.IMREAD_GRAYSCALE)
# 直方图均衡化
dst = cv.equalizeHist(img)
cv.imshow("img", img)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()
运行后两张图的对比:

3.2 对比度受限的自适应直方图均衡化
很明显,因为全局调整亮度和对比度的原因,脸部太亮,大部分细节都丢失了。自适应均衡化就是用来解决这一问题的:它在每一个小区域内(默认8×8)进行直方图均衡化。当然,如果有噪点的话,噪点会被放大,需要对小区域内的对比度进行了限制,所以这个算法全称叫:对比度受限的自适应直方图均衡化。
API:
clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)
参数解释:
clipLimit(可选):对比度限制参数,用于控制直方图均衡化过程中对比度增强的程度。如果设置一个大于1的值(如2.0或4.0),CLAHE会限制对比度增强的最大程度,避免过度放大噪声。如果不设置,OpenCV会使用一个默认值。
tileGridSize(可选):图像分块的大小,通常是一个包含两个整数的元组,如
(8, 8)
,表示将图像划分成8x8的小块进行独立的直方图均衡化处理。分块大小的选择会影响到CLAHE的效果以及处理速度。
创建CLAHE对象后,可以使用 .apply()
方法对图像进行CLAHE处理:
img=clahe.apply(image)
参数解释:
image:要均衡化的图像。
img均衡后的图像
示例代码:
python
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()方法
dst = clahe.apply(img)
cv.imshow("src", img)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()
运行后图片对比:

二、模版匹配
1、概念
模板匹配就是用模板图(通常是一个小图)在目标图像(通常是一个比模板图大的图片)中不断的滑动比较,通过某种比较方法来判断是否匹配成功,找到模板图所在的位置。
-
不会有边缘填充。
-
类似于卷积,滑动比较,挨个比较象素。
-
返回结果大小是:目标图大小-模板图大小-1。
2、匹配方法
API:
res=cv2.matchTemplate(image, templ, method)
参数解释:
image:原图像,这是一个灰度图像或彩色图像(在这种情况下,匹配将在每个通道上独立进行)。
templ:模板图像,也是灰度图像或与原图像相同通道数的彩色图像。
method:匹配方法,可以是以下之一:
cv2.TM_CCOEFF
cv2.TM_CCOEFF_NORMED
cv2.TM_CCORR
cv2.TM_CCORR_NORMED
cv2.TM_SQDIFF
cv2.TM_SQDIFF_NORMED
这些方法决定了如何度量模板图像与原图像子窗口之间的相似度。
返回值res
函数在完成图像模板匹配后返回一个结果矩阵,这个矩阵的大小与原图像相同。矩阵的每个元素表示原图像中相应位置与模板图像匹配的相似度。
匹配方法不同,返回矩阵的值的含义也会有所区别。以下是几种常用的匹配方法及其返回值含义:
-
cv2.TM_SQDIFF
或cv2.TM_SQDIFF_NORMED
:返回值越接近0,表示匹配程度越好。最小值对应的最佳匹配位置。
-
cv2.TM_CCORR
或cv2.TM_CCORR_NORMED
:返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。
-
cv2.TM_CCOEFF
或cv2.TM_CCOEFF_NORMED
:返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。
2.1 平方差匹配
cv2.TM_SQDIFF
以模板图与目标图所对应的像素值使用平方差公式来计算,其结果越小,代表匹配程度越高,计算过程举例如下。
注意:模板匹配过程皆不需要边缘填充,直接从目标图像的左上角开始计算。

2.2 归一化平方差匹配
cv2.TM_SQDIFF_NORMED
与平方差匹配类似,只不过需要将值统一到0到1,计算结果越小,代表匹配程度越高,计算过程举例如下。

2.3 相关匹配
cv2.TM_CCORR
使用对应像素的乘积进行匹配,乘积的结果越大其匹配程度越高,计算过程举例如下。

2.4 归一化相关匹配
cv2.TM_CCORR_NORMED
与相关匹配类似,只不过是将其值统一到0到1之间,值越大,代表匹配程度越高,计算过程举例如下。

2.5 相关系数匹配
cv2.TM_CCOEFF
需要先计算模板与目标图像的均值,然后通过每个像素与均值之间的差的乘积再求和来表示其匹配程度,1表示完美的匹配,-1表示最差的匹配,计算过程举例如下。

2.6 归一化相关系数匹配
cv2.TM_CCOEFF_NORMED
也是将相关系数匹配的结果统一到0到1之间,值越接近1代表匹配程度越高,计算过程举例如下。

示例代码:
python
import numpy as np
# 读图
img = cv.imread("../images/game.png")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
temp = cv.imread("../images/temp.png")
gray_temp = cv.cvtColor(temp, cv.COLOR_BGR2GRAY)
h, w = temp.shape[:2]
# 模板匹配 拿到匹配结果矩阵
res = cv.matchTemplate(gray_temp, gray, cv.TM_CCOEFF_NORMED)
# 设置阈值
thresh = 0.8
# 获取匹配上的结果的索引
loc = np.where(res >= thresh)
# 解包,拿到成对的x y索引
for i in zip(*loc):
x, y = i[1], i[0]
cv.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 1)
cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()
三、霍夫变换
1、概念
霍夫变换 是图像处理的一种技术,主要用于检测图像中的直线、圆等几何形状。基本思想就是将图像空间中的点映射到参数空间 中,通过在参数空间中寻找累计最大值实现对特定形状的检测。

2、霍夫直线变换
原理:直角坐标系下直线(y = kx + b)可转化为(b = -kx + y),直角坐标系中的直线对应霍夫空间中的点,反之亦然。为解决垂直于 x 轴直线无法映射问题,引入极坐标系,极坐标中的点对应霍夫空间中的线,霍夫空间中的点对应极坐标中的直线。
API
lines=cv2.HoughLines(image, rho, theta, threshold)
参数解释:
image
:输入图像,通常为二值图像,其中白点表示边缘点,黑点为背景。
rho
:r的精度,以像素为单位,表示霍夫空间中每一步的距离增量, 值越大,考虑越多的线。
theta
:角度θ的精度,通常以弧度为单位,表示霍夫空间中每一步的角度增量。值越小,考虑越多的线。
threshold
:累加数阈值,只有累积投票数超过这个阈值的候选直线才会被返回。返回值:
cv2.HoughLines
函数返回一个二维数组,每一行代表一条直线在霍夫空间中的参数(rho, theta)
。
示例代码:
python
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)
# print(dst)
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("old",img)
cv.waitKey(0)
cv.destroyAllWindows()
3、统计概率霍夫直线变换
原理:改进的霍夫变换,在获取直线后检测原图中直线上的点,获取端点坐标,通过直线长度与最短长度阈值比较决定是否保留。
API
lines=cv2.HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=0, maxLineGap=0)
参数解释:
image
:输入图像,通常为二值图像,其中白点表示边缘点,黑点为背景。
rho
:极径分辨率,以像素为单位,表示极坐标系中的距离分辨率。
theta
:极角分辨率,以弧度为单位,表示极坐标系中角度的分辨率。
threshold
:阈值,用于过滤掉弱检测结果,只有累计投票数超过这个阈值的直线才会被返回。
lines
(可选):一个可初始化的输出数组,用于存储检测到的直线参数。
minLineLength
(可选):最短长度阈值,比这个长度短的线会被排除。
maxLineGap
(可选):同一直线两点之间的最大距离。当霍夫变换检测到一系列接近直角的线段时,这些线段可能是同一直线的不同部分。maxLineGap
参数指定了在考虑这些线段属于同一直线时,它们之间最大可接受的像素间隔。返回值lines:
cv2.HoughLinesP
函数返回一个二维数组,每个元素是一个包含4个元素的数组,分别表示每条直线的起始点和结束点在图像中的坐标(x1, y1, x2, y2)
示例代码:
python
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.HoughLinesP(edges, 0.8, np.pi/180, 90, minLineLength=50, maxLineGap=10)
for line in lines:
x1, y1, x2, y2 = line[0]
cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()
4、霍夫圆变换
原理 :从图像中找出潜在圆形结构,用(x_center, y_center, r)
表示,一般使用霍夫梯度法减少计算量。
API
circles=cv2.HoughCircles(image, method, dp, minDist, param1, param2)
参数解释:
image
:输入图像,通常是灰度图像。
method
:使用的霍夫变换方法:霍夫梯度法,可以是cv2.HOUGH_GRADIENT
,这是唯一在OpenCV中用于圆检测的方法。
dp
:累加器分辨率与输入图像分辨率之间的降采样比率,用于加速运算但不影响准确性。设置为1表示霍夫梯度法中累加器图像的分辨率与原图一致
minDist
:检测到的圆心之间的最小允许距离,以像素为单位。在霍夫变换检测圆的过程中,可能会检测到许多潜在的圆心。minDist
参数就是为了过滤掉过于接近的圆检测结果,避免检测结果过于密集。当你设置一个较小的minDist
值时,算法会尝试找出尽可能多的圆,即使是彼此靠得很近的圆也可能都被检测出来。相反,当你设置一个较大的minDist
值时,算法会倾向于只检测那些彼此间存在一定距离的独立的圆。
param1
和param2
:这两个参数是在使用cv2.HOUGH_GRADIENT
方法时的特定参数,分别为:
param1
(可选):阈值1,决定边缘强度的阈值。
param2
:阈值2,控制圆心识别的精确度。较大的该值会使得检测更严格的圆。param2
通常被称为圆心累积概率的阈值。在使用霍夫梯度方法时,param2
设置的是累加器阈值,它决定了哪些候选圆点集合被认为是有效的圆。较高的param2
值意味着对圆的检测更严格,只有在累加器中积累了足够高的响应值才认为是真实的圆;较低的param2
值则会降低检测的门槛,可能会检测到更多潜在的圆,但也可能包含更多的误检结果。返回值:
cv2.HoughCircles
返回一个二维numpy数组,包含了所有满足条件的圆的参数。
示例代码:
python
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)
# 霍夫圆变换
circles = cv.HoughCircles(edges, cv.HOUGH_GRADIENT, 1, 20, param2=20)
# print(circles)
for circle in circles:
x, y, r = circle[0]
# print(x,y,r)
x, y, r = np.int_(np.round(x)), np.int_(np.around(y)), np.int_(r)
# print(x,y,r)
cv.circle(img, (x, y), r, (0, 255, 0), 2)
cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()
四、图像亮度变换
1、亮度变换
对比度调整:图像暗处像素强度变低,图像亮处像素强度变高,从而拉大中间某个区域范围的显示精度。
亮度调整:图像像素强度整体变高或者变低。

上图中,a把亮度调高,就是图片中的所有像素值加上了一个固定值;b把亮度调低,就是图片中的所有像素值减去了一个固定值;c增大像素对比度(白的地方更白,黑的地方更黑);d减小像素对比度(整幅图都趋于一个颜色);
OpenCV调整图像对比度和亮度时,公式为:。但是不能浅显的讲
是控制对比度,
是控制亮度的。
对比度:需要通过、
一起控制。
亮度:通过控制。
2、线性变换
使用 cv2.addWeighted()
函数,可以对图像的像素值进行加权平均,进而改变图像的整体亮度。亮度增益可以通过向每个像素值添加一个正值来实现。
cv2.addWeighted(src1, alpha, src2, beta, gamma)
参数解释:
src1
:第一张输入图像,它将被赋予权重alpha
。
alpha
:第一个输入图像的权重。
src2
:第二张输入图像,它将被赋予权重beta
。
beta
:第二个输入图像的权重。
gamma
:一个标量,将被添加到权重求和的结果上,可用于调整总体亮度。计算公式为: dst = src1 * alpha + src2 * beta + gamma
示例代码:
python
import cv2 as cv
# 读图
cat = cv.imread("../images/cat1.png")
# 线性变换
dst = cv.addWeighted(cat, 1, cat, 0, 20)
cv.imshow("cat", cat)
cv.imshow("dst", dst)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果:

3、直接像素值修改
如果只需要增加或减少固定的亮度值,可以直接遍历图像像素并对每个像素值进行加减操作。
使用的API:
numpy.clip(a, a_min, a_max)
用于对数组中的元素进行限定,将超出指定范围的元素值截断至指定的最小值和最大值之间
a
:输入数组。
a_min
:指定的最小值,数组中所有小于a_min
的元素将被替换为a_min
。
a_max
:指定的最大值,数组中所有大于a_max
的元素将被替换为a_max
。
代码示例:
python
import cv2 as cv
import numpy as np
# 读图
cat = cv.imread("../images/cat1.png")
# 创建窗口 用于实现滑条
window_name = "slide"
cv.namedWindow(window_name)
img = cv.imread("../images/cat1.png")
def change(p):
x = p/256*511-255
dst = np.uint8(np.clip(img + x, 0, 255, ))
cv.imshow("dst", dst)
pass
# 创建滑动条
initial_value = 100
change(initial_value)
cv.createTrackbar("add_p", window_name, initial_value, 255, change)
五、形态学变换
形态学变换(Morphological Transformations)是一种基于形状的简单变换,它的处理对象通常是二值化图像。形态学变换有两个输入,一个输出:输入为原图像、核(结构化元素),输出为形态学变换后的图像。其基本操作有腐蚀和膨胀,这两种操作是相反的,即较亮的像素会被腐蚀和膨胀。
1、核
自适应二值化中,我们已经接触过核了,还记得吗?就是那个在原图中不断滑动计算的3*3的小区域,那其实就是一个3*3的核。
核(kernel)其实就是一个小区域,通常为3*3、5*5、7*7大小,有着其自己的结构,比如矩形结构、椭圆结构、十字形结构,如下图所示。通过不同的结构可以对不同特征的图像进行形态学操作的处理。

2、操作
操作 | 作用 | 特点 |
---|---|---|
腐蚀(Erode) | 去除边界白色像素,缩小前景 | 消除小噪点,细化边缘 |
膨胀(Dilate) | 增加边界白色像素,扩大前景 | 填充小孔洞,连接断裂 |
开运算(Open) | 先腐蚀后膨胀 | 去噪点,分离物体 |
闭运算(Close) | 先膨胀后腐蚀 | 填充孔洞,连接裂缝 |
礼帽(Top-hat) | 原图 - 开运算结果 | 提取比背景亮的局部区域 |
黑帽(Black-hat) | 闭运算结果 - 原图 | 提取比背景暗的局部区域 |
形态学梯度 | 膨胀图 - 腐蚀图 | 提取边缘轮廓 |
API | cv2.morphologyEx() ,需指定核(kernel)和操作类型 |
示例代码:
python
import cv2 as cv
import numpy as np
car = cv.imread("../images/car.png", cv.IMREAD_GRAYSCALE)
# 定义核
kernel = np.ones((5, 5), np.uint8)
# 腐蚀
erosion = cv.erode(car, kernel, iterations=1)
# 膨胀
dilation = cv.dilate(car, kernel, iterations=1)
# 开操作
opening = cv.morphologyEx(car, cv.MORPH_OPEN, kernel)
# 闭操作
closing = cv.morphologyEx(car, cv.MORPH_CLOSE, kernel)
# 礼帽
tophat = cv.morphologyEx(car, cv.MORPH_TOPHAT, kernel)
# 黑帽
blackhat = cv.morphologyEx(car, cv.MORPH_BLACKHAT, kernel)
cv.imshow("car", car)
cv.imshow("erosion", erosion)
cv.imshow("dilation", dilation)
cv.imshow("opening", opening)
cv.imshow("closing", closing)
cv.imshow("tophat", tophat)
cv.imshow("blackhat", blackhat)
cv.waitKey(0)
cv.destroyAllWindows()