机器视觉学习-day02-灰度化实验

彩色图是由RGB三个通道组成,灰度图只有一个通道,也成为单通道图像,所以彩色图转换为灰度图的过程本质就是将RGB三通道合并成一个通道的过程。

灰度化实验的三种方法:最大值法、平均值法、加权平均值法

1 最大值法

最大值法就是从三个通道的值中选择最大值作为灰度后的数值,因此最大值法生成的灰度图会偏亮,适合给较暗的图像使用。

原图名1.png

python 复制代码
import cv2  # OpenCV库,用于图像处理
import numpy as np  # NumPy库,用于数值计算和数组操作

if __name__ == '__main__':
    # 1. 图片输入
    path = '1.jpg'  # 定义图片路径
    image_np = cv2.imread(path)  # 使用OpenCV读取图片
    """
       cv2.imread()函数:
       - 参数:图片路径
       - 返回值:NumPy数组,包含图像的像素数据
       - 格式:BGR顺序(不是常见的RGB顺序)
       - 形状:(高度, 宽度, 通道数)
    """
    image_shape = image_np.shape  # 获取图像的形状(尺寸信息)
    print(image_shape)  # (512, 512, 3)
    # 打印图像尺寸(例如(512, 512, 3)表示高512像素,宽512像素,3个颜色通道)

    # 2. 创建存储灰度图的数组
    # 创建一个全黑的图像数组,尺寸与原图相同,3个通道,数据类型为无符号8位整数(0-255)
    image_np_gray = np.zeros((image_shape[0], image_shape[1], 3), dtype=np.uint8)
    """
      np.zeros()函数:
      - 参数1:数组形状 (高度, 宽度, 通道数)
      - 参数2:数据类型(uint8表示0-255的整数)
      - 效果:创建一个全0(黑色)的图像数组
    """
    # 3. 使用最大值法进行灰度化
    # 遍历全局像素
    """image_shape==(512, 512, 3)"""
    for i in range(image_shape[0]):  # 遍历每一行(高度方向)
        for j in range(image_shape[1]):  # 遍历每一列(宽度方向)

            # 获取当前像素的BGR值
            b, g, r = image_np[i, j]  # 解包为三个变量
            """
            图像像素访问:
            - image_np[i, j] 返回一个包含三个值的数组 [B, G, R]
            - Python的元组解包:b, g, r = [B, G, R] 分别赋值
            """
            # 最大值法:取三个通道中的最大值作为灰度值
            max_val = max(b, g, r)  # 使用Python内置的max函数
            """
            最大值法原理:
            - 对于每个像素,取它的B、G、R三个通道中的最大值
            - 这样得到的灰度值会使图像整体变亮
            """
            # 将灰度值赋给三个通道
            image_np_gray[i, j] = [max_val, max_val, max_val]
            """
            为什么赋值三个相同的值?
            - 显示器显示灰度图像时,需要R、G、B三个通道的值相同
            - 如果只给一个通道赋值,图像会显示为单色(如红色)
            """

    # 打印处理后图像的形状(应与原图相同)
    print(image_np_gray.shape)

    # 4.图片矫正. 图片输出
    # 显示原始彩色图像
    cv2.imshow('Original Image', image_np)  # 第一个参数是窗口标题
    # 显示处理后的灰度图像
    cv2.imshow('Gray Image (Max Value Method)', image_np_gray)
    """
       cv2.imshow()函数:
       - 参数1:窗口标题(字符串)
       - 参数2:要显示的图像数据(NumPy数组)
       - 注意:OpenCV窗口会自动调整大小以适应图像
       """

    cv2.waitKey(0)  # 等待用户按下任意键(参数0表示无限等待)关闭弹窗
    """
    cv2.waitKey()函数:
    - 参数:等待时间(毫秒),0表示无限等待
    - 返回值:按键的ASCII码(但这里不关心返回值)
    - 功能:保持窗口显示,直到用户按键
    """

    # 保存结果(可选)
    cv2.imwrite('lena_gray_max_value.png', image_np_gray)

    """
    cv2.imwrite()函数:
    - 参数1:保存的文件名(包括扩展名)
    - 参数2:要保存的图像数据
    - 支持多种图像格式:PNG, JPG等
    """

代码运行后:

2 平均法:

平均值法使用三个通道的平均值作为灰度化后的数值,对结果需要进行取整。

python 复制代码
# 导入必要的库
import cv2  # OpenCV库,用于图像处理(读取、显示、保存图像)
import numpy as np  # NumPy库,用于高效处理数组和矩阵运算

# 程序的主入口(当直接运行此脚本时执行)
if __name__ == '__main__':
    # 1. 图片输入部分
    path = '1.jpg'  # 指定要处理的图片路径(图片名为lena.png)
    image_np = cv2.imread(path)  # 使用OpenCV读取图片

    """
    解释cv2.imread():
    - 功能:从指定路径读取图像文件
    - 返回值:一个NumPy数组,包含图像的所有像素数据
    - 格式:BGR顺序(不是常见的RGB顺序)
    - 形状:(高度, 宽度, 通道数)
    """

    image_shape = image_np.shape  # 获取图像的形状(尺寸信息)
    print(f"图像尺寸: {image_shape}")  # 打印图像尺寸(如(512, 512, 3)表示高512像素,宽512像素,3个颜色通道)

    # 2. 创建用于存储灰度图的数组
    # 创建一个全黑的图像数组,尺寸与原图相同,3个通道,数据类型为无符号8位整数(0-255)
    image_np_gray = np.zeros((image_shape[0], image_shape[1], 3), dtype=np.uint8)

    """
    解释np.zeros():
    - 功能:创建一个指定形状的全零数组
    - 参数1:(高度, 宽度, 通道数) - 这里使用原图的高度和宽度,3个通道
    - 参数2:dtype=np.uint8 - 表示每个元素是0-255的整数
    - 效果:创建一个全黑的图像数组
    """

    # 3. 使用平均法进行灰度化处理
    # 遍历图像的每一个像素
    for i in range(image_shape[0]):  # 遍历每一行(高度方向)
        for j in range(image_shape[1]):  # 遍历每一列(宽度方向)

            # 获取当前像素的B、G、R值
            b, g, r = image_np[i, j]  # 将像素的三个通道值分别赋给b,g,r

            """
            解释像素访问:
            - image_np[i, j] 返回一个包含三个值的数组 [B, G, R]
            - Python的元组解包:b, g, r = [B, G, R] 分别赋值
            - 注意:OpenCV使用BGR顺序,不是RGB
            """

            # 平均法:计算B、G、R三个值的平均值作为灰度值
            avg_val = (int(b) + int(g) + int(r)) // 3  # 使用整数除法(//)避免小数

            """
            平均法原理:
            - 对于每个像素,计算它的B、G、R三个通道的平均值
            - 公式:灰度值 = (B + G + R) / 3
            - 使用整数除法(//)而不是普通除法(/):
                - // 是整数除法,结果向下取整(如 100//3=33)
                - 普通除法会产生小数,需要转换为整数
            """

            # 将灰度值赋给三个通道(使图像显示为灰度)
            image_np_gray[i, j] = [avg_val, avg_val, avg_val]

            """
            为什么赋值三个相同的值?
            - 显示器显示灰度图像时,需要R、G、B三个通道的值相同
            - 如果只给一个通道赋值,图像会显示为单色(如红色)
            - 三个通道值相同:0=黑色,255=白色,中间值=灰色
            """

    # 打印处理后图像的形状(应与原图相同)
    print(f"处理后图像尺寸: {image_np_gray.shape}")

    # 4.图片矫正. 图片输出部分
    # 显示原始彩色图像
    cv2.imshow('Original Color Image', image_np)  # 第一个参数是窗口标题

    # 显示处理后的灰度图像
    cv2.imshow('Gray Image (Average Method)', image_np_gray)

    """
    解释cv2.imshow():
    - 功能:在窗口中显示图像
    - 参数1:窗口标题(字符串)
    - 参数2:要显示的图像数据(NumPy数组)
    - 注意:OpenCV窗口会自动调整大小以适应图像
    """

    cv2.waitKey(0)  # 等待用户按下任意键(参数0表示无限等待)

    """
    解释cv2.waitKey():
    - 功能:等待键盘输入
    - 参数:等待时间(毫秒),0表示无限等待
    - 返回值:按键的ASCII码(但这里不关心返回值)
    - 作用:保持窗口显示,直到用户按键
    """

    # 保存处理结果到文件
    cv2.imwrite('平均灰度化.png', image_np_gray)

    """
    解释cv2.imwrite():
    - 功能:将图像保存到文件
    - 参数1:保存的文件名(包括扩展名,如.png)
    - 参数2:要保存的图像数据
    - 支持多种图像格式:PNG, JPG, BMP等
    """

    # 关闭所有OpenCV窗口
    cv2.destroyAllWindows()

    """
    解释cv2.destroyAllWindows():
    - 功能:关闭所有由OpenCV创建的窗口
    - 在程序结束前清理资源
    """

"""    
优化修改:
import cv2
import numpy as np

if __name__ == '__main__':
    # 1. 读取图片
    path = 'image.jpg'
    image_np = cv2.imread(path)

    if image_np is None:
        print("错误:无法读取图片,请检查路径")
        exit()

    print(f"原始图像尺寸: {image_np.shape}")

    # 2. 使用向量化操作进行灰度化(平均法)
    # 更高效的方法:直接计算三个通道的平均值
    gray_vals = np.mean(image_np, axis=2, dtype=np.uint8)

    # 3. 创建三通道灰度图像
    image_np_gray = np.stack([gray_vals] * 3, axis=-1)

    print(f"处理后图像尺寸: {image_np_gray.shape}")

    # 4.图片矫正. 显示和保存结果
    cv2.imshow('Original Color Image', image_np)
    cv2.imshow('Gray Image (Average Method)', image_np_gray)
    cv2.waitKey(0)
    cv2.imwrite('平均灰度化.png', image_np_gray)
    cv2.destroyAllWindows()
"""

3 加权平均值法

使用RGB的加权平均数作为灰度化的数值,RGB的权重系数分别为0.299,0.587,0.114,这个数值是根据人类生物学实验得来的。

python 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

if __name__ == '__main__':
    # 1. 图片输入
    path = 'image.jpg'
    image_np = cv2.imread(path)
    image_shape = image_np.shape
    print(image_shape)  # (512, 512, 3)
    # print(image_np)

    # 灰度化处理
    # 初始化灰度图像存储

    # 2. 三通道图 + 灰度化
    # 创建一个纯黑图(B,G,R为0),分辨率相同,用于后续存储灰度后的数据
    image_np_gray = np.zeros((image_shape[0], image_shape[1], 3), dtype=np.uint8)
    print(image_np_gray.shape)
    # 数据拷贝
    image_np_gray = image_np.copy()
    # print(image_np_gray)
    # 三个权重
    wr = 0.299  # 红色权重
    wg = 0.587  # 绿色权重
    wb = 0.114  # 蓝色权重
    # 遍历全局像素
    for i in range(image_shape[0]):
        for j in range(image_shape[1]):
            # print(i,j)
            # 加权平均法计算灰度值
            avg = image_np[i, j][2] * wr + image_np[i, j][1] * wg + image_np[i, j][0] * wb

            # 通道顺序:
            # image_np[i, j][0]:蓝色通道(B)
            # image_np[i, j][1]:绿色通道(G)
            # image_np[i, j][2]:红色通道(R)

            # print(avg)
            avg = int(avg)  # 取整/浮点数改整数
            # print(avg)
            # 存储到image_np_gray中
            image_np_gray[i, j] = avg  # 三通道赋相同值
    print(image_np_gray.shape)
    print(image_np_gray)  # 如果BGR三通道的数值相同,表示灰度,但真正的灰度只需要一个通道

    # 4.图片矫正. 图片输出
    # plt.imshow(image_np_gray)
    # plt.title('image_np_gray')
    # plt.axis('off')
    # plt.show()
    # 后续彩图可以直接使用OpenCV展示,CV展示图片会收到系统缩放的影响
    cv2.imshow('image_np_gray',  # 必须:title
               image_np_gray)  # 展示的图像
    cv2.waitKey(0)  # 等待按下任意按键关闭弹窗

代码运行结果:

以上是自行所写代码,了解各个灰度化方式的运行代码流程,后续应用时使用下面的代码进行灰度化:这个函数使用的是加权平均法进行的灰度化。

python 复制代码
import cv2

# 读取彩色图像
image = cv2.imread('image.jpg')  # 替换为你的图像路径

# 检查图像是否正确加载
if image is None:
    print("错误: 无法读取图像,请检查文件路径")
    exit()

# 将BGR图像转换为灰度图像
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 显示原始图像和灰度图像
cv2.imshow('Original Color Image', image)
cv2.imshow('Grayscale Image', gray_image)

# 保存灰度图像
cv2.imwrite('gray_image.jpg', gray_image)

# 等待按键后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()