openCV高阶操作之金字塔操作与直方图分析
在计算机视觉领域,图像金字塔和直方图是基础且重要的工具。图像金字塔用于多尺度分析(如图像缩放、特征融合),直方图则用于描述图像像素分布(如亮度调整、对比度增强)。本文将通过Python和OpenCV实现这两部分功能,并结合代码详细讲解原理。
一、图像金字塔:上下采样与高斯/拉普拉斯金字塔
1.1 金字塔基础概念
图像金字塔是一种多分辨率表示方法,通过**下采样(降分辨率)和上采样(升分辨率)**生成不同尺度的图像集合。常见的金字塔有两种:
- 高斯金字塔:通过高斯模糊后下采样生成,用于多尺度表示。
- 拉普拉斯金字塔:通过高斯金字塔相邻层差值生成,用于图像重建(保留细节)。
1.2 下采样(pyrDown)
cv2.pyrDown()
用于对图像进行下采样,生成分辨率减半的图像(宽高各缩小为1/2)。其核心步骤是:
- 对原图进行高斯模糊(核大小为5x5),消除高频细节;
- 将图像尺寸从
(w, h)
缩小为(w//2, h//2)
(舍入取整)。
import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取灰度图像(G0层,原始层)
face = cv2.imread('longpic.png', cv2.IMREAD_GRAYSCALE)
cv2.imshow('Original Face (G0)', face)
cv2.waitKey(0)# 下采样生成G1层(分辨率减半)
face_down_1 = cv2.pyrDown(face) # 尺寸:(w//2, h//2)
cv2.imshow('Downsampled G1', face_down_1)
cv2.waitKey(0)# 继续下采样生成G2层(分辨率再减半)
face_down_2 = cv2.pyrDown(face_down_1) # 尺寸:(w//4, h//4)
cv2.imshow('Downsampled G2', face_down_2)
cv2.waitKey(0)
1.3 上采样(pyrUp)
cv2.pyrUp()
用于对图像进行上采样,生成分辨率翻倍的图像(宽高各放大为2倍)。其核心步骤是:
- 将图像尺寸从
(w, h)
放大为(w*2, h*2)
(新增像素用0填充); - 对放大后的图像进行高斯模糊(核大小为5x5)。
# 上采样生成G1'层(分辨率翻倍)
face_up_1 = cv2.pyrUp(face) # 尺寸:(w*2, h*2)(可能超出原图尺寸)
cv2.imshow('Upsampled G1\'', face_up_1)
cv2.waitKey(0)# 继续上采样生成G2'层(分辨率再翻倍)
face_up_2 = cv2.pyrUp(face_up_1) # 尺寸:(w*4, h*4)
cv2.imshow('Upsampled G2\'', face_up_2)
cv2.waitKey(0)
1.4 关键注意点:下采样后上采样无法完全复原
直接对下采样图像上采样会丢失细节(因为下采样时丢弃了部分高频信息)。例如,对G1层上采样得到G1’层,其尺寸与原图G0相同,但内容模糊。此时可通过拉普拉斯金字塔恢复细节。
1.5 拉普拉斯金字塔(Laplacian Pyramid)
拉普拉斯金字塔通过高斯金字塔相邻层的差值生成,用于保留图像的高频细节。公式为:
L_i = G_i - pyrUp(G_{i+1})
# 构建拉普拉斯金字塔
# G0层 - 上采样后的G1层 → L0层(高频细节)
L0 = face - cv2.pyrUp(face_down_1) # 注意:pyrUp(face_down_1)的尺寸与face相同
# G1层 - 上采样后的G2层 → L1层(次高频细节)
L1 = face_down_1 - cv2.pyrUp(face_down_2)# 从拉普拉斯金字塔恢复原始图像(G1层 + L0层)
reconstructed_face = cv2.pyrUp(face_down_1) + L0
cv2.imshow('Reconstructed Face', reconstructed_face)
cv2.waitKey(0)
二、直方图:像素分布的可视化分析
2.1 直方图的作用
直方图是图像像素灰度值的分布统计图,横轴表示灰度值(0-255),纵轴表示该灰度值的像素数量。通过直方图可以:
- 判断图像亮度(过暗/过曝);
- 分析对比度(是否集中);
- 指导图像增强(如直方图均衡化)。
2.2 计算直方图:cv2.calcHist
cv2.calcHist()
是OpenCV中计算直方图的核心函数,参数如下:
images
:输入图像(需用列表包裹,如[img]
);channels
:通道索引(灰度图为[0]
,BGR彩色图为[0]
/[1]
/[2]
);mask
:掩膜(指定统计区域,None
表示全图);histSize
:直方图bin数量(如[256]
表示256个灰度级);ranges
:像素值范围(灰度图为[0, 256]
)。
示例1:灰度图直方图
# 读取灰度图像
phone = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)# 方法1:用numpy绘制直方图(一维数组+matplotlib)
a = phone.ravel() # 展平为一维数组
plt.hist(a, bins=256, color='gray') # bins=256表示256个灰度级
plt.title('Phone Gray Histogram (numpy)')
plt.show()# 方法2:用cv2.calcHist绘制直方图曲线
phone_hist = cv2.calcHist([phone], [0], None, [256], [0, 256])
plt.plot(phone_hist, color='black')
plt.title('Phone Gray Histogram (OpenCV)')
plt.show()
示例2:彩色图直方图
彩色图像(BGR格式)需分别统计每个通道的直方图:
img = cv2.imread('phone.png') # 读取彩色图像(BGR格式)# 定义颜色映射(BGR对应蓝、绿、红)
colors = ('b', 'g', 'r')# 遍历每个通道计算直方图并绘制
for i, col in enumerate(colors):hist = cv2.calcHist([img], [i], None, [256], [0, 256])plt.plot(hist, color=col, label=f'{col.upper()} Channel')
plt.title('Phone Color Histogram')
plt.legend()
plt.show()
2.3 掩膜(Mask):统计局部区域直方图
掩膜是一个与原图同尺寸的二值图像(0表示忽略,255表示关注)。通过掩膜可以只统计图像中特定区域的直方图。
# 创建掩膜(关注中间区域:行50-350,列100-470)
mask = np.zeros(phone.shape, dtype=np.uint8)
mask[50:350, 100:470] = 255 # 中间矩形区域设为255(关注)# 应用掩膜:统计局部区域的灰度分布
phone_masked = cv2.bitwise_and(phone, phone, mask=mask) # 掩膜后的图像
phone_hist_mask = cv2.calcHist([phone], [0], mask, [256], [0, 256])# 可视化结果
cv2.imshow('Mask', mask)
cv2.imshow('Masked Phone', phone_masked)
plt.plot(phone_hist_mask, color='red')
plt.title('Phone Local Histogram (Masked)')
plt.show()
cv2.waitKey(0)
三、总结
本文通过代码实践讲解了:
- 图像金字塔:下采样(
pyrDown
)和上采样(pyrUp
)的原理与应用,以及如何通过拉普拉斯金字塔保留细节; - 直方图分析:使用
cv2.calcHist
计算全局/局部直方图,并通过matplotlib可视化,理解像素分布对图像的影响。
实际应用中,金字塔常用于图像拼接、目标检测(多尺度匹配),直方图则用于图像增强(如直方图均衡化)、缺陷检测(如亮度不均)。建议读者通过调整代码参数(如掩膜区域、金字塔层数)进一步探索效果!