文章目录
- 前言
- 完整代码
- 准备工作:导入库与显示函数
- 图像金字塔:下采样与上采样
-
- 读取图像并统一尺寸
- 下采样(缩小)
- 上采样(放大)
- 对下采样结果再上采样
- 拉普拉斯金字塔
-
- 从残差重建原图
- 直方图
-
- 灰度直方图
- 计算直方图数据
- 彩色图像的三通道直方图
- 只统计感兴趣区域(掩码直方图)
- 计算掩码区域的直方图
- 总结
前言
在图像处理中,我们经常需要 放大 或 缩小 图像(比如适应屏幕、匹配特征点尺度),这就是 图像金字塔 的典型应用。OpenCV 提供了 pyrDown(下采样,缩小)和 pyrUp(上采样,放大)两个函数,它们基于高斯卷积,让缩放后的图像更平滑。
另一个常用工具是 直方图,它能统计图像中每个灰度值(或颜色通道)的像素数量,帮助我们分析亮度分布、对比度,甚至通过掩码(Mask)只统计感兴趣区域。
完整代码
样图:

import cv2
import numpy as np
def cv_show(name,image):
cv2.imshow(name,image)
cv2.waitKey(0)
a = cv2.imread('kele.png',cv2.IMREAD_GRAYSCALE)
kele = cv2.resize(a,(500,500))
cv2.imshow('kele',kele)
cv2.waitKey(0)
kele_down_1 = cv2.pyrDown(kele)#下采样
cv_show('kele_down',kele_down_1)
kele_down_2 = cv2.pyrDown(kele_down_1)
cv_show('kele_down_2',kele_down_2)
#上采样
kele_up_1 = cv2.pyrUp(kele)
cv_show('kele_up_1',kele_up_1)
kele_up_2 = cv2.pyrUp(kele_up_1)
cv_show('kele_up_2',kele_up_2)
###对下采样后图像进行上采样,图像变模糊,无法复原
kele_down_1_up = cv2.pyrUp(kele_down_1)
kele_down_2_up = cv2.pyrUp(kele_down_2)
cv_show('kele_down_1_up',kele_down_1_up)
cv_show('kele_down_2_up',kele_down_2_up)
###拉普拉斯金字塔
L0 = kele – kele_down_1_up
L1 = kele_down_1 – kele_down_2_up
fu_yuan = kele_down_1_up + L0
cv_show('L0',L0)
cv_show('L1',L1)
cv_show('fu_yuan',fu_yuan)
############## 直方图################################
import matplotlib.pyplot as plt
phone = cv2.imread('phone.png',cv2.IMREAD_GRAYSCALE)
a = phone.ravel()
plt.hist(a,bins=256)
plt.show()
phone_hist = cv2.calcHist([phone],[0],None,[16],[0,256])
plt.plot(phone_hist)
plt.show()
img = cv2.imread('phone.png')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.show()
phone = cv2.imread('phone.png',cv2.IMREAD_GRAYSCALE)
cv_show('phone',phone)
mask = np.zeros(phone.shape[:2],np.uint8)
mask[50:350,100:470] = 255
cv_show('mask',mask)
Phone_mask = cv2.bitwise_and(phone,phone,mask=mask)
cv_show('Phone_mask',Phone_mask)
phone_hist_mask = cv2.calcHist([phone],[0],mask,[256],[0,256])
plt.plot(phone_hist_mask)
plt.show()
准备工作:导入库与显示函数
import cv2
import numpy as np
import matplotlib.pyplot as plt
def cv_show(name, image):
cv2.imshow(name, image)
cv2.waitKey(0)
图像金字塔:下采样与上采样
读取图像并统一尺寸
a = cv2.imread('kele.png', cv2.IMREAD_GRAYSCALE)
kele = cv2.resize(a, (500, 500))
cv2.imshow('kele', kele)
cv2.waitKey(0)
以灰度模式读取 kele.png,然后缩放到 500×500 的固定尺寸,方便后续比较。
下采样(缩小)
kele_down_1 = cv2.pyrDown(kele)
cv_show('kele_down', kele_down_1)
kele_down_2 = cv2.pyrDown(kele_down_1)
cv_show('kele_down_2', kele_down_2)
-
cv2.pyrDown 会先对图像进行高斯平滑,然后去掉偶数行和偶数列,从而使图像宽高各减半(尺寸变为原来的 1/4)。
-
第一次下采样后尺寸变为 250×250,第二次变为 125×125。
这种操作会丢失细节,但保留了整体轮廓,常用于构建图像金字塔。
运行结果: 
上采样(放大)
kele_up_1 = cv2.pyrUp(kele)
cv_show('kele_up_1', kele_up_1)
kele_up_2 = cv2.pyrUp(kele_up_1)
cv_show('kele_up_2', kele_up_2)
-
cv2.pyrUp 是下采样的逆操作:先将图像宽高各加倍(插入零像素),再用高斯卷积填充新像素值。
-
第一次上采样后尺寸变为 1000×1000,第二次变为 2000×2000。
上采样会放大图像,但分辨率并不会真正提高,所以图像会显得模糊。
运行结果(右为原图): 
对下采样结果再上采样
kele_down_1_up = cv2.pyrUp(kele_down_1)
kele_down_2_up = cv2.pyrUp(kele_down_2)
cv_show('kele_down_1_up', kele_down_1_up)
cv_show('kele_down_2_up', kele_down_2_up)
将下采样后的图像(250×250)再进行上采样,尺寸回到 500×500。 虽然尺寸和原图一样,但图像已经变模糊,因为下采样丢失的像素无法通过上采样恢复。即下采样会丢失信息,是不可逆的。 后续我们可以通过获取下采样丢失的残差图像,和对下采样结果再上采样的结果图结合,恢复原图像。
运行结果: 
拉普拉斯金字塔
拉普拉斯金字塔是高斯金字塔与原图之间的残差,即下采样丢失的图像。 公式:L_i = 当前层图像 – 上一层图像上采样后的结果
L0 = kele – kele_down_1_up
L1 = kele_down_1 – kele_down_2_up
-
kele_down_1_up 是 kele_down_1 上采样回 500×500 的结果,它与原图 kele 的差值就是第一层拉普拉斯残差 L0。
-
同理 L1 是 250×250 尺寸上的残差。

从残差重建原图
fu_yuan = kele_down_1_up + L0
cv_show('fu_yuan', fu_yuan)
将下采样再上采样后的模糊图加上残差 L0,就可以完美还原原图。 拉普拉斯金字塔可以用来无损重建图像,是多分辨率分析的基础。 
直方图
灰度直方图
phone = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)
a = phone.ravel()
plt.hist(a, bins=256)
plt.show()
-
ravel() 将二维灰度图像展平为一维数组,方便统计。
-
plt.hist 绘制直方图,bins=256 表示将灰度值 0~255 划分为 256 个区间。
直方图能直观告诉我们图像的亮度分布:偏暗则集中在左侧,偏亮则集中在右侧,对比度低则柱状图狭窄。
运行结果: 
计算直方图数据
phone_hist = cv2.calcHist([phone], [0], None, [16], [0, 256])
plt.plot(phone_hist)
plt.show()
参数解释:
-
[phone]:输入图像列表(可多张)。
-
[0]:要统计的通道索引(灰度图为 0)。
-
None:没有掩码(统计全图)。
-
[16]:直方图的 bins 数量(这里只分 16 个区间,比 256 粗糙,但曲线更平滑)。
-
[0, 256]:像素值范围。

彩色图像的三通道直方图
img = cv2.imread('phone.png')
color = ('b', 'g', 'r')
for i, col in enumerate(color):
histr = cv2.calcHist([img], [i], None, [256], [0, 256])
plt.plot(histr, color=col)
plt.show()
彩色图像有 B、G、R 三个通道,我们分别计算每个通道的直方图。循环中 [i] 表示第 i 个通道,并用对应的颜色绘制曲线,方便区分。 
只统计感兴趣区域(掩码直方图)
有时候我们只想统计图像中某一局部区域的像素分布(比如检测 ROI 的亮度),这时可以用掩码。
phone = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)
cv_show('phone', phone)
mask = np.zeros(phone.shape[:2], np.uint8)
mask[50:350, 100:470] = 255
cv_show('mask', mask)
Phone_mask = cv2.bitwise_and(phone, phone, mask=mask)
cv_show('Phone_mask', Phone_mask)
先创建与图像同样大小的全零矩阵(黑色),然后将矩形区域 [50:350, 100:470] 设置为 255(白色)。这个白色区域就是我们关心的 ROI。 cv2.bitwise_and(phone, phone, mask=mask):将原图与掩码做“与”运算,掩码为白色的区域保留原像素,黑色区域变为 0。这样就得到只显示 ROI 的图像。 
计算掩码区域的直方图
phone_hist_mask = cv2.calcHist([phone], [0], mask, [256], [0, 256])
plt.plot(phone_hist_mask)
plt.show()
在 calcHist 中传入 mask 参数,它会只统计掩码为 255 区域的像素,忽略其他部分。这样我们就能单独分析 ROI 的亮度分布,非常实用(如人脸检测中只统计脸部区域)。 
和完整图像的直方图相比还是有区别的。
总结
| 下采样(缩小) | cv2.pyrDown | 构建图像金字塔,多尺度处理 |
| 上采样(放大) | cv2.pyrUp | 恢复尺寸,但会模糊 |
| 拉普拉斯残差 | 原图 – 上采样(下采样图) | 图像压缩、重建、边缘提取 |
| 灰度直方图 | plt.hist / cv2.calcHist | 分析亮度分布,辅助阈值选择 |
| 彩色直方图 | 分别统计各通道 | 分析颜色分布 |
| 掩码直方图 | 传入 mask 参数 | 只统计感兴趣区域(ROI) |


