当前位置: 首页 > news >正文

自适应二值化与形态学变换在图像颜色识别与替换中的应用解析

目录

  • 前言
  • 一、自适应二值化
  • 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=3时,使用转置矩阵进行乘法,得到九个点的权重值:

  • 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重新换算色调角度范围0180°。

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颜色空间的掩膜技术结合位运算实现精准颜色识别,通过颜色范围定义与开运算优化掩膜后,完成特定颜色区域的像素替换。文中结合代码实例,展示了从二值化处理到颜色替换的全流程,为复杂场景下的图像分析提供实用解决方案。

相关文章:

  • .Net中的流处理类总结 Stream/FileStream/MemoryStream/NetworkStream/StreamReader
  • 04-深入解析 Spring 事务管理原理及源码
  • YOLOv5配置训练以及华为昇腾910B推理
  • JavaScript数组Array的使用:添加、删除、排序、遍历、互转
  • 【python中级】使用 distutils 和wheel生成whl 轮子文件
  • 蓝桥杯 切割
  • 深入探究Spring MVC
  • C++学习day4
  • 基于大数据分析的门户信息推荐系统
  • 《STL 六大组件之容器篇:简单了解 list》
  • 国家天文台携手阿里云,发布国际首个太阳大模型“金乌”
  • 用Python实现TCP代理
  • Java数据类型与数据库类型映射技术文档
  • 基础框架系列分享:一个通用的Excel报表生成管理框架
  • MQTT 服务器(emqx)搭建及使用(一)
  • [图论]拓扑排序
  • 【JavaEE】MyBatis - Plus
  • deepseek v3-0324 化学键线式Canvas编辑器设计
  • 线程池自顶向下
  • 关于 @Autowired 和 @Value 使用 private 字段的警告问题分析与解决方案
  • 气候多米诺:厄尔尼诺与东南亚跨境害虫或威胁中国粮食安全
  • 技术派|更强的带刀侍卫:从054B型战舰谈谈世界护卫舰发展
  • 从“求生”到“生活”:医保纳入创新药让梗阻性肥厚型心肌病患者重拾生活掌控权
  • 科普|“小”耳洞也会引发“大”疙瘩,如何治疗和预防?
  • 5年建成强化城市核心功能新引擎,上海北外滩“风景文化都是顶流”
  • 智利观众也喜欢上海的《好东西》