OpenCV 高阶实战:图像直方图与掩码图像深度解析

发布于:2025-09-12 ⋅ 阅读:(24) ⋅ 点赞:(0)

目录

一、图像直方图:读懂图像的 “像素分布报告”

1. 什么是图像直方图?

2. 图像直方图的核心作用

(1)分析亮度分布

(2)判断对比度高低

(3)辅助图像增强与阈值分割

(4)检测色彩偏移

3、举例

4. OpenCV 直方图计算:cv2.calcHist() 详解

(1)函数语法与参数解析

(2)实战案例:计算灰度图与彩色图直方图

(3)运行结果分析

二、掩码图像(Mask):精准定位感兴趣区域

1. 什么是掩码图像?

2. 掩码的核心应用场景

3. OpenCV 掩码操作:cv2.bitwise_and() 详解

(1)函数语法

(2)实战案例:用掩码提取局部区域并计算直方图

3)运行结果分析


在计算机视觉领域,图像直方图掩码图像是两种基础且核心的技术。直方图是分析图像像素分布的 “数据眼镜”,掩码则是精准定位感兴趣区域的 “手术刀”。本文将从概念原理出发,结合 OpenCV 实战代码,详细讲解图像直方图的计算与应用、掩码图像的制作与使用,以及直方图均衡化(含自适应均衡化)的实现,帮助大家掌握这两项 OpenCV 高阶技能。

一、图像直方图:读懂图像的 “像素分布报告”

1. 什么是图像直方图?

图像直方图是描述图像像素值分布的统计图形,它将图像的像素值(通常 0-255)作为横轴,像素值出现的频次(或概率)作为纵轴,用柱状图或折线图展示。

  • 对灰度图:直方图反映不同灰度级(0-255)的像素数量;
  • 对彩色图:可分别展示蓝(B)、绿(G)、红(R)三通道的像素分布。

简单来说,直方图就像图像的 “体检报告”,通过它能快速判断图像的亮度、对比度等关键信息。

2. 图像直方图的核心作用

(1)分析亮度分布
  • 若直方图峰值集中在左侧(低像素值):图像整体偏暗;
  • 若峰值集中在右侧(高像素值):图像整体偏亮;
  • 若峰值分布均匀:图像亮度适中。
(2)判断对比度高低
  • 直方图宽度宽(像素值跨度大,从 0 到 255 覆盖完整):对比度高,细节清晰;
  • 直方图宽度窄(像素值集中在某一区间):对比度低,图像灰蒙蒙。
(3)辅助图像增强与阈值分割
  • 直方图均衡化:通过重新分布像素值,扩大对比度;
  • 阈值分割:利用直方图的 “双峰谷底” 确定分割阈值,分离前景与背景(如分割文字与背景)。
(4)检测色彩偏移

对彩色图三通道直方图对比,若某一通道(如红色)峰值偏移明显,说明图像存在色彩偏色。

3、举例

灰度值在0 - 255范围之间总共 256 个值,可以将我们的范围划分为子部分(称为bins),例

4. OpenCV 直方图计算:cv2.calcHist() 详解

(1)函数语法与参数解析
cv2.calcHist(images, channels, mask, histSize, ranges, accumulate=False)

(2)实战案例:计算灰度图与彩色图直方图
import cv2
import matplotlib.pyplot as plt
import numpy as np

# 1. 读取灰度图并计算直方图
# 读取灰度图像(以手机图像为例)
gray_img = cv2.imread("phone.png", cv2.IMREAD_GRAYSCALE)
if gray_img is None:
    print("图像读取失败,请检查文件路径!")
    exit()

# 方法1:用matplotlib直接绘制(需将图像展平为一维数组)
plt.figure(figsize=(12, 5))
plt.subplot(1, 3, 1)
plt.hist(gray_img.ravel(), bins=256, color='gray')  # ravel()将二维图像转为一维
plt.title("灰度图直方图(matplotlib)")
plt.xlabel("像素值(0-255)")
plt.ylabel("像素数量")

# 方法2:用OpenCV的cv2.calcHist()计算(bins=16,分组更粗)
hist_cv2 = cv2.calcHist([gray_img], [0], None, [16], [0, 256])
plt.subplot(1, 3, 2)
plt.plot(hist_cv2, color='black', marker='o')  # 折线图展示
plt.title("灰度图直方图(cv2.calcHist,bins=16)")
plt.xlabel("分组索引(0-15)")
plt.ylabel("像素数量")

# 2. 读取彩色图并计算三通道直方图
color_img = cv2.imread("phone.png")  # OpenCV默认BGR格式
color = ('b', 'g', 'r')  # 对应B/G/R通道

plt.subplot(1, 3, 3)
for i, col in enumerate(color):
    # 分别计算B、G、R通道的直方图
    hist_channel = cv2.calcHist([color_img], [i], None, [256], [0, 256])
    plt.plot(hist_channel, color=col, label=f"{col.upper()}通道")

plt.title("彩色图三通道直方图")
plt.xlabel("像素值(0-255)")
plt.ylabel("像素数量")
plt.legend()
plt.tight_layout()
plt.show()

# 展示原始图像
cv2.imshow("灰度图", gray_img)
cv2.imshow("彩色图(BGR)", color_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

(3)运行结果分析
  • 灰度图直方图:可直观看到图像像素集中在哪个区间(如集中在 100-200,说明图像偏亮);
  • 彩色图三通道:若 B 通道峰值高于 G/R,说明图像偏蓝;反之则偏红或偏绿;
  • bins=16 时,直方图更简洁,适合快速观察整体分布趋势。

二、掩码图像(Mask):精准定位感兴趣区域

1. 什么是掩码图像?

掩码图像是与原图像尺寸完全相同的二进制图像,像素值仅为 0 或 255(无符号 8 位整数,np.uint8):

  • 像素值为 0屏蔽区域(后续操作不处理该区域);
  • 像素值为 255保留区域(后续操作仅作用于该区域)。

形象地说,掩码就像 “照片遮罩”,只让感兴趣的区域 “透出来” 参与处理。

2. 掩码的核心应用场景

  • 计算局部区域的直方图(如仅分析人脸区域的亮度);
  • 图像修复(如仅修复口罩遮挡的面部区域);
  • 目标分割(如仅提取图像中的文字区域);
  • 图像融合(如将 logo 贴到指定区域)。

3. OpenCV 掩码操作:cv2.bitwise_and() 详解

掩码通常与按位与运算结合使用,原理是:原图像像素 & 掩码像素,只有当掩码像素为 255(二进制 11111111)时,原像素值才保留;若掩码为 0(二进制 00000000),结果为 0(黑色)。

(1)函数语法
cv2.bitwise_and(src1, src2, dst=None, mask=None)

(2)实战案例:用掩码提取局部区域并计算直方图
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 1. 读取灰度图像
gray_img = cv2.imread("phone.png", cv2.IMREAD_GRAYSCALE)
cv2.imshow("原始灰度图", gray_img)
cv2.waitKey(0)

# 2. 创建掩码图像(步骤:全0矩阵 → 局部设为255)
# 2.1 生成与原图像尺寸相同的全0矩阵(纯黑图像)
mask = np.zeros(gray_img.shape[:2], dtype=np.uint8)  # shape[:2]取高和宽
# 2.2 定义感兴趣区域(ROI):[y1:y2, x1:x2](注意:OpenCV中坐标是(y, x))
# 此处以“手机屏幕区域”为例,需根据实际图像调整坐标
mask[50:350, 100:470] = 255  # 该区域设为255(白色)

# 展示掩码图像
cv2.imshow("掩码图像(白色为保留区域)", mask)
cv2.waitKey(0)

# 3. 应用掩码:提取感兴趣区域
masked_img = cv2.bitwise_and(gray_img, gray_img, mask=mask)
cv2.imshow("掩码提取后的图像", masked_img)
cv2.waitKey(0)

# 4. 对比:全图直方图 vs 掩码区域直方图
plt.figure(figsize=(10, 4))

# 全图直方图
plt.subplot(1, 2, 1)
hist_full = cv2.calcHist([gray_img], [0], None, [256], [0, 256])
plt.plot(hist_full, color='black')
plt.title("全图直方图")
plt.xlabel("像素值")
plt.ylabel("数量")

# 掩码区域直方图
plt.subplot(1, 2, 2)
hist_masked = cv2.calcHist([gray_img], [0], mask, [256], [0, 256])
plt.plot(hist_masked, color='red')
plt.title("掩码区域(手机屏幕)直方图")
plt.xlabel("像素值")
plt.ylabel("数量")

plt.tight_layout()
plt.show()

cv2.destroyAllWindows()

运行结果如下:

3)运行结果分析
  • 掩码图像:黑色区域为屏蔽区,白色矩形为保留的 “手机屏幕区域”;
  • 掩码提取后的图像:仅屏幕区域保留原像素,其他区域为黑色;
  • 直方图对比:若屏幕区域偏亮,掩码直方图峰值会比全图直方图更靠右,精准反映局部亮度分布。

网站公告

今日签到

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