OpenCV 图像处理实战:从图像金字塔到直方图分析

发布于:2025-09-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

OpenCV 图像处理实战:从图像金字塔到直方图分析

在计算机视觉领域,OpenCV 是一款功能强大的开源库,提供了丰富的图像处理函数。本文将围绕一段 OpenCV 实战代码,详细讲解图像金字塔(上采样与下采样)、图像直方图计算以及掩码操作的实现过程与原理,帮助大家快速掌握这些核心图像处理技术。

一、准备工作:环境与图像

在开始之前,需确保已安装所需的 Python 库:OpenCV(cv2)、Matplotlib(绘图)和 NumPy(数值计算)。可通过以下命令安装:

pip install opencv-python matplotlib numpy

代码中用到了 3 张测试图像:mywife.jpg(用于图像金字塔操作)、love.jpg(用于直方图分析)、phone.png(用于掩码与直方图结合操作)。建议将图像与代码放在同一目录下,或在cv2.imread()中填写完整图像路径。

二、核心技术 1:图像金字塔(上采样与下采样)

图像金字塔是一种多尺度表示方法,通过 “下采样”(缩小图像)和 “上采样”(放大图像)构建不同分辨率的图像序列。但需注意:下采样会丢失图像信息,再对下采样图像上采样无法完全复原原图。

1.1 下采样(pyrDown):缩小图像

下采样通过cv2.pyrDown()实现,核心步骤是:先对图像进行高斯滤波(去除高频噪声),再删除图像的偶数行和偶数列,最终图像尺寸变为原图像的 1/2(宽和高均减半)。

代码解析

# 读取灰度图像(cv2.IMREAD_GRAYSCALE表示灰度模式)

face = cv2.imread('mywife.jpg', cv2.IMREAD_GRAYSCALE)

cv2.imshow('face', face) # 显示原图

cv2.waitKey(0) # 等待按键(0表示任意键关闭窗口)

# 第一次下采样:尺寸变为原图1/2

face_down_1 = cv2.pyrDown(face)

cv2.imshow('down_1', face_down_1)

cv2.waitKey(0)

# 第二次下采样:尺寸变为down_1的1/2(原图的1/4)

face_down_2 = cv2.pyrDown(face_down_1)

cv2.imshow('down_2', face_down_2)

cv2.waitKey(0)

运行效果:连续按任意键,会依次显示 “原图→1/2 尺寸图→1/4 尺寸图”,图像逐渐变小,但视觉上仍保持基本轮廓(因高斯滤波平滑了边缘)。

1.2 上采样(pyrUp):放大图像

上采样通过cv2.pyrUp()实现,核心步骤是:先将图像的宽和高各扩大 2 倍(新增行 / 列填充 0),再对图像进行高斯滤波(平滑填充的空白区域)。但由于下采样已丢失信息,上采样无法恢复原图细节。

代码解析

# 对原图直接上采样:尺寸变为原图2倍

face_up_1 = cv2.pyrUp(face)

cv2.imshow('up_1', face_up_1)

cv2.waitKey(0)

# 第二次上采样:尺寸变为up_1的2倍(原图的4倍)

face_up_2 = cv2.pyrUp(face_up_1)

cv2.imshow('up_2', face_up_2)

cv2.waitKey(0)

# 关键对比:对下采样图像上采样(无法复原)

face_down_1_up = cv2.pyrUp(face_down_1) # down_1(1/2)→ up后(原图尺寸)

face_down_2_up = cv2.pyrUp(face_down_2) # down_2(1/4)→ up后(1/2尺寸)

cv2.imshow('down_1_up', face_down_1_up) # 与原图对比,明显模糊

cv2.imshow('down_2_up', face_down_2_up)

cv2.waitKey(0)

核心结论:face_down_1_up的尺寸与原图相同,但因下采样时丢失了高频细节(如边缘、纹理),上采样后图像会模糊,无法完全复原原图。

1.3 拉普拉斯金字塔:提取图像残差

为了保留下采样丢失的信息,可通过 “拉普拉斯金字塔” 计算 “残差图像”(原图与 “下采样 + 上采样” 图像的差值)。残差图像包含了下采样丢失的高频细节,可用于图像复原或融合。

代码解析

# 计算残差(拉普拉斯金字塔层)

L0 = face - face_down_1_up # 原图与down_1_up的差值(保留原图高频细节)

L1 = face_down_1 - face_down_2_up # down_1与down_2_up的差值

# 显示残差图像(暗部为0,亮部为丢失的细节)

cv2.imshow('L0', L0)

cv2.imshow('L1', L1)

cv2.waitKey(0)

# 尝试复原:残差 + 下采样上采样图像

fuyuan = face_down_1_up + L0 # down_1_up(模糊图)+ L0(残差)→ 接近原图

cv2.imshow('fuyuan', fuyuan)

cv2.waitKey(0)

运行效果:残差图像(L0、L1)中,亮区域对应原图中边缘、纹理等高频细节;复原后的图像(fuyuan)与原图几乎一致,证明残差图像成功保留了丢失的信息。

三、核心技术 2:图像直方图分析

图像直方图是反映图像像素灰度分布的工具,通过统计每个灰度值(0-255)的像素数量,可直观了解图像的亮度、对比度等特征。OpenCV 的cv2.calcHist()和 Matplotlib 的plt.hist()均可计算直方图。

3.1 灰度图像直方图(两种方法)

以love.jpg为例,分别用 Matplotlib 和 OpenCV 计算灰度图像的直方图。

方法 1:Matplotlib plt.hist ()
# 读取灰度图像

love = cv2.imread('love.jpg', cv2.IMREAD_GRAYSCALE)

# 将图像展平为1维数组(ravel():多维数组→1维)

a = love.ravel()

# 绘制直方图:bins=256(灰度级0-255)

plt.hist(a, bins=256)

plt.title('Gray Image Histogram (Matplotlib)')

plt.xlabel('Gray Level')

plt.ylabel('Pixel Count')

plt.show()
方法 2:OpenCV cv2.calcHist ()
# 计算直方图:[love](输入图像)、[0](通道索引,灰度图仅1通道)

# None(无掩码)、[16](直方图 bins 数量,合并灰度级)、[0,256](灰度范围)

love_hist = cv2.calcHist([love], [0], None, [16], [0,256])

# 绘制直方图(OpenCV计算结果需用plt.plot())

plt.plot(love_hist)

plt.title('Gray Image Histogram (OpenCV, bins=16)')

plt.xlabel('Binned Gray Level')

plt.ylabel('Pixel Count')

plt.show()

对比说明:plt.hist()直接对展平后的像素数组统计,bins=256 时可显示每个灰度级的分布;cv2.calcHist()支持指定 bins 数量(如 16),将 256 个灰度级合并为 16 组,直方图更简洁。

3.2 彩色图像直方图

彩色图像包含 B(蓝)、G(绿)、R(红)3 个通道,需分别计算每个通道的直方图并绘制。

代码解析

# 读取彩色图像(cv2.imread()默认返回BGR格式,非RGB)

img = cv2.imread('love.jpg')

color = ('b', 'g', 'r') # 对应B、G、R通道

# 遍历3个通道,计算并绘制直方图

for i, col in enumerate(color):

# i:通道索引(0=B,1=G,2=R)

hister = cv2.calcHist([img], [i], None, [256], [0,256])

plt.plot(hister, color=col) # 按通道颜色绘制

plt.title('Color Image Histogram (BGR)')

plt.xlabel('Gray Level')

plt.ylabel('Pixel Count')

plt.show()

注意事项:OpenCV 读取的彩色图像默认是 BGR 格式,而 Matplotlib 显示时默认 RGB 格式,但此处仅绘制直方图,通道顺序不影响统计结果,只需确保color元组与通道索引对应即可。

四、核心技术 3:掩码(mask)与直方图结合

掩码(mask)是一种 “区域选择” 工具,通过创建与图像尺寸相同的二进制数组(0 表示 “排除”,255 表示 “保留”),可仅对图像的指定区域进行处理(如计算直方图、滤波等)。

4.1 创建掩码与区域选择

以phone.png为例,创建掩码并选择手机图像的中间区域:

代码解析

# 读取手机灰度图像

phone = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)

cv2.imshow('phone', phone)

cv2.waitKey(0)

# 创建全黑掩码(尺寸与phone相同,uint8类型)

mask = np.zeros(phone.shape[:2], np.uint8)

# 将掩码的[50:350, 100:470]区域设为255(白色,表示保留该区域)

mask[50:350, 100:470] = 255

cv2.imshow('mask', mask) # 显示掩码:中间白色区域为选中区域

cv2.waitKey(0)

掩码原理:mask的尺寸与原图一致,像素值为 255 的区域会被 “保留”,0 的区域会被 “排除”。通过调整[50:350, 100:470]的坐标,可选择不同的目标区域。

4.2 掩码与图像结合(bitwise_and)

通过cv2.bitwise_and()函数,可将掩码应用到原图,仅保留掩码选中的区域:

# 按掩码提取区域:仅保留mask为255的像素

phone_mask = cv2.bitwise_and(phone, phone, mask=mask)

cv2.imshow('phone_mask', phone_mask) # 显示:仅中间区域有图像,其余为黑

cv2.waitKey(0)

函数作用:cv2.bitwise_and(a, b, mask)对 a 和 b 进行按位与操作,但仅在 mask 为 255 的区域执行,最终实现 “区域裁剪” 效果。

4.3 掩码区域的直方图

仅计算掩码选中区域的直方图,可更精准地分析目标区域的像素分布:

# 计算掩码区域的直方图:mask参数指定仅统计选中区域

phone_hist_mask = cv2.calcHist([phone], [0], mask, [256], [0,256])

plt.plot(phone_hist_mask)

plt.title('Histogram of Masked Area (Phone)')

plt.xlabel('Gray Level')

plt.ylabel('Pixel Count')

plt.show()

# 关闭所有OpenCV窗口

cv2.destroyAllWindows()

实际意义:若需分析图像中特定物体(如手机屏幕)的亮度分布,通过掩码仅统计目标区域的直方图,可避免背景像素的干扰,结果更准确。

五、总结与拓展

本文通过一段实战代码,覆盖了 OpenCV 中 3 个核心图像处理技术:

  1. 图像金字塔:下采样(缩小 + 平滑)、上采样(放大 + 填充),以及拉普拉斯金字塔(提取残差,用于复原);
  1. 直方图分析:灰度图 / 彩色图的直方图计算,两种工具(Matplotlib/OpenCV)的对比;
  1. 掩码操作:通过二进制掩码选择区域,结合直方图实现精准的区域分析。

希望本文能帮助大家快速上手这些实用技术,后续可结合具体场景(如人脸识别、目标检测)进一步深化应用!


网站公告

今日签到

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