初识opencv05——图像预处理4

发布于:2025-07-29 ⋅ 阅读:(17) ⋅ 点赞:(0)

初识opencv

初识opencv01——基本api操作
初识opencv02——图像预处理1
初识opencv03——图像预处理2
初识opencv04——图像预处理3



一、直方图

直方图是对数据进行统计的一种方法,并且将统计值组织到一系列实现定义好的 bin 当中。其中, bin 为直方图中经常用到的一个概念,可以译为 “直条” 或 “组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征。

1.1 绘制直方图

这里以反映图像像素分布的统计图为例,横坐标就是图像像素的取值,纵坐标是该像素的个数。也就是对一张图像中不同像素值的像素个数的统计。

这里是示例代码:

import cv2 as cv
import numpy as np

# 读图
img=cv.imread('./imageBase/images/bg.png')
# 直方图
hist_b=cv.calcHist([img],[0],None,[256],[0,256])
hist_g=cv.calcHist([img],[1],None,[256],[0,256])
hist_r=cv.calcHist([img],[2],None,[256],[0,256])
# 创建黑色图像,用于绘制直方图
histb_img=np.zeros((256,256,3),dtype=np.uint8)
histg_img=np.zeros((256,256,3),dtype=np.uint8)
histr_img=np.zeros((256,256,3),dtype=np.uint8)
# 获取直方图中的y最大值和最小值及其对应的x坐标[列,行]
b_arg=cv.minMaxLoc(hist_b)
g_arg=cv.minMaxLoc(hist_g)
r_arg=cv.minMaxLoc(hist_r)
# print(minval,maxval,minloc,maxloc)
# print(hist[249,0]) 用[行,列]取
# plt.plot(hist)
# plt.show()
h_hist=np.int32(256)
# 遍历256个灰度级,绘制对应的直方图线条
for i in range(256):
    # 计算当前灰度级在图像中的高度,按比例缩放
    l_b=int(hist_b[i].item()*h_hist/b_arg[1])
    # 设置线条的起始点(图像上方)
    point1=(i,256-l_b)
    # 设置线条的终止点(图像底部)
    point2=(i,256)
    # 在直方图图像上绘制垂直线段
    cv.line(histb_img,point1,point2,(255,0,0),1)
    l_g=int(hist_g[i].item()*h_hist/g_arg[1])
    point1=(i,256-l_g)
    point2=(i,256)
    cv.line(histg_img,point1,point2,(0,255,0),1)
    l_r=int(hist_r[i].item()*h_hist/r_arg[1])
    point1=(i,256-l_r)
    point2=(i,256)
    cv.line(histr_img,point1,point2,(0,0,255),1)

cv.imshow('histb_img',histb_img)
cv.imshow('histg_img',histg_img)
cv.imshow('histr_img',histr_img)

# 等待用户按键操作
cv.waitKey(0)
# 关闭所有创建的窗口
cv.destroyAllWindows()

运行结果如下:

原图

蓝色通道 绿色通道 红色通道

其中用到的api有:

  • 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]

如果是统计灰度图的像素分布,统计返回值可能是hist 是一个256*1的数组,数组中的每个值表示图像中对应灰度等级的像素计数。

1.2 直方图均衡化

一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。

如果一幅图像整体很亮,那所有的像素值的取值个数应该都会很高,所以应该把它的直方图做一个横向拉伸,就可以扩大图像像素值的分布范围,提高图像的对比度

通俗的讲,就是遍历图像的像素统计出灰度值的个数、比例与累计比例,并重新映射到0-255范围(也可以是其他范围)内。

以下面这张图为例:
在这里插入图片描述
在这里插入图片描述
可以看出直方图均衡化的作用有:

  • 增强对比度
  • 提高图像质量

1.2.1 直方图均衡化

直方图均衡化(Adaptive Histogram Equalization, AHE),通过调整图像像素值的分布,使得图像的对比度和亮度得到改善。

具体过程如下所示:
假设有一个3*3的图像,其灰度图的像素值如下图所示,现在我们要对其进行直方图均衡化,

首先就是统计其每个像素值的个数、比例以及其累计比例。接下来我们就要进行计算,就是将要缩放的范围(通常是缩放到0-255,所以就是255-0)乘以累计比例,得到新的像素值,并将新的像素值放到对应的位置上,这样就完成了最基本的直方图均衡化的过程。如下图所示。

该方法适用于图像的灰度分布不均匀,且灰度分布集中在更窄的范围,图像的细节不够清晰且对比度较低的情况,然而,传统的直方图均衡化方法会引入噪声,并导致图像中出现过度增强的区域。这是因为直方图均衡化方法没有考虑到图像的局部特征和全局对比度的差异。结果如下:

在这里插入图片描述

原图(左)均衡化(右)

1.2.2 对比度受限的直方图均衡化

很明显,因为全局调整亮度和对比度的原因,脸部太亮,大部分细节都丢失了。自适应均衡化就是用来解决这一问题的:它在每一个小区域内(默认8×8)进行直方图均衡化。当然,如果有噪点的话,噪点会被放大,需要对小区域内的对比度进行了限制,所以这个算法全称叫:对比度受限的自适应直方图均衡化(Contrast Limited Adaptive Histogram Equalization, CLAHE)。

其主要步骤为:

  1. 图像分块(Tiling)
    • 图像首先被划分为多个不重叠的小块(tiles)。这样做的目的是因为在全局直方图均衡化中,单一的直方图无法反映图像各个局部区域的差异性。通过局部处理,AHE能够更好地适应图像内部的不同光照和对比度特性。(tiles
      的 大小默认是 8x8)
  2. 计算子区域直方图
    • 对于每个小块,独立计算其内部像素的灰度直方图。直方图反映了该区域内像素值的分布情况。
  3. 子区域直方图均衡化
    • 对每个小块的直方图执行直方图均衡化操作。这涉及重新分配像素值,以便在整个小块内更均匀地分布。均衡化过程会增加低频像素的数量,减少高频像素的数量,从而提高整个小块的对比度。
  4. 对比度限制(Contrast Limiting)
    • 如果有噪声的话,噪声会被放大。为了防止过大的对比度增强导致噪声放大,出现了限制对比度自适应直方图均衡化(CLAHE)。CLAHE会在直方图均衡化过程中引入一个对比度限制参数。当某一小块的直方图在均衡化后出现极端值时,会对直方图进行平滑处理(使用线性或非线性的钳制函数),确保对比度增强在一个合理的范围内。
  5. 重采样和邻域像素融合
    • 由于小块之间是不重叠的,直接拼接经过均衡化处理的小块会产生明显的边界效应。因此,在CLAHE中通常采用重采样技术来消除这种效应,比如通过双线性插值将相邻小块的均衡化结果进行平滑过渡,使最终图像看起来更为自然和平滑。
  6. 合成输出图像
    • 将所有小块均衡化后的结果整合在一起,得到最终的自适应直方图均衡化后的图像。

示例代码如下:

img=cv.imread('./imageBase/images/zhifang.png',cv.IMREAD_GRAYSCALE)
# 计算直方图
hist_o=cv.calcHist([img],[0],None,[256],[0,256])
# 全局均衡化
img_eq=cv.equalizeHist(img)
hist_eq=cv.calcHist([img_eq],[0],None,[256],[0,256])
# 自适应均衡化
clahe=cv.createCLAHE(clipLimit=2,tileGridSize=(8,8))
img_clahe=clahe.apply(img)
hist_clahe=cv.calcHist([img_clahe],[0],None,[256],[0,256])

cv.imshow('img',img)
cv.imshow('img_eq',img_eq)
cv.imshow('img_clahe',img_clahe)

hist_o_img=np.zeros((256,256,3),dtype=np.uint8)
hist_eq_img=np.zeros((256,256,3),dtype=np.uint8)
hist_clahe_img=np.zeros((256,256,3),dtype=np.uint8)

h_hist=np.int32(256)
for i in range(256):
    l_o=int(hist_o[i].item()*h_hist/hist_o.max())
    point1=(i,256-l_o)
    point2=(i,256)
    cv.line(hist_o_img,point1,point2,(0,255,0),1)

    l_eq=int(hist_eq[i].item()*h_hist/hist_eq.max())
    point1=(i,256-l_eq)
    point2=(i,256)
    cv.line(hist_eq_img,point1,point2,(255,0,0),1)

    l_clahe=int(hist_clahe[i].item()*h_hist/hist_clahe.max())
    point1=(i,256-l_clahe)
    point2=(i,256)
    cv.line(hist_clahe_img,point1,point2,(0,0,255),1)

cv.imshow('hist_o_img',hist_o_img)
cv.imshow('hist_eq_img',hist_eq_img)
cv.imshow('hist_clahe_img',hist_clahe_img)


cv.waitKey(0)
cv.destroyAllWindows()

运行结果如下:

原图 均衡化 对比度受限的均衡化

其中用到的api有:

  • dst = cv.equalizeHist(imgGray)
    imgGray为需要直方图均衡化的灰度图,返回值为处理后的图像
  • clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)
    • clipLimit(可选):对比度限制参数,用于控制直方图均衡化过程中对比度增强的程度。如果设置一个大于1的值(如2.0或4.0),CLAHE会限制对比度增强的最大程度,避免过度放大噪声。如果不设置,OpenCV会使用一个默认值。
    • tileGridSize(可选):图像分块的大小,通常是一个包含两个整数的元组,如(8, 8),表示将图像划分成8x8的小块进行独立的直方图均衡化处理。分块大小的选择会影响到CLAHE的效果以及处理速度。
  • img=clahe.apply(image)
    创建CLAHE对象后,可以使用 .apply() 方法对图像进行CLAHE处理:
    • image:要均衡化的图像。
    • img均衡后的图像

二、模板匹配

模板匹配就是用模板图(通常是一个小图)在目标图像(通常是一个比模板图大的图片)中不断的滑动比较,通过某种比较方法来判断是否匹配成功,找到模板图所在的位置。

  • 不会有边缘填充。

  • 类似于卷积,滑动比较,挨个比较象素。

  • 返回结果是:一个 二维数组(矩阵),大小为(目标图宽-模板图宽+1,目标图高-模板图高+1)。

在这里插入图片描述
通常来讲有以下几种匹配方法:

2.1 平方差匹配(cv2.TM_SQDIFF)及其归一化匹配(cv2.TM_SQDIFF_NORMED)

以模板图与目标图所对应的像素值使用平方差公式来计算,其结果越小,代表匹配程度越高。归一化只不过需要将值统一到0到1,计算结果越小,代表匹配程度越高。计算过程举例如下。

模板匹配过程皆不需要边缘填充,直接从目标图像的左上角开始计算。

平方差匹配 归一化平方差

2.2 相关匹配(cv2.TM_CCORR)及其归一化匹配(cv2.TM_CCORR_NORMED)

使用对应像素的乘积进行匹配,乘积的结果越大其匹配程度越高。归一化只不过是将其值统一到0到1之间,值越大,代表匹配程度越高。计算过程举例如下。

相关匹配 归一化相关

2.3 相关系数匹配(cv2.TM_CCOEFF)及其归一化匹配(cv2.TM_CCOEFF_NORMED)

需要先计算模板与目标图像的均值,然后通过每个像素与均值之间的差的乘积再求和来表示其匹配程度,1表示完美的匹配,-1表示最差的匹配。其归一化处理也是将相关系数匹配的结果统一到0到1之间,值越接近1代表匹配程度越高。

相关匹配系数 归一化相关系数

2.4 匹配结果处理

示例代码如下

img=cv.imread('./imageBase/images/game.png')
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
template=cv.imread('./imageBase/images/temp.png',cv.IMREAD_GRAYSCALE)
# 模板匹配
res=cv.matchTemplate(gray,template,cv.TM_CCOEFF_NORMED)
h,w=template.shape
# 筛选匹配结果
threshold=0.8
loc=np.where(res>=threshold) # 第一行为行索引,第二行为列索引
# 结果处理

for pt in zip(*loc):
    # 筛选结果转为图像坐标
    position=(pt[1],pt[0])
    cv.rectangle(img,position,(position[0]+w,position[1]+h),(0,0,255),1)


cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()
原图 模板 匹配结果
在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
其中用到的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 函数在完成图像模板匹配后返回一个结果矩阵,这个矩阵的大小与原图像不相同,若为灰度图则大小为(目标图宽-模板图宽+1,目标图高-模板图高+1)。矩阵的每个元素表示原图像中相应位置与模板图像匹配的相似度。

  • loc=np.where(array > 0.8) #loc包含array中所有大于0.8的元素索引的数组
    np.where(condition) 是 NumPy 的一个函数,当条件为真时,返回满足条件的元素的索引。
  • *zip(loc)
    • *loc 是解包操作,将 loc 中的多个数组拆开,作为单独的参数传递给 zip
    • zip 将这些数组按元素一一配对,生成一个迭代器,每个元素是一个元组,表示一个坐标点。

三、霍夫变换

霍夫变换是图像处理的一种技术,主要用于检测图像中的直线、圆等几何形状。基本思想就是将图像空间中的点映射到参数空间中,通过在参数空间中寻找累计最大值实现对特定形状的检测。

3.1 标准霍夫变换

从下图中我们可以看出,在直角坐标系下的一个直线,在变换后的空间中仅仅表示为一点,对于变换后的空间,我们称其为霍夫空间,也就是参数空间。而类似的,霍夫空间中的一条直线也对应了直角坐标系中的一个点。

那么对于一个二值化后的图形来说,其中的每一个目标像素点(这里假设目标像素点为白色像素点)都对应了霍夫空间的一条直线,当霍夫空间中有两条直线相交时,就代表了直角坐标系中某两个点所构成的直线。而当霍夫空间中有很多条线相交于一点时,说明直角坐标系中有很多点能构成一条直线,也就意味着这些点共线,因此我们就可以通过检测霍夫空间中有最多直线相交的交点来找到直角坐标系中的直线。

然而对于x=1这种直线(垂直于x轴)来说,y已经不存在了,斜率无穷大,无法映射到霍夫空间中去,那么就没办法使用上面的方法进行检测了,为了解决这个问题,我们就将直角坐标系转化为极坐标系,然后通过极坐标系与霍夫空间进行相互转化。

直线的极坐标表达式为:
ρ = s i n θ ∗ y + c o s θ ∗ x ρ=sinθ*y+cosθ*x ρ=sinθy+cosθx
将其中y与x视为参数,而ρ与θ视为变量,即完成了霍夫空间极坐标系转化。
可以通过将k、b用极坐标表示来推导得出,这里不做赘述
ρ = s i n θ ∗ y 1 + c o s θ ∗ x 1 ρ=sinθ*y_1+cosθ*x_1 ρ=sinθy1+cosθx1

这里给出示例代码:

img=cv.imread('./imageBase/images/huofu.png')
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
_,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
edges=cv.Canny(binary,30,70)
lines=cv.HoughLines(edges,0.8,np.pi/180,90)
for line in lines:
    rho,theta=line[0]
    cos_theta=np.cos(theta)
    sin_theta=np.sin(theta)
    x1,x2=0,img.shape[1]
    y1=int((rho-x1*cos_theta)/sin_theta)
    y2=int((rho-x2*cos_theta)/sin_theta)
    cv.line(img,(x1,y1),(x2,y2),(0,0,255),2)

cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()

运行结果如下:

在这里插入图片描述 在这里插入图片描述
原图 形状检测(直线)

其中用到的api有:

  • lines=cv2.HoughLines(image, rho, theta, threshold)
    参数含义如下:
  • image:输入图像,通常为二值图像,其中白点表示边缘点,黑点为背景。
  • rho:r的精度,以像素为单位,表示霍夫空间中每一步的距离增量, 值越大,考虑越多的线。
  • theta:角度θ的精度,通常以弧度为单位,表示霍夫空间中每一步的角度增量。值越小,考虑越多的线。
  • threshold:累加数阈值,只有累积投票数超过这个阈值的候选直线才会被返回。

返回值:cv2.HoughLines 函数返回一个二维数组,每一行代表一条直线在霍夫空间中的参数 (rho, theta)

3.2 统计概率霍夫变换

前面的方法又称为标准霍夫变换,它会计算图像中的每一个点,计算量比较大,另外它得到的是整一条线(r和θ),并不知道原图中直线的端点。所以提出了统计概率霍夫直线变换(Probabilistic Hough Transform),是一种改进的霍夫变换,它在获取到直线之后,会检测原图中在该直线上的点,并获取到两侧的端点坐标,然后通过两个点的坐标来计算该直线的长度,通过直线长度与最短长度阈值的比较来决定该直线要不要被保留。

这里给出一份示例代码:

img=cv.imread('./imageBase/images/huofu.png')
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
_,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
edges=cv.Canny(binary,30,70)
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()

运行结果如下:

在这里插入图片描述 在这里插入图片描述
原图 形状检测(直线)

其中用到的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)。

3.3 霍夫圆变换

霍夫圆变换跟直线变换类似,它可以从图像中找出潜在的圆形结构,并返回它们的中心坐标和半径。只不过线是用(r,θ)表示,圆是用(x_center,y_center,r)来表示,从二维变成了三维,数据量变大了很多;所以一般使用霍夫梯度法减少计算量。

这里给出示例代码:

img=cv.imread('./imageBase/images/huofu.png')
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
_,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
edges=cv.Canny(binary,30,70)
circles=cv.HoughCircles(edges,cv.HOUGH_GRADIENT,1,20,param2=20)
for circle in circles:
    x,y,r=np.int32(circle[0])
    cv.circle(img,(x,y),r,(0,255,0),2)

cv.imshow("img",img)
cv.waitKey(0)
cv.destroyAllWindows()

运行结果如下:

在这里插入图片描述 在这里插入图片描述
原图 形状检测(圆)

其中用到的api有:

  • circles=cv2.HoughCircles(image, method, dp, minDist, param1, param2)
  • image:输入图像,通常是灰度图像。
  • method:使用的霍夫变换方法:霍夫梯度法,可以是 cv2.HOUGH_GRADIENT,这是唯一在OpenCV中用于圆检测的方法。
  • dp:累加器分辨率与输入图像分辨率之间的降采样比率,用于加速运算但不影响准确性。设置为1表示霍夫梯度法中累加器图像的分辨率与原图一致
  • minDist:检测到的圆心之间的最小允许距离,以像素为单位。在霍夫变换检测圆的过程中,可能会检测到许多潜在的圆心。minDist 参数就是为了过滤掉过于接近的圆检测结果,避免检测结果过于密集。当你设置一个较小的 minDist 值时,算法会尝试找出尽可能多的圆,即使是彼此靠得很近的圆也可能都被检测出来。相反,当你设置一个较大的 minDist 值时,算法会倾向于只检测那些彼此间存在一定距离的独立的圆。
  • 返回值:cv2.HoughCircles 返回一个二维numpy数组,包含了所有满足条件的圆的参数。
  • param1param2:这两个参数是在使用 cv2.HOUGH_GRADIENT 方法时的特定参数,分别为:
    • param1(可选):阈值1,决定边缘强度的阈值。
    • param2:阈值2,控制圆心识别的精确度。较大的该值会使得检测更严格的圆。param2 通常被称为圆心累积概率的阈值。在使用霍夫梯度方法时,param2 设置的是累加器阈值,它决定了哪些候选圆点集合被认为是有效的圆。较高的
      param2 值意味着对圆的检测更严格,只有在累加器中积累了足够高的响应值才认为是真实的圆;较低的 param2
      值则会降低检测的门槛,可能会检测到更多潜在的圆,但也可能包含更多的误检结果。

四、形态学变换

形态学变换(Morphological Transformations)是一种基于形状的简单变换,它的处理对象通常是二值化图像。形态学变换有两个输入,一个输出:输入为原图像、核(结构化元素),输出为形态学变换后的图像。其基本操作有腐蚀和膨胀,这两种操作是相反的,即较亮的像素会被腐蚀和膨胀。下面我们来说一下核、腐蚀与膨胀的概念。

4.1 核

核(kernel)其实就是一个小区域,通常为3*3、5*5、7*7大小,有着其自己的结构,比如矩形结构、椭圆结构、十字形结构,如下图所示。通过不同的结构可以对不同特征的图像进行形态学操作的处理。
在这里插入图片描述

4.2 腐蚀

腐蚀操作就是使用核在原图(二值化图)上进行从左到右、从上到下的滑动(也就是从图像的左上角开始,滑动到图像的右下角)。在滑动过程中,令核值为1的区域与被核覆盖的对应区域进行相乘,得到其最小值,该最小值就是卷积核覆盖区域的中心像素点的新像素值,接着继续滑动。由于操作图像为二值图,所以不是黑就是白,这就意味着,在被核值为1覆盖的区域内,只要有黑色(像素值为0),那么该区域的中心像素点必定为黑色(0)。这样做的结果就是会将二值化图像中的白色部分尽可能的压缩,如下图所示,该图经过腐蚀之后,“变瘦”了。

在这里插入图片描述 在这里插入图片描述
原图 腐蚀后

在腐蚀操作的详细流程中,遍历图像的过程如下:

  1. 初始化
    • 设置一个起始位置(通常从图像的左上角开始)。
    • 准备好结构元素(structuring element),它是一个小的矩阵,大小通常是奇数,并且有一个明确的中心点。
  2. 逐像素处理: 对于输入图像中的每一个像素,执行以下步骤:
    a. 定位: 将结构元素移动到当前待处理像素的位置,使得结构元素的中心与该像素对齐。
    b. 区域覆盖: 结构元素会覆盖图像上的一个局部邻域,这个邻域由结构元素的尺寸决定。
    c. 条件检查: 检查结构元素覆盖区域内所有图像像素的颜色。对于二值图像来说,就是看这些像素是否都是白色(前景像素)。如果所有被结构元素覆盖的像素均为白色,则继续下一个步骤;否则,跳过此步骤,将中心像素视为背景像素。
    d. 侵蚀决策: 如果结构元素覆盖的所有像素都是白色,则原图像中的中心像素保持不变(在输出图像中仍为白色);否则,将中心像素变为黑色(在输出图像中变为背景色)。
  3. 迭代移动: 结构元素沿着图像从左到右、从上到下逐行逐列地移动,重复上述过程,直到整个图像都被结构元素遍历过。
  4. 循环处理: 如果指定了多个迭代次数,那么在整个图像完成一次遍历后,再次从头开始进行同样的遍历和侵蚀决策,直到达到指定的迭代次数。

通过这样的遍历方式,腐蚀操作能够逐步收缩目标物体边界,消除孤立的噪声像素以及细化连续的前景区域。
在这里插入图片描述

4.3 膨胀

膨胀与腐蚀刚好相反,膨胀操作就是使用核在原图(二值化图)上进行从左到右、从上到下的滑动(也就是从图像的左上角开始,滑动到图像的右下角),在滑动过程中,令核值为1的区域与被核覆盖的对应区域进行相乘,得到其最大值,该最大值就是核覆盖区域的中心像素点的新像素值,接着继续滑动。由于操作图像为二值图,所以不是黑就是白,这就意味着,在卷积核覆盖的区域内,只要有白色(像素值为255),那么该区域的中心像素点必定为白色(255)。这样做的结果就是会将二值化图像中的白色部分尽可能的扩张,如下图所示,该图经过膨胀之后,“变胖”了。

在这里插入图片描述 在这里插入图片描述
原图 膨胀后

4.4 补充

4.4.1 开运算

开运算是先腐蚀后膨胀,其作用是:分离物体,消除小区域。特点:消除噪点,去除小的干扰块,而不影响原来的图像

在这里插入图片描述 在这里插入图片描述
原图 开运算

4.4.2 闭运算

闭运算与开运算相反,是先膨胀后腐蚀,作用是消除/“闭合”物体里面的孔洞,特点:可以填充闭合区域。

在这里插入图片描述 在这里插入图片描述
原图 闭运算

4.4.3 礼帽运算

原图像与“开运算“的结果图之差,因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。

礼帽运算用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用礼帽运算进行背景提取

在这里插入图片描述 在这里插入图片描述
原图 礼帽运算

4.4.4 黑帽运算

黑帽运算为”闭运算“的结果图与原图像之差,其运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。

黑帽运算用来分离比邻近点暗一些的斑块

在这里插入图片描述 在这里插入图片描述
原图 黑帽运算

4.4.5 形态学梯度

形态学梯度是一个基于结构元素的图像处理方法,它通过比较原图像与膨胀图和腐蚀图之间的差异来突出图像边缘特征。具体来说,对于图像中的每个像素点,其形态学梯度值是该像素点在膨胀后的图像值与其在腐蚀后的图像值之差。这样得到的结果通常能够强化图像的边缘信息,并且对噪声有一定的抑制作用

在这里插入图片描述 在这里插入图片描述
原图 形态学梯度

示例代码为:

img=cv.imread('./imageBase/images/car4.png')
img=cv.resize(img,(500,300))
# 二值化
ret,thresh=cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
# 定义核
kernel=np.ones((5,5),np.uint8)
# 腐蚀
erosion=cv.erode(thresh,kernel,iterations=1) # iterations迭代次数
# 膨胀
dilation=cv.dilate(thresh,kernel,iterations=1)
#  opening
opening=cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel)
#  closing
closing=cv.morphologyEx(thresh,cv.MORPH_CLOSE,kernel)
# 礼帽运算
tophat=cv.morphologyEx(thresh,cv.MORPH_TOPHAT,kernel)
# 黑帽运算
blackhat=cv.morphologyEx(thresh,cv.MORPH_BLACKHAT,kernel)
# 形态学梯度
gradient=cv.morphologyEx(thresh,cv.MORPH_GRADIENT,kernel)

cv.imshow('origin',thresh)
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.imshow('gradient',gradient)
cv.waitKey(0)
cv.destroyAllWindows()

其中用到的api有:

  • dst = cv2.erode(src, kernel, anchor=None, iterations=1,
    borderType=cv.BORDER_CONSTANT, borderValue=0)

    缩小前景物体的边界(侵蚀白色区域),用于消除噪声、分离物体。参数解析:
  • src: 输入图像(二值化或灰度图),常用 np.uint8 类型。
  • kernel: 结构元素(核),通过 cv.getStructuringElement() 创建,控制腐蚀形状。
  • anchor: 核的锚点位置,默认 (-1, -1) 表示中心点。
  • iterations: 腐蚀迭代次数(默认 1),次数越多效果越强。
  • borderType: 边界处理方式(默认 cv.BORDER_CONSTANT)。
  • borderValue: 当 borderType=cv.BORDER_CONSTANT 时的边界填充值(默认 0)。
  • dst = cv2.dilate(src, kernel, anchor=None, iterations=1,
    borderType=cv.BORDER_CONSTANT, borderValue=0)

    扩展前景物体的边界(扩张白色区域),用于连接断裂部分或填充空洞。参数:

与 cv2.erode 相同,作用相反。

  • dst = cv.morphologyEx(src, op, kernel, anchor=None, iterations=1,
    borderType=cv.BORDER_CONSTANT, borderValue=0)

    执行组合形态学操作(如开/闭运算、梯度等)。
  • 关键参数 op:
    • cv.MORPH_OPEN: 开运算(先腐蚀后膨胀)→ 去噪、消除小物体。
    • cv.MORPH_CLOSE: 闭运算(先膨胀后腐蚀)→ 填充空洞、连接相邻物体。
    • cv.MORPH_GRADIENT: 形态学梯度 → 获取物体边缘(膨胀图 - 腐蚀图)。
    • cv.MORPH_TOPHAT: 顶帽运算 → 突出亮区域(原图 - 开运算图)。
    • cv.MORPH_BLACKHAT: 黑帽运算 → 突出暗区域(闭运算图 - 原图)。
    • cv.MORPH_ERODE/cv.MORPH_DILATE: 直接调用腐蚀/膨胀。
  • 其他参数:同 cv.erode/cv.dilate。

总结

本文主要介绍了图像处理中的图像处理核心技术,包括直方图、模板匹配、霍夫变换、形态学变换。


网站公告

今日签到

点亮在社区的每一天
去签到