自适应二值化与形态学变换在图像颜色识别与替换中的应用解析
目录
- 前言
- 一、自适应二值化
- 1.1 取均值 ADAPTIVE_THRESH_MEAN_C
- 1.2 高斯加权求和 ADAPTIVE_THRESH_GAUSSIAN_C
- 1.2.1 一维高斯分布
- 1.2.2 二维高速分布
- 1.2.3 二维高斯分布权重计算规则
- 1.2.3.1 用户设置了σ
- 1.2.3.2 用户没有设置σ
- 1.3 代码
- 二、形态学变换
- 2.1 核
- 2.2 腐蚀
- 2.3 膨胀
- 2.4 代码
- 三、图片颜色识别
- 3.1 RGB颜色空间
- 3.2 HSV颜色空间
- 3.3 色彩空间转换
- 3.4 制作掩膜
- 3.5 与运算
- 3.6 代码
- 四、图片颜色的替换
- 4.1 开运算
- 4.2 颜色替换
- 2. 代码
- 五、练习集群
- 5.1 练习1
- 5.2 练习2
- 5.3 练习3
- 5.4 练习4
- 5.5 练习5
- 5.6 练习6
- 总结
前言
书接上文
从像素到二值化:OpenCV图像处理实战入门指南-CSDN博客文章浏览阅读557次,点赞13次,收藏10次。本文通过OpenCV库实战解析机器视觉核心概念,从像素本质、三维矩阵存储原理切入,结合棋盘格绘制与通道拆分实验揭示计算机眼中的图像逻辑,重点探讨灰度化三大算法(加权平均/最大值/平均值)及六种二值化方法(阈值法/反阈值法/截断阈值法/零处理/OTSU算法),通过Python代码演示图像处理全流程,帮助读者快速掌握图像特征提取核心技能,提升工程实践能力。https://blog.csdn.net/qq_58364361/article/details/146798890?fromshare=blogdetail&sharetype=blogdetail&sharerId=146798890&sharerefer=PC&sharesource=qq_58364361&sharefrom=from_link
一、自适应二值化
与上一章的二值化算法相比,自适应二值化更加适合明暗分布不均匀的图片,导致图片上每个小部分都有自己的前景和背景,此时可以使用自适应二值化,自适应二值化会对图像的所有像素点单独计算阈值,这样更好的针对每个像素点判断是前景还是背景。
自适应二值化可以更好的保留图片的信息。
参数:
- maxval:最大值,通常为255
- aadaptiveMethod:小区域阈值的计算方式
- ADAPTIVE_THRESH_MEAN_C:小区域内取均值
- ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核
- thresholdType:二值化方法
- THRESH_BINARY 阈值法
- THRESH_BINARY_INV 反阈值法
- blockSize:小区域的面积,通常取奇数,例如7表示7*7,后期称之为核(各种滤波和卷积等都用的到),计算的方式为滑动(从左到右,从上到下)计算
- c:最终阈值等于计算的阈值减去此值
1.1 取均值 ADAPTIVE_THRESH_MEAN_C
取blockSize=3,因此小区域面积为3*3,从原图的左上角像素点(162)开始计算其邻域的平均值,如果处于边缘区域就会进行边缘填充,填充值就是边界的像素点,如下所示。
计算九个点的平均值后减去c,就是最终当前像素点的阈值。
可以手动调节C值和BlockSize等值观察二值化结果:
- C值越大前景色越多
- blockSize分别为7和3的二值化效果
1.2 高斯加权求和 ADAPTIVE_THRESH_GAUSSIAN_C
1.2.1 一维高斯分布
高斯分布,通过概率密度函数进行定义,一维的高斯概率分布函数为
通过改变函数中和的值,我们可以得到如下图像,其中均值为μ,标准差为σ。
1.2.2 二维高速分布
根据一维高斯分布,拓展到二维高斯分布的表达式为:
高斯概率函数相对二维坐标产生,其中(x,y)为点坐标,以3*3的核为例,其点坐标分布如下:
1.2.3 二维高斯分布权重计算规则
把1.2.2节的九个点坐标分别计算权重并不是简单的套用公式,而是要分为以下几种情况:
1.2.3.1 用户设置了σ
此时把σ和x,y分别带入高斯函数计算权重
1.2.3.2 用户没有设置σ
此时要看blockSize的大小,即核的边长。
- 如果blockSize小于等于7
此时直接套用固定系数
- blockSize大于等于7
直接套用下面的公式:
size表示kernel(核)的尺寸,即blockSize,可以计算出σ的值,再结合(x,y)的坐标值带入到高斯密度函数中计算出权重。
1.3 代码
# 导入必要的库
import cv2 # OpenCV库,提供图像处理功能(版本需>3.0)
import numpy as np # 数值计算库,用于处理矩阵运算
import matplotlib.pyplot as plt # 绘图库,用于图像可视化(建议使用%matplotlib inline在Notebook中显示)
# 定义图像显示函数
def show_image(image, name):
"""
图像显示工具函数(存在潜在问题需注意)
参数:
image: 输入图像(需为BGR或Grayscale格式)
name : 显示图像的标题
"""
# 设置中文字体显示(仅在安装了中文字体的系统中有效)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体(Windows系统有效)
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题
# 注意:当传入单通道图像时,此转换会报错!
# 解决方法:在显示灰度/二值图像前添加条件判断
if len(image.shape) == 3: # 仅当为三通道图像时进行转换
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
else:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度图转RGB用于显示
# 创建图像窗口
plt.imshow(image)
plt.title(name) # 设置标题(依赖中文字体设置)
plt.axis('off') # 隐藏坐标轴
# 主程序入口
if __name__ == '__main__':
# 文件路径设置(建议使用os.path处理路径,增强跨平台兼容性)
path = "image.jpg" # 输入图像路径(需确保文件存在)
# 读取原始图像(返回值检查建议:if image_np is None: 处理错误)
image_np = cv2.imread(path) # 默认读取为BGR格式的uint8数组
# 创建1行2列的子图布局(121表示1行2列第1个位置)
plt.figure(figsize=(10,5)) # 建议设置画布大小
plt.subplot(121)
show_image(image_np, "原始图像") # 显示原始图像
# 转换为灰度图像(可选方法:cv2.COLOR_BGR2GRAY)
image_np_h = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) # 输出为单通道矩阵
# 自适应阈值处理(高斯加权法)
"""
参数详解:
src:输入灰度图像(必须单通道)
maxValue:满足阈值时设置的最大值(通常设为255)
adaptiveMethod:
ADAPTIVE_THRESH_MEAN_C:邻域均值法
ADAPTIVE_THRESH_GAUSSIAN_C:高斯加权法(效果更好但更耗时)
thresholdType:
THRESH_BINARY: 二值化(白字黑底)
THRESH_BINARY_INV:反二值化(黑底白字)
blockSize:邻域大小(必须奇数,值越大背景噪点越少但细节丢失越多)
C:从均值/加权均值中减去的常数(用于微调阈值)
"""
image_np_adaptive = cv2.adaptiveThreshold(
image_np_h,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV,
77, # 建议根据图像尺寸调整(典型值11-101)
4 # 根据图像对比度调整(典型值2-10)
)
# 保存处理结果(建议检查保存是否成功)
cv2.imwrite("demo_adaptive.jpg", image_np_adaptive) # 默认保存为单通道图像
# 显示处理结果(需要处理单通道图像的显示问题)
plt.subplot(122)
show_image(image_np_adaptive, "自适应阈值") # 当前版本已修复显示问题
# 显示所有图像(在脚本中需要调用,Notebook中可能自动显示)
plt.tight_layout() # 添加自动调整布局
plt.show()
二、形态学变换
形态学变换(Morphological Transformations)是一种基于形状的简单变换,它的处理对象通常是二值化后(白色前景,黑色背景)的图像。
形态学变换需要两个输入:
- 源图像(二值化)
- 核(结构化元素)
输出为形态学变换后的图像:
- 腐蚀
- 膨胀
上面两种形态学变换的处理结果是相反的,腐蚀会让较亮的元素变少,膨胀会让较亮的元素变多。
节点参数如下:
- morph_op_type 形态学操作类型
- erode 腐蚀
- dilate 膨胀
- shape 核的形状
- MORPH_RECT 矩形
- MORPH_ELLIPSE 椭圆
- MORPH_CROSS 十字形
- ksize 核尺寸
以腐蚀、矩形核、3*3尺寸的参数为例,说明计算过程:
实际图像是11111111或00000000,下面用一个1或0代替。
最终计算结果:
需要注意的是,上面的边缘填充都是补零,使用的Border Constant规则,这种填充的规则是一圈都填充一个设定value值,例如0。
卷积核中的0区域不考虑,例如十字形和椭圆形。
2.1 核
在上一章自适应二值化中已经接触过核的概念,实际上就是一个小区域,通常为3*3、5*5、7*7等,本章中的核会增加结构:
- 矩形结构
- 椭圆结构
- 十字形结构
通过不同的结构可以对不同特征的图像进行形态学操作的处理。
2.2 腐蚀
后续的图像处理,通常都会把前景设置为白色,下面就是一个腐蚀的操作效果:
腐蚀操作的具体步骤如下:
1. 选择一个核kernel(模板template)的结构元素,它是一个定义好的小型矩阵,用于在图像上滑动(从左上到右下)。核的大小和形状会决定腐蚀操作的强度。
2. 滑动核:将核在原始图像中从左到右,从上到下滑动,每次滑动,核覆盖图像的一个区域。
3. 计算:与之前的自适应二值化不同,此处不再进行平均值计算,计算的是位与操作,例如二值化之后的图像只会有纯白和纯黑,纯白的二进制是11111111,纯黑的而执行是00000000。
4. 重复步骤:继续滑动核并重复上述过程,直到图像的所有部分都被处理。
5. 结果:腐蚀操作的结构是一个“更瘦”或“更小”的前景图像,因为白色区域的边缘被削减了,只要白色边缘的像素点有黑色像素,这些边缘就会被消除。
2.3 膨胀
膨胀操作的具体步骤如下:
1. 选择一个核kernel(模板template)的结构元素,它是一个定义好的小型矩阵,用于在图像上滑动(从左上到右下)。核的大小和形状会决定膨胀操作的强度。
2. 滑动核:将核在原始图像中从左到右,从上到下滑动,每次滑动,核覆盖图像的一个区域。
3. 计算:与之前的自适应二值化不同,此处不再进行平均值计算,计算的是位或操作,例如二值化之后的图像只会有纯白和纯黑,纯白的二进制是11111111,纯黑的而执行是00000000。
4. 重复步骤:继续滑动核并重复上述过程,直到图像的所有部分都被处理。
5. 结果:膨胀操作的结构是一个“更胖”或“更大”的前景图像,因为白色区域的边缘被扩大了,同时填充了前景之间的空隙,把黑色间隙通过膨胀填充为白色。
2.4 代码
# 导入OpenCV计算机视觉库、numpy数学计算库和matplotlib绘图库
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 自定义图像显示函数(假设在别处已定义)
def show_image(img, title):
"""将BGR格式图像转换为RGB格式并显示"""
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if len(img.shape)==3 else img
plt.imshow(img_rgb, cmap='gray')
plt.title(title)
plt.axis('off')
if __name__ == '__main__':
# 图像路径(注意:实际使用时需要确保文件存在)
path = "image_2.jpg"
# 使用OpenCV读取图像,默认BGR格式
image = cv2.imread(path)
# 创建1x3的子图画布布局,激活第一个位置
plt.subplot(131)
# 显示原始BGR图像(show_image会做BGR到RGB的转换)
show_image(image, "原始图像")
# 将彩色图像转换为灰度图像
image_h = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用Otsu自动阈值算法进行图像二值化
# 参数说明:
# 127:初始阈值(Otsu会自动覆盖这个值)
# 255:最大值(超过阈值的像素设为白色)
# THRESH_BINARY + THRESH_OTSU:组合使用二进制阈值和Otsu算法
ret, image2 = cv2.threshold(image_h, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 在第二个位置显示二值化结果
plt.subplot(132)
show_image(image2, "二值化图像")
# 创建5x5矩形结构元素(用于形态学操作)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
# 执行腐蚀操作(消除细小噪声,分离相邻物体)
# 腐蚀原理:用结构元素扫描图像,取邻域内最小值
image_fs = cv2.erode(image2, kernel)
# 在第三个位置显示腐蚀后的图像
plt.subplot(133)
show_image(image_fs, "腐蚀图像")
# 显示所有子图
plt.show()
三、图片颜色识别
在本章中,主要通过掩膜对原图像进行与运算来找到要识别的颜色。
3.1 RGB颜色空间
在图像处理中,最常见的就是RGB颜色空间。RGB颜色空间是我们接触最多的颜色空间,是一种用于表示和显示彩色图像的一种颜色模型。RGB代表红色(Red)、绿色(Green)和蓝色(Blue),这三种颜色通过不同强度的光的组合来创建其他颜色,广泛应用于我们的生活中,比如电视、电脑显示屏以及上面实验中所介绍的RGB彩色图。
RGB颜色模型基于笛卡尔坐标系,如下图所示,RGB原色值位于3个角上,二次色青色、红色和黄色位于另外三个角上,黑色位于原点处,白色位于离原点最远的角上。因为黑色在RGB三通道中表现为(0,0,0),所以映射到这里就是原点;而白色是(255,255,255),所以映射到这里就是三个坐标为最大值的点。
RGB颜色空间可以产生大约1600万种颜色,几乎包括了世界上的所有颜色,也就是说可以使用RGB颜色空间来生成任意一种颜色
3.2 HSV颜色空间
HSV颜色空间(HSV颜色模型,也成为HSL)是一种与RGB颜色模型并列的颜色空间表示法。
HSV颜色空间使用色调或色相(Hue)、饱和度(Saturation)和亮度或明度(Value)三个参数来表示颜色,色调H表示颜色的种类,如红色、绿色、蓝色等;饱和度表示颜色的纯度或强度,如红色越纯,饱和度就越高;亮度表示颜色的明暗程度,如黑色比白色亮度低。HSV颜色模型是一种六角锥体模型,如下图所示:
由于HSV的颜色是连续的,比RGB更适合进行颜色范围划分,常见的颜色经过试验得到的范围如下:
由于颜色是8位的,所以最多表示256个数字,无法完全表示360°,OpenCV重新换算了色调的角度,范围是0到180°。
HSV三个属性是相互共同作用的,例如橙色必须是H范围11-25且S范围是43-255且V范围46-255。
HSV具有的优势如下:
3.3 色彩空间转换
3.4 制作掩膜
掩膜(Mask)是一种在图像处理中常见的操作(类似于PS中的蒙版),它可以选择性地遮挡图像的某个部分,从而实现特定任务的目标。
掩膜通常是一个二值化图像,并且与原图相同大小,其中目标区域被设置为1(白色),而其他区域设置为0(黑色),目标区域可以根据HSV的参数修改,上左图中就是制作红色掩膜的过程。
通过掩膜可以使用白色区域操作原图中的同位置像素。
3.5 与运算
3.6 代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
def show_image(image, name):
"""显示图像
Args:
image: 输入图像,可以是灰度图或彩色图
name: 图像显示的标题
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
if len(image.shape) == 3: # 如果是彩色图像
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR转RGB
else: # 如果是灰度图像
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度转RGB
plt.imshow(image) # 显示图像
plt.title(name) # 设置标题
plt.axis('off') # 关闭坐标轴
if __name__ == '__main__':
path = "sansequan.jpg" # 图像路径
image = cv2.imread(path) # 读取图像
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 将图像从BGR颜色空间转换到HSV颜色空间
# 制作掩膜(同时识别黄色和青色)
# 定义黄色的HSV范围
yellow_low = np.array([26, 43, 46]) # 黄色下限
yellow_high = np.array([34, 255, 255]) # 黄色上限
# 制作黄色掩膜
mask1 = cv2.inRange(hsv_image, yellow_low, yellow_high) # 根据阈值创建黄色掩膜
# 定义青色的HSV范围
cyan_low = np.array([78, 43, 46]) # 青色下限
cyan_high = np.array([99, 255, 255]) # 青色上限
mask2 = cv2.inRange(hsv_image, cyan_low, cyan_high) # 根据阈值创建青色掩膜
# 合并黄色和青色掩膜
mask = cv2.bitwise_or(mask1, mask2) # 对两个掩膜进行或运算
# 通过掩膜提取原图中的目标颜色区域
res = cv2.bitwise_and(image, image, mask=mask) # 按位与运算
# 显示图像
plt.figure(figsize=(8, 8), dpi=300) # 创建画布
plt.subplot(131) # 1行3列第1个子图
show_image(image, "原图") # 显示原图
plt.subplot(132) # 1行3列第2个子图
show_image(mask, "掩膜") # 显示掩膜
plt.subplot(133) # 1行3列第3个子图
show_image(res, "结果") # 显示结果
四、图片颜色的替换
在上一个实验中,可以在图像中识别某一种(多种)颜色,那么就可以对这些颜色进行单独操作,比如替换成其他的颜色,原理就是在得到掩膜后,对掩膜中白色区域的原图进行一个像素值的修改。
4.1 开运算
在形态学变换章节中学习了腐蚀和膨胀的工作原理,开运算就是先对图像进行腐蚀操作,再进行膨胀操作,开运算的效果是可以去除二值化图中的小噪点,并分离相连的物体。
开运算节点的参数如下:
kernel表示核的尺寸
开运算的参数比较简洁,如果需要细节的操作,可以手动编程使用腐蚀和膨胀函数细节控制。
经过开运算,上图图像的连接部分和边缘毛刺都被消除。
4.2 颜色替换
由于掩膜与原图的大小相同,并且像素的一一对应的,可以得到掩膜中的白色区域坐标,把掩膜中所有白色区域的坐标带入到原图中,就可以得到原图中所有红色区域的坐标。拿到原图红色的坐标后,可以进行任何红色像素值的操作,例如修改颜色。
2. 代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
path = "sigequan.jpg"
image = cv2.imread(path)
plt.figure(figsize=(8, 8), dpi=300)
plt.subplot(141)
show_image(image, "原图")
#hsv
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 制作红色掩膜
red_low = np.array([0, 43, 46]) # 红色下限
red_high = np.array([10, 255, 255]) # 红色上限
mask1 = cv2.inRange(hsv_image, red_low, red_high) # 根据阈值创建红色掩膜
red_low = np.array([156, 43, 46]) # 红色下限
red_high = np.array([180, 255, 255]) # 红色上限
mask2 = cv2.inRange(hsv_image, red_low, red_high) # 根据阈值创建红色掩膜
mask = cv2.bitwise_or(mask1, mask2) # 对两个掩膜进行或运算
#展示一下掩膜
plt.subplot(142)
show_image(mask, "红色掩膜")
#开运算
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5)) # 创建矩形结构元素
mask_opened = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 开运算
plt.subplot(143)
show_image(mask_opened, "红色掩膜开运算")
plt.show()
#颜色替换
#遍历掩膜像素
for i in range(mask_opened.shape[0]):
for j in range(mask_opened.shape[1]):
if mask_opened[i,j] == 255:
#修改原图颜色
image[i,j] = (255,0,0)
#展示图片
show_image(image, "原图")
plt.show()
五、练习集群
5.1 练习1
墙上的贴纸用白色显示,墙用黑色显示,要求效果尽量好。
import cv2
import matplotlib.pyplot as plt
def show_image(image, name):
"""显示图像
Args:
image: 输入图像(numpy数组)
name: 图像标题(str)
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
if len(image.shape) == 3: # 彩色图像
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR转RGB
else: # 灰度图像
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度转RGB
plt.imshow(image) # 显示图像
plt.title(name) # 设置标题
plt.axis('off') # 关闭坐标轴
if __name__ == '__main__':
path = "image.jpg" # 图像路径
image_np = cv2.imread(path) # 读取图像
plt.figure(figsize=(10,10),dpi=300) # 创建画布
plt.subplot(121) # 子图1
show_image(image_np, "原始图像") # 显示原始图像
# 图像处理部分
image_np_h = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) # 转为灰度图
image_np_adaptive = cv2.adaptiveThreshold(
image_np_h, # 输入灰度图像
255, # 输出最大值
cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # 自适应方法(高斯加权)
cv2.THRESH_BINARY_INV, # 二值化类型(反二值化)
77, # 邻域大小
4 # 常数C
)
cv2.imwrite("demo_adaptive.jpg", image_np_adaptive) # 保存结果
plt.subplot(122) # 子图2
show_image(image_np_adaptive, "自适应阈值") # 显示处理结果
plt.tight_layout() # 调整子图间距
plt.show() # 显示图像
5.2 练习2
处理整个图片,尽量只显示
,其他部分都是白色。
import cv2
import matplotlib.pyplot as plt
def show_image(image, name):
"""显示图像
Args:
image: 输入图像(numpy数组)
name: 图像标题(str)
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
if len(image.shape) == 3: # 彩色图像
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR转RGB
else: # 灰度图像
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度转RGB
plt.imshow(image) # 显示图像
plt.title(name) # 设置标题
plt.axis('off') # 关闭坐标轴
if __name__ == '__main__':
path = "yaohe.jpg" # 图像路径
image_np = cv2.imread(path) # 读取图像
plt.figure(figsize=(10,10),dpi=300) # 创建画布
plt.subplot(121) # 子图1
show_image(image_np, "原始图像") # 显示原始图像
# 图像处理部分
image_np_h = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) # 转为灰度图
image_np_adaptive = cv2.adaptiveThreshold(
image_np_h, # 输入灰度图像
255, # 输出最大值
cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # 自适应方法(高斯加权)
cv2.THRESH_BINARY, # 二值化类型(反二值化)
999, # 邻域大小
120 # 常数C
)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) # 创建椭圆结构元素
#膨胀
image_np_dilate = cv2.dilate(image_np_adaptive, kernel) # 膨胀操作
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)) # 创建更小的椭圆结构元素
#腐蚀
image_np_erode = cv2.erode(image_np_dilate, kernel) # 第一次腐蚀
image_np_erode = cv2.erode(image_np_erode, kernel) # 第二次腐蚀
image_np_erode = cv2.erode(image_np_erode, kernel) # 第三次腐蚀
plt.subplot(122) # 子图2
show_image(image_np_erode, "腐蚀") # 显示腐蚀后的图像
plt.show() # 显示图像
5.3 练习3
识别下图中的蓝色(不包括云的边框)和橙色
#%%
import cv2
import numpy as np
import matplotlib.pyplot as plt
def show_image(image, name):
"""显示图像
Args:
image: 输入图像(numpy数组)
name: 图像标题(str)
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
if len(image.shape) == 3: # 彩色图像
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR转RGB
else: # 灰度图像
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度转RGB
plt.imshow(image) # 显示图像
plt.title(name) # 设置标题
plt.axis('off') # 关闭坐标轴
if __name__ == '__main__':
path = "caihong.jpg" # 图像路径
image_np = cv2.imread(path) # 读取图像
plt.figure(dpi=300) # 创建画布
plt.subplot(241) # 子图1
show_image(image_np, "原始图像")
#转换色彩空间
image_np_hsv = cv2.cvtColor(image_np, cv2.COLOR_BGR2HSV) # 转为HSV图
#制作掩膜
#橙色掩膜
o_low = np.array([11, 43, 46]) # 橙色下限
o_high = np.array([25, 255, 255]) # 橙色上限
mask = cv2.inRange(image_np_hsv, o_low, o_high) # 掩膜1
plt.subplot(242)
show_image(mask, "橙色掩膜")
#制作蓝色掩膜
blue_low = np.array([110, 43, 46]) # 蓝色下限
blue_high = np.array([124, 255, 255]) # 蓝色上限
mask_blue = cv2.inRange(image_np_hsv, blue_low, blue_high) # 掩膜1
#开运算
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) # 创建椭圆结构元素
mask_blue = cv2.morphologyEx(mask_blue, cv2.MORPH_OPEN, kernel) # 开运算
plt.subplot(243)
show_image(mask_blue, "蓝色掩膜")
mask = mask + mask_blue # 掩膜相加
plt.subplot(244)
show_image(mask, "掩膜")
ret = cv2.bitwise_and(image_np, image_np, mask=mask) # 掩膜与原图相乘
plt.subplot(245)
show_image(ret, "最终图像")
plt.show() # 显示图像
5.4 练习4
把百事可乐的配色改为可口可乐风格
import cv2
import numpy as np
import matplotlib.pyplot as plt
def show_image(image, name):
"""显示图像
Args:
image: 输入图像(numpy数组)
name: 图像标题(str)
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
if len(image.shape) == 3: # 彩色图像
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR转RGB
else: # 灰度图像
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度转RGB
plt.imshow(image) # 显示图像
plt.title(name) # 设置标题
plt.axis('off') # 关闭坐标轴
if __name__ == '__main__':
path = "kele.jpg"
image = cv2.imread(path)
plt.figure(dpi=300)
plt.subplot(131)
show_image(image, "原始图像")
#图像处理部分
image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 转为HSV图
#制作蓝色掩膜
blue_low = np.array([90, 110, 110]) # 蓝色下限
blue_high = np.array([120, 255, 255]) # 蓝色上限
mask_blue = cv2.inRange(image_hsv, blue_low, blue_high) # 掩膜1
plt.subplot(132)
show_image(mask_blue, "蓝色掩膜")
for i in range(mask_blue.shape[0]):
for j in range(mask_blue.shape[1]):
if mask_blue[i][j] == 255:
image[i][j] = [32, 45, 159] # 将蓝色区域变为红色
plt.subplot(133)
show_image(image, "红色图像")
plt.show() # 显示图像
5.5 练习5
把越和幸运都换成蓝色,让墙变得更白
import cv2
import numpy as np
import matplotlib.pyplot as plt
def show_image(image, name):
"""显示图像
Args:
image: 输入图像(numpy数组)
name: 图像标题(str)
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
if len(image.shape) == 3: # 彩色图像
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR转RGB
else: # 灰度图像
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度转RGB
plt.imshow(image) # 显示图像
plt.title(name) # 设置标题
plt.axis('off') # 关闭坐标轴
path = "image.jpg"
image = cv2.imread(path) # 读取图像
plt.figure(dpi=300) # 创建画布,设置DPI
plt.subplot(331) # 创建3x3子图,位置1
show_image(image, "原始图像") # 显示原始图像
# 图像处理部分
image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 转为HSV色彩空间
# 制作黑色掩膜
black_low = np.array([0, 0, 0]) # 黑色HSV下限
black_high = np.array([180, 255, 80]) # 黑色HSV上限
mask_black = cv2.inRange(image_hsv, black_low, black_high) # 生成黑色区域掩膜
# 制作蓝色掩膜
blue_low = np.array([90, 110, 110]) # 蓝色HSV下限
blue_high = np.array([120, 255, 255]) # 蓝色HSV上限
mask_blue = cv2.inRange(image_hsv, blue_low, blue_high) # 生成蓝色区域掩膜
# 制作黄色掩膜
yellow_low = np.array([15, 80, 0]) # 黄色HSV下限
yellow_high = np.array([40, 255, 255]) # 黄色HSV上限
mask_yellow = cv2.inRange(image_hsv, yellow_low, yellow_high) # 生成黄色区域掩膜
plt.subplot(332) # 子图位置2
show_image(mask_yellow, "黄色掩膜") # 显示黄色掩膜
# 制作红色掩膜
red_low = np.array([0, 50, 0]) # 红色HSV下限(低色调范围)
red_high = np.array([15, 255, 255]) # 红色HSV上限
red_mask = cv2.inRange(image_hsv, red_low, red_high) # 生成红色区域掩膜(低色调)
red_low = np.array([150, 50, 0]) # 红色HSV下限(高色调范围)
red_high = np.array([200, 255, 255]) # 红色HSV上限
red_mask = red_mask + cv2.inRange(image_hsv, red_low, red_high) # 合并高低色调红色掩膜
plt.subplot(333) # 子图位置3
show_image(red_mask, "红色掩膜") # 显示红色掩膜
mask = mask_yellow + red_mask # 合并黄色和红色掩膜
plt.subplot(334) # 子图位置4
show_image(mask, "黄红组合掩膜") # 显示组合掩膜
# 把区域改成蓝色
for i in range(mask.shape[0]): # 遍历图像高度
for j in range(mask.shape[1]): # 遍历图像宽度
if mask[i][j] == 255: # 如果是掩膜区域
image[i][j] = (149,86,24) # 修改为蓝色(BGR格式)
plt.subplot(335) # 子图位置5
show_image(image, "转变字体颜色") # 显示颜色修改后的图像
mask = mask_blue + mask_yellow + red_mask + mask_black # 合并所有颜色掩膜
plt.subplot(336) # 子图位置6
show_image(mask, "完整掩膜") # 显示完整掩膜
# 掩膜取反
mask = 255 - mask # 对掩膜取反
plt.subplot(337) # 子图位置7
show_image(mask, "掩膜取反") # 显示取反后的掩膜
for i in range(mask.shape[0]): # 遍历图像高度
for j in range(mask.shape[1]): # 遍历图像宽度
if mask[i][j] == 255: # 如果是掩膜区域
image[i][j] = (255,255,255) # 修改为白色(BGR格式)
plt.subplot(338) # 子图位置8
show_image(image, "最终图像") # 显示最终处理结果
plt.show() # 显示图像
5.6 练习6
把原图的噪点去掉。
import cv2
import numpy as np
import matplotlib.pyplot as plt
def show_image(image, name):
"""显示图像
Args:
image: 输入图像(numpy数组)
name: 图像标题(str)
"""
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
if len(image.shape) == 3: # 彩色图像
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # BGR转RGB
else: # 灰度图像
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) # 灰度转RGB
plt.imshow(image) # 显示图像
plt.title(name) # 设置标题
plt.axis('off') # 关闭坐标轴
path = "quzao.jpg"
image = cv2.imread(path) # 读取图像
image_fuben = image
plt.figure(dpi=300) # 创建画布,设置DPI
plt.subplot(231) # 创建2x3子图,位置1
show_image(image, "原始图像") # 显示原始图像
# 图像处理部分
image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 转为HSV色彩空间
image_hsv_fuben = cv2.cvtColor(image_fuben, cv2.COLOR_BGR2HSV) # 转为HSV色彩空间
# 制作黄色掩膜
yellow_low = np.array([15, 80, 0]) # 黄色HSV下限
yellow_high = np.array([40, 255, 255]) # 黄色HSV上限
mask_yellow = cv2.inRange(image_hsv, yellow_low, yellow_high) # 生成黄色区域掩膜
#制作红色掩膜
red_low = np.array([0, 50, 0]) # 红色HSV下限(低色调范围)
red_high = np.array([15, 255, 255]) # 红色HSV上限
red_mask = cv2.inRange(image_hsv, red_low, red_high) # 生成红色区域掩膜(低色调)
red_low = np.array([150, 50, 0]) # 红色HSV下限(高色调范围)
red_high = np.array([200, 255, 255]) # 红色HSV上限
red_mask = red_mask + cv2.inRange(image_hsv, red_low, red_high) # 合并高低色调红色掩膜
#制作绿色掩膜
green_low = np.array([35, 43, 46]) # 绿色HSV下限
green_high = np.array([77, 255, 255]) # 绿色HSV上限
mask_green = cv2.inRange(image_hsv, green_low, green_high) # 生成绿色区域掩膜
#制作蓝色掩膜
blue_low = np.array([90, 110, 110]) # 蓝色HSV下限
blue_high = np.array([120, 255, 255]) # 蓝色HSV上限
mask_blue = cv2.inRange(image_hsv, blue_low, blue_high) # 生成蓝色区域掩膜
#制作总掩膜
mask = mask_yellow + red_mask + mask_green + mask_blue # 合并所有颜色掩膜
plt.subplot(232) # 子图位置2
show_image(mask, "颜色掩膜") # 显示颜色掩膜
#开运算
kernel = np.ones((5, 5), np.uint8) # 定义卷积核
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 开运算
# 掩膜取反
mask = 255 - mask # 对掩膜取反
for i in range(mask.shape[0]): # 遍历图像高度
for j in range(mask.shape[1]): # 遍历图像宽度
if mask[i][j] == 255: # 如果是掩膜区域
image[i][j] = (255,255,255) # 修改为白色(BGR格式)
#制作黑色掩膜
black_low = np.array([0, 0, 0]) # 黑色HSV下限
black_high = np.array([180, 254, 254]) # 黑色HSV上限
mask_black = cv2.inRange(image_fuben, black_low, black_high) # 生成黑色区域掩膜
#膨胀
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2,2))
mask_black = cv2.dilate(mask_black, kernel) # 膨胀
for i in range(mask_black.shape[0]): # 遍历图像高度
for j in range(mask_black.shape[1]): # 遍历图像宽度
if mask_black[i][j] == 255: # 如果是黑色区域
image[i][j] = (187,157,137) # 修改为黑色(BGR格式)
plt.subplot(233) # 子图位置3
show_image(image, "最终图像") # 显示最终处理结果
plt.show() # 显示图像
总结
本文深入探讨图像处理中自适应二值化、形态学变换及颜色操作的核心技术。自适应二值化通过动态计算像素阈值,有效解决明暗不均图像的前景分离问题,其均值与高斯加权方法可灵活调整细节保留与噪声控制。形态学变换通过腐蚀、膨胀及开运算优化二值图像结构,消除噪声并增强目标特征。基于HSV颜色空间的掩膜技术结合位运算实现精准颜色识别,通过颜色范围定义与开运算优化掩膜后,完成特定颜色区域的像素替换。文中结合代码实例,展示了从二值化处理到颜色替换的全流程,为复杂场景下的图像分析提供实用解决方案。