一、前言
今天学习了 OpenCV 中几种常见的图像处理方法,包括图像金字塔、直方图、直方图均衡化、掩膜、角点检测、SIFT 特征提取以及图像旋转。
这些操作是计算机视觉的基础。在很多项目中,我们都需要先调整图像大小、增强对比度、提取关键特征,然后才能继续完成目标识别和图像匹配。
二、图像金字塔
图像金字塔是一种多尺度表示方法。它通过不断缩小或放大图像,得到不同分辨率的图片。
1. 图像下采样
下采样就是缩小图像,OpenCV 中使用 cv2.pyrDown():
import cv2
image = cv2.imread(
"face.png",
cv2.IMREAD_GRAYSCALE
)
image_down_1 = cv2.pyrDown(image)
image_down_2 = cv2.pyrDown(image_down_1)
cv2.imshow("original", image)
cv2.imshow("down_1", image_down_1)
cv2.imshow("down_2", image_down_2)
cv2.waitKey(0)
cv2.destroyAllWindows()
每次调用 pyrDown(),图像的宽度和高度通常会缩小为原来的一半。例如:
原图:800×600
第一次下采样:400×300
第二次下采样:200×150
下采样并不是简单地删除像素,而是会先对图像进行平滑处理,再缩小尺寸,这样可以减少锯齿和噪声。
2. 图像上采样
上采样就是放大图像:
image_up = cv2.pyrUp(image)
cv2.imshow("up", image_up)
cv2.waitKey(0)
cv2.destroyAllWindows()
如果先缩小再放大:
image_down = cv2.pyrDown(image)
image_restore = cv2.pyrUp(image_down)
虽然图像尺寸可能恢复,但清晰度通常无法完全恢复。这是因为下采样过程中已经丢失了一部分像素信息,放大时只能通过插值估计。
图像金字塔常用于:
- 多尺度目标检测;
- 图像融合;
- 图像压缩;
- 图片拼接;
- 边缘和细节分析。
三、拉普拉斯金字塔
高斯金字塔保存不同尺寸的图像,拉普拉斯金字塔则保存图像在不同尺度之间的细节变化。
down = cv2.pyrDown(image)
up = cv2.pyrUp(down)
laplacian = image - up
拉普拉斯图像主要保存边缘和细节信息。它可以用于图像重建和图像融合。
简单理解:
高斯金字塔:保存图像的整体内容
拉普拉斯金字塔:保存图像的细节变化
四、图像直方图
图像直方图表示图像中像素值的分布情况。
对于灰度图:
- 0 表示黑色;
- 255 表示白色;
- 横坐标表示灰度值;
- 纵坐标表示像素数量。
如果直方图集中在左边,说明图像比较暗;如果集中在右边,说明图像比较亮;如果灰度分布范围很窄,说明图像对比度可能较低。
1. 使用Matplotlib绘制
import matplotlib.pyplot as plt
phone = cv2.imread(
"phone.png",
cv2.IMREAD_GRAYSCALE
)
pixels = phone.ravel()
plt.hist(
pixels,
bins=256,
range=(0, 256)
)
plt.show()
ravel() 可以将二维图像数组转换成一维数组,方便绘制直方图。
2. 使用OpenCV计算
hist = cv2.calcHist(
[phone],
[0],
None,
[256],
[0, 256]
)
plt.plot(hist)
plt.show()
cv2.calcHist() 的常见参数为:
images:输入图像
channels:通道编号
mask:掩膜
histSize:区间数量
ranges:像素范围
如果不需要统计每一个灰度值,也可以将区间数量设置为 16:
hist = cv2.calcHist(
[phone],
[0],
None,
[16],
[0, 256]
)
这样会把 0 到 255 分成 16 个区间。
五、彩色图像直方图
OpenCV 使用 BGR 颜色顺序,因此可以分别计算三个颜色通道:
image = cv2.imread("phone.png")
colors = ("b", "g", "r")
for i, color in enumerate(colors):
hist = cv2.calcHist(
[image],
[i],
None,
[256],
[0, 256]
)
plt.plot(
hist,
color=color
)
plt.show()
三个通道分别是:
- 0:蓝色;
- 1:绿色;
- 2:红色。
通过观察三个通道的直方图,可以了解图片中不同颜色的分布情况。
六、掩膜与局部直方图
掩膜可以指定图像中需要处理的区域。
mask = np.zeros(
phone.shape[:2],
np.uint8
)
mask[50:350, 100:470] = 255
掩膜中的白色区域表示参与计算,黑色区域表示忽略。
提取指定区域:
phone_mask = cv2.bitwise_and(
phone,
phone,
mask=mask
)
cv2.imshow("mask_image", phone_mask)
cv2.waitKey(0)
计算局部区域的直方图:
hist_mask = cv2.calcHist(
[phone],
[0],
mask,
[256],
[0, 256]
)
plt.plot(hist_mask)
plt.show()
掩膜可以应用在:
- 人脸区域分析;
- 车牌区域分析;
- 目标物体统计;
- 忽略复杂背景。
七、直方图均衡化
当图像对比度较低时,可以使用直方图均衡化增强图像:
woman = cv2.imread(
"woman.png",
cv2.IMREAD_GRAYSCALE
)
equalized = cv2.equalizeHist(woman)
查看原图和处理后的图像:
result = np.hstack(
(woman, equalized)
)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
直方图均衡化会重新分配灰度值,使原来集中在某个区域的像素分布更加分散,从而提高对比度。
不过,全局均衡化对光照不均匀的图片不一定理想。某些区域可能变得太亮,或者暗部细节仍然不明显。
八、自适应直方图均衡化
自适应均衡化会把图像划分成多个小区域,分别进行直方图处理。
clahe = cv2.createCLAHE(
clipLimit=1,
tileGridSize=(16, 16)
)
clahe_image = clahe.apply(woman)
其中:
clipLimit 用于限制对比度增强程度;
tileGridSize 表示局部区域大小。
对比三种图像:
result = np.hstack(
(woman, equalized, clahe_image)
)
cv2.imshow("compare", result)
cv2.waitKey(0)
CLAHE 特别适合光照不均匀的图片,例如夜间照片、监控图像和医学影像。
九、角点检测
角点是图像中灰度变化明显的位置。例如,矩形的四个拐角就是典型角点。
使用 Harris 算法:
image = cv2.imread(
"huanghelou.png"
)
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
dst = cv2.cornerHarris(
gray,
4,
3,
0.04
)
根据响应值筛选角点:
image[
dst > 0.01 * dst.max()
] = [0, 0, 255]
最后显示结果:
cv2.imshow("corners", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
角点检测常用于:
图像配准;
物体定位;
图像拼接;
运动分析。
十、SIFT特征提取
SIFT 可以检测图像中的关键点,并计算对应的特征描述符。
image = cv2.imread("man.png")
gray = cv2.cvtColor(
image,
cv2.COLOR_BGR2GRAY
)
sift = cv2.SIFT_create()
keypoints = sift.detect(gray)
绘制关键点:
result = cv2.drawKeypoints(
image,
keypoints,
None,
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
)
cv2.imshow("sift", result)
cv2.waitKey(0)
计算描述符:
keypoints, descriptors = \
sift.compute(
gray,
keypoints
)
print(len(keypoints))
print(descriptors.shape)
SIFT 关键点不仅包含坐标,还包含大小、方向和响应值。描述符可以用于后续两张图片之间的特征匹配。
十一、图像旋转
使用 NumPy 旋转:
rotated_1 = np.rot90(
image,
k=-1
)
rotated_2 = np.rot90(
image,
k=1
)
使用 OpenCV 旋转:
clockwise = cv2.rotate(
image,
cv2.ROTATE_90_CLOCKWISE
)
counterclockwise = cv2.rotate(
image,
cv2.ROTATE_90_COUNTERCLOCKWISE
)
rotated_180 = cv2.rotate(
image,
cv2.ROTATE_180
)
图像旋转在处理手机照片、证件图片和竖屏图片时非常实用。
总结
本篇学习了 OpenCV 的图像增强和特征提取基础。
图像金字塔可以对图像进行多尺度处理;直方图可以分析像素分布;掩膜可以选择局部区域;直方图均衡化可以增强对比度;角点和 SIFT 可以提取图像中的关键特征;图像旋转则可以调整图片方向。
这些操作虽然看起来比较基础,但它们经常作为后续图像识别任务的预处理步骤。只有先把图片处理好,后面的轮廓检测、模板匹配和 OCR 识别才能取得更好的效果。