欢迎光临
我们一直在努力

机器学习篇---数字图像增强(灰度变换与直方图均衡)

一、模块导入与环境准备

import cv2
import numpy as np
import matplotlib.pyplot as plt
import os

  • cv2:OpenCV 库,提供图像读取、写入、颜色空间转换、直方图均衡化等函数。

  • numpy:科学计算库,用于高效的数组运算,图像本质上就是 NumPy 数组。

  • matplotlib.pyplot:绘图库,用于显示图像和绘制直方图。

  • os:操作系统接口库,用于创建输出目录。


二、准备工作:创建输出目录

output_dir = 'output_1'
os.makedirs(output_dir, exist_ok=True)

  • output_dir:定义输出文件夹名称为 'output_1'。

  • os.makedirs():递归创建目录,exist_ok=True 表示若目录已存在不报错,保证每次运行都能正常保存结果。


三、自定义工具函数:读取灰度图像

def load_gray_image(path):

  • 定义函数 load_gray_image,接收一个参数 path(图像文件路径),负责将任意图像统一读取为灰度图。

img = cv2.imread(path, cv2.IMREAD_UNCHANGED)

  • cv2.imread():读取图像文件。

  • cv2.IMREAD_UNCHANGED:保留原始图像的通道数和位深度,不自动转换。

if img is None:
raise FileNotFoundError(f"图像文件 {path} 未找到,请检查路径")

  • 若图像读取失败(返回 None),抛出一个明确的文件未找到异常,方便定位错误。

if len(img.shape) == 3:
print(f"[提示] {path} 是彩色图,已自动转换为灰度图")
return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

  • len(img.shape) == 3:判断是否为三通道彩色图像(形状为 (高, 宽, 3))。

  • 若是彩色图,打印提示信息,并用 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 将 BGR 彩色图转换为单通道灰度图后返回。

return img

  • 若已是灰度图(二维数组),直接返回。


四、任务1:反色处理

def task1_invert(original_gray):

  • 定义函数 task1_invert,接收正常光照的灰度图像,实现底片效果的反色处理。

inverted = 255 – original_gray # s = 255 – r

  • 反色公式:s = 255 – r,对每个像素值 r 用 255 减去,暗变亮、亮变暗,呈现底片效果。利用 NumPy 广播机制对整幅图像一次性完成运算。

plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.imshow(original_gray, cmap='gray', vmin=0, vmax=255)
plt.title('Original Gray Image')
plt.axis('off')

  • 创建 10×4 英寸画布,1行2列布局,左侧显示原图。

  • vmin=0, vmax=255:固定灰度显示范围,确保0为黑、255为白。

plt.subplot(1, 2, 2)
plt.imshow(inverted, cmap='gray', vmin=0, vmax=255)
plt.title('Inverted Image (255 – r)')
plt.axis('off')
plt.tight_layout()
plt.show()

  • 右侧显示反色图像,tight_layout() 调整间距,show() 渲染显示。

cv2.imwrite(os.path.join(output_dir, 'inverted.jpg'), inverted)
print("[任务1] 反色图像已保存")

  • cv2.imwrite():将反色图像保存为 JPG 文件到输出目录,并打印完成提示。


五、任务2:对数变换

def task2_log_transform(low_light, over_exposed):

  • 定义函数,接收低照度图像 low_light 和曝光过度图像 over_exposed 两幅灰度图。

5.1 内部辅助函数:对数变换

def log_transform(img):
img_float = img.astype(np.float32)
c = 255.0 / np.log(1 + np.max(img_float))
log_img = c * np.log(1 + img_float)
return np.clip(log_img, 0, 255).astype(np.uint8)

  • img.astype(np.float32):将 uint8 类型转为 float32,避免对数运算溢出。

  • c:缩放常数,计算公式为 255 / log(1 + max),使变换后最大像素值恰好映射到 255。

  • np.log(1 + img_float):对数变换核心,加1防止对0取对数导致负无穷。

  • np.clip(log_img, 0, 255):裁剪到 [0, 255] 范围,防止越界。

  • .astype(np.uint8):转回 8 位无符号整数类型,用于显示和保存。

log_low = log_transform(low_light)
log_over = log_transform(over_exposed)

  • 分别对低照度和过度曝光图像执行对数变换。

5.2 显示与直方图绘制(以低照度为例)

plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1)
plt.imshow(low_light, cmap='gray', vmin=0, vmax=255)
plt.title('Low Light Original')
plt.axis('off')

  • 2行2列布局,左上角显示低照度原图。

plt.subplot(2, 2, 2)
plt.imshow(log_low, cmap='gray', vmin=0, vmax=255)
plt.title('After Log Transform')
plt.axis('off')

  • 右上角显示对数变换后的低照度图像。

plt.subplot(2, 2, 3)
plt.hist(low_light.ravel(), bins=256, range=[0, 256], color='gray', alpha=0.7)
plt.title('Low Light Histogram')

  • plt.hist():绘制直方图。

  • .ravel():将二维图像数组展平为一维,便于统计。

  • bins=256:256 个柱,对应 0-255 每个灰度级。

  • range=[0, 256]:统计范围为 [0, 256)。

  • alpha=0.7:设置透明度。

plt.subplot(2, 2, 4)
plt.hist(log_low.ravel(), bins=256, range=[0, 256], color='gray', alpha=0.7)
plt.title('Log Transformed Histogram')
plt.tight_layout()
plt.show()

  • 右下角绘制变换后的直方图,展示对数变换对灰度分布的扩展效果。

曝光过度图像的显示逻辑完全相同,仅替换数据源。

5.3 保存结果

cv2.imwrite(os.path.join(output_dir, 'log_low.jpg'), log_low)
cv2.imwrite(os.path.join(output_dir, 'log_over.jpg'), log_over)
print("[任务2] 对数变换结果已保存")


六、任务3:线性拉伸与直方图均衡化

def task3_histogram_enhancement(low_light, over_exposed):

  • 定义函数,对低照度和过度曝光图像分别进行线性拉伸和直方图均衡化,并对比展示。

6.1 内部辅助函数

def linear_stretch(img):
min_val, max_val = np.min(img), np.max(img)
if max_val == min_val:
return img
return ((img – min_val) / (max_val – min_val) * 255).astype(np.uint8)

  • 线性拉伸公式:(x – min) / (max – min) * 255,将原图灰度范围线性映射到 [0, 255]。

  • max_val == min_val:若图像灰度值全部相同(常量图像),直接返回原图避免除零错误。

def equalize(img):
return cv2.equalizeHist(img)

  • cv2.equalizeHist():OpenCV 的直方图均衡化函数,自动重新分布灰度值使直方图尽可能平坦,增强全局对比度。

6.2 执行增强

low_linear = linear_stretch(low_light)
low_eq = equalize(low_light)
over_linear = linear_stretch(over_exposed)
over_eq = equalize(over_exposed)

  • 分别生成四幅增强结果。

6.3 三组对比显示(以低照度为例)

plt.figure(figsize=(15, 10))

  • 2行3列布局,上排三幅图(原图、线性拉伸、均衡化),下排对应直方图。

plt.subplot(2, 3, 1)
plt.imshow(low_light, cmap='gray', vmin=0, vmax=255)
plt.title('Low Light Original')
plt.axis('off')

  • 左上:低照度原图。

plt.subplot(2, 3, 2)
plt.imshow(low_linear, cmap='gray', vmin=0, vmax=255)
plt.title('Linear Stretch')
plt.axis('off')

  • 中上:线性拉伸结果。

plt.subplot(2, 3, 3)
plt.imshow(low_eq, cmap='gray', vmin=0, vmax=255)
plt.title('Histogram Equalization')
plt.axis('off')

  • 右上:直方图均衡化结果。

plt.subplot(2, 3, 4)
plt.hist(low_light.ravel(), bins=256, range=[0, 256], color='gray', alpha=0.7)
plt.title('Original Histogram')

  • 左下:原图直方图,反映灰度集中在暗区的特点。

plt.subplot(2, 3, 5)
plt.hist(low_linear.ravel(), bins=256, range=[0, 256], color='gray', alpha=0.7)
plt.title('Linear Stretch Histogram')

  • 中下:线性拉伸后直方图,分布被均匀拉开但保持原有相对形状。

plt.subplot(2, 3, 6)
plt.hist(low_eq.ravel(), bins=256, range=[0, 256], color='gray', alpha=0.7)
plt.title('Equalized Histogram')
plt.tight_layout()
plt.show()

  • 右下:均衡化后直方图,分布更加均匀平坦。

曝光过度图像的三组对比采用完全相同的布局。

6.4 保存结果

cv2.imwrite(os.path.join(output_dir, 'low_linear.jpg'), low_linear)
cv2.imwrite(os.path.join(output_dir, 'low_eq.jpg'), low_eq)
cv2.imwrite(os.path.join(output_dir, 'over_linear.jpg'), over_linear)
cv2.imwrite(os.path.join(output_dir, 'over_eq.jpg'), over_eq)
print("[任务3] 线性拉伸与直方图均衡化结果已保存")


七、主程序入口

if __name__ == "__main__":

  • Python 的惯用写法,确保该脚本被直接运行时才执行以下代码,被 import 时不执行。

img_normal_path = './DIP2026/DIP2026/bird.jpg'
img_low_path = './DIP2026/DIP2026/night.jpg'
img_over_path = './DIP2026/DIP2026/car_1.jpg'

  • 定义三幅测试图像的路径变量,分别为正常光照、低照度、曝光过度图像。

original = load_gray_image(img_normal_path)
low = load_gray_image(img_low_path)
over = load_gray_image(img_over_path)

  • 调用 load_gray_image() 读取并统一转换为灰度图。

task1_invert(original)
task2_log_transform(low, over)
task3_histogram_enhancement(low, over)

  • 按顺序执行三个实验任务。

print(f"\\n所有实验完成,结果保存在 '{output_dir}' 文件夹中。")

  • 打印完成提示。


八、整体流程总结

任务功能核心算法输入输出文件
任务1 反色处理 s = 255 – r 正常光照图 inverted.jpg
任务2 对数变换 c·log(1+r) 低照度图 + 过曝图 log_low.jpg, log_over.jpg
任务3 线性拉伸与均衡化 (x-min)/(max-min)*255 + equalizeHist 低照度图 + 过曝图 low_linear.jpg, low_eq.jpg, over_linear.jpg, over_eq.jpg

每个任务都包含处理 → 可视化对比(图像 + 直方图) → 保存三个环节,便于观察灰度变换对图像质量和像素分布的影响。

框图结构说明

层级节点说明
准备阶段 创建输出目录 确保 output_1 文件夹存在
数据加载 三路读取 load_gray_image() 自动将彩色图转灰度
任务1 反色处理 一路输入(正常图),公式简单直接,输出1个文件
任务2 对数变换 两路输入(低照度+过曝),内部定义 log_transform(),每路显示4幅图(原图、变换图、两直方图),输出2个文件
任务3 线性拉伸与均衡化 两路输入,每路执行 linear_stretch() 和 cv2.equalizeHist() 两种算法,每路显示6幅图(3图+3直方图),输出4个文件
收尾 结束 三个任务按顺序串行执行完毕后程序终止

三种灰度变换算法对比

算法公式适用场景效果
反色 s = 255 – r 正常光照 亮暗反转,底片效果
对数变换 s = c·log(1+r) 低照度 / 过曝 压缩高灰度、拉伸低灰度
线性拉伸 (x-min)/(max-min)×255 对比度不足 线性展开灰度范围
直方图均衡化 累计分布函数映射 对比度不足 非线性展平灰度分布
赞(0)
未经允许不得转载:171主机测评 » 机器学习篇---数字图像增强(灰度变换与直方图均衡)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址