本文全面讲解OpenCV图像预处理的七大核心技术(插值方法、边缘填充、图像矫正(透视变换)、图像掩膜、ROI切割、图像添加水印、图像噪点消除),每个知识点都配有详细解释和实用代码示例,帮助初学者建立系统的图像处理知识体系。
一、插值方法:图像缩放的核心技术
插值是在图像缩放或旋转时估算新像素值的方法,不同方法在速度和质量上有显著差异。
1. 最近邻插值
- 原理:直接取最邻近像素的值
- 特点:速度最快,但会产生锯齿
- 适用场景:实时系统或低功耗设备
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_NEAREST)
2. 双线性插值
- 原理:取周围4个像素的加权平均值
- 特点:速度与质量平衡
- 适用场景:大多数常规缩放需求
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_LINEAR)
3. 双三次插值
- 原理:取周围16个像素的加权平均值
- 特点:质量高但速度慢
- 适用场景:高质量图像处理
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_CUBIC)
4. Lanczos插值
- 原理:使用sinc函数进行插值
- 特点:最高质量,最慢速度
- 适用场景:专业图像处理
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_LANCZOS4)
5.完整示例
import cv2# 读图
cat = cv2.imread('../images/1.jpg')
w = cat.shape[1]
h = cat.shape[0]# 旋转
# 获取旋转矩阵
M = cv2.getRotationMatrix2D((w//2, h//2), 45, 0.5)# 应用仿射变换函数
dst = cv2.warpAffine(cat, M, (w, h))# 最近邻插值 flags=cv2.INTER_NEAREST
dst1 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_NEAREST)# 双线性插值 单线性插值 插两次:水平和垂直
dst2 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LINEAR)# 像素区域插值 缩小:均值滤波 放大:整数 最近邻 不是整数 双线性 4点 2 x 2
dst3 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_AREA)# 双三次插值 16个 4 x 4 w_i w_j
dst4 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_CUBIC)# Lanczos插值 64个 8 x 8
dst5 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LANCZOS4)# 显示
cv2.imshow('dst', dst)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.imshow('dst3', dst3)
cv2.imshow('dst4', dst4)
cv2.imshow('dst5', dst5)cv2.waitKey(0)
cv2.destroyAllWindows()
二、边缘填充:处理边界问题的关键
当进行卷积操作时,图像边界需要特殊处理,OpenCV提供多种填充方式。
1. 填充方法对比
方法 | 原理 | 效果示例 |
---|---|---|
BORDER_REPLICATE | 复制边界像素值 | `aaaa |
BORDER_REFLECT | 镜像反射边界 | `fedc |
BORDER_REFLECT_101 | 反射不含边界 | `gfed |
BORDER_CONSTANT | 用常数填充 | `0000 |
BORDER_WRAP | 循环填充 | `cdef |
2. 完整示例
import cv2# 读图
cat = cv2.imread('../images/1.jpg')
w = cat.shape[1]
h = cat.shape[0]# 旋转
# 获取旋转矩阵
M = cv2.getRotationMatrix2D((w//2, h//2), 45, 0.5)# 应用仿射变换函数
dst = cv2.warpAffine(cat, M, (w, h))# 最近邻插值 边界复制
dst1 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_NEAREST,borderMode=cv2.BORDER_REPLICATE)# 双线性插值 边界反射
dst2 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT)# 像素区域插值 边界反射101
dst3 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_AREA,borderMode=cv2.BORDER_REFLECT_101)# 双三次插值 边界常数
dst4 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_CUBIC,borderMode=cv2.BORDER_CONSTANT,borderValue=(0,0,255))# Lanczos插值 边界包裹
dst5 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LANCZOS4,borderMode=cv2.BORDER_WRAP)# 显示
cv2.imshow('dst', dst)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.imshow('dst3', dst3)
cv2.imshow('dst4', dst4)
cv2.imshow('dst5', dst5)cv2.waitKey(0)
cv2.destroyAllWindows()
三、图像矫正:透视变换的应用
透视变换可以将倾斜视角的图像矫正为正视角,常用于文档扫描和车牌识别。
1. 透视变换四步法
- 识别源点:选择图像中的四个特征点
- 定义目标点:确定矫正后的位置
- 计算变换矩阵:使用
getPerspectiveTransform
- 应用变换:使用
warpPerspective
2. 完整示例
import cv2
import numpy as np# 读图
card = cv2.imread('../images/3.png')
shape = card.shape# 获取透视变换矩阵
# 原图中卡片的四个角点:左上、右上、左下、右下
# [[178, 100], [487, 134], [124, 300], [490, 350]]
pt1 =np.float32([[178, 100], [487, 134], [124, 300], [490, 350]])
pt2 =np.float32([[0,0],[shape[1],0],[0,shape[0]],[shape[1],shape[1]]])M = cv2.getPerspectiveTransform(pt1,pt2)# 透视变换
dst = cv2.warpPerspective(card,M,(shape[1],shape[0]),cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT)cv2.imshow('card',card)
cv2.imshow('dst',dst)cv2.waitKey(0)
cv2.destroyAllWindows()
四、图像掩膜:精确控制处理区域
掩膜是二值化图像,用于指定哪些区域需要处理或保留,以实现特定任务的目标。
掩膜操作三部曲
- 创建掩膜:手动绘制或通过阈值处理生成
- 应用掩膜:使用按位与操作保留目标区域
- 颜色替换:修改特定区域颜色
第一种:
# 创建圆形掩膜
mask = np.zeros(img.shape[:2], dtype=np.uint8)
cv2.circle(mask, (200, 200), 100, 255, -1)# 应用掩膜
masked_img = cv2.bitwise_and(img, img, mask=mask)# 颜色替换
img[mask == 255] = (0, 255, 0) # 将掩膜区域变为绿色
第两种(完整):
cv2.inRange用于进行多通道图像(尤其是彩色图像)的阈值操作。
import cv2
import numpy as np# 读图
demo = cv2.imread('../images/demo.png')
demo = cv2.resize(demo,(500,500))# 颜色空间转换 转 HSV
demo_hsv = cv2.cvtColor(demo,cv2.COLOR_BGR2HSV)# 定义颜色范围
low = np.array([0,43,46])
high = np.array([10,255,255])# 创建掩膜 cv2.inRange(img,low,high) 传 hsv 颜色空间下的图像 二维
mask = cv2.inRange(demo_hsv,low,high)# 颜色提取 cv2.bitwise_and(src1,src2[,mask]) 传的是原图
dst = cv2.bitwise_and(demo,demo,mask=mask)cv2.imshow('demo',demo)
cv2.imshow('mask',mask)
cv2.imshow('dst',dst)cv2.waitKey(0)
cv2.destroyAllWindows()
含颜色替换操作示例:
import cv2
import numpy as np# 读图
demo = cv2.imread('../images/demo.png')
demo = cv2.resize(demo,(500,500))# 颜色空间转换 转 HSV
demo_hsv = cv2.cvtColor(demo,cv2.COLOR_BGR2HSV)# 定义颜色范围
low = np.array([0,43,46])
high = np.array([10,255,255])# 创建掩膜 cv2.inRange(img,low,high) 传 hsv 颜色空间下的图像 二维
mask = cv2.inRange(demo_hsv,low,high)# 颜色替换 布尔索引
arr = (mask == 255)
# print(arr)demo[arr] = (0, 255, 0)
# demo[mask == 255] = (0, 255, 0)cv2.imshow('demo',demo)cv2.waitKey(0)
cv2.destroyAllWindows()
五、ROI切割:聚焦关键区域
ROI(Region of Interest)是图像中需要特别关注的区域。
ROI操作技巧
# 直接选择矩形区域
roi = img[100:300, 200:400] # y1:y2, x1:x2# 在原始图像上绘制ROI框
cv2.rectangle(img, (200, 100), (400, 300), (0, 255, 0), 2)# 处理ROI区域(例如转换为灰度)
roi_gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
六、图像添加水印:版权保护技术
水印技术可分为可见水印和不可见水印。
1. 三步骤:
- 模板输入:使用模板输入所输入的图片其实是作为要添加的水印,有了水印的彩色图之后,我们需要用它来制作一个掩模,这就用到了灰度化和二值化,即先灰度化后二值化,这就得到了带有水印图案的掩模。
- 与运算:有了模板的掩膜后(也就是二值化图),在要添加水印的图像中,根据掩膜的大小切割出一个ROI区域,也就是要添加水印的区域,让其与模板的掩膜进行与运算。与运算的过程中,只要有黑色像素,那么得到的结果图中的对应位置也会是黑色像素。由于模板的掩膜中目标物体的像素值为黑色,所以经过与运算后,就会在ROI区域中得到模板图的形状。
- 图像融合:将模板的形状添加到水印区域之后,就可以将该图像与原始的模板图进行图像融合。该组件的目的就是将图像对应的数组中的对应元素进行相加(一定要注意这里的两个数组是规格相同的,也就是说要么都是灰度图,要么都是彩图),其过程如下图所示。
2. 可见水印实现
# 读取水印图片
watermark = cv2.imread("watermark.png", cv2.IMREAD_UNCHANGED)# 调整水印大小
watermark = cv2.resize(watermark, (200, 100))# 提取alpha通道
watermark_img = watermark[:, :, :3]
alpha = watermark[:, :, 3] / 255.0# 创建叠加区域
roi = img[10:110, 10:210]# 混合图像
for c in range(3):roi[:, :, c] = (1 - alpha) * roi[:, :, c] + alpha * watermark_img[:, :, c]
3. 完整示例:
import cv2# 读图
bg = cv2.imread('../images/bg.png')
logo = cv2.imread('../images/logohq.png')
h, w = logo.shape[0:2] # h, w
roi = bg[0:h, 0:w]# 转为灰度图
gray = cv2.cvtColor(logo, cv2.COLOR_BGR2GRAY)# 二值化
# 黑 logo 白底(阈值法 小于阈值的变黑,大于阈值的设maxval/变白) 与上背景
_, mask1 = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
bg1 = cv2.bitwise_and(roi, roi, mask=mask1)# 白 logo黑底(反阈值法 大于阈值变黑,小于阈值的设maxval/变白) 与上 logo
_, mask2 = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
bg2 = cv2.bitwise_and(logo, logo, mask=mask2)# 融合 两个结果相加
roi[:] = cv2.add(bg1,bg2) # 直接对 roi 修改,就能实现原背景改变,因为切片出来的 roi和原背景变量指向内存同一块地址
# dst = cv2.add(bg1,bg2)
# roi[:] = dstcv2.imshow('mask1', mask1)
cv2.imshow('bg1', bg1)
cv2.imshow('mask2', mask2)
cv2.imshow('bg2', bg2)
cv2.imshow('roi', roi)
cv2.imshow('bg', bg)cv2.waitKey(0)
cv2.destroyAllWindows()
七、图像噪点消除:提升图像质量
噪声是图像中随机出现的像素值变化,常见滤波方法如下:
1. 均值滤波
- 原理:取邻域内像素的平均值
- 效果:消除高斯噪声,但模糊边缘
blur = cv2.blur(img, (5, 5))
2. 高斯滤波
- 原理:加权平均,中心点权重最高
- 效果:有效消除高斯噪声
gaussian = cv2.GaussianBlur(img, (5, 5), 0)
3. 中值滤波
- 原理:取邻域内像素的中值
- 效果:消除椒盐噪声,保留边缘
median = cv2.medianBlur(img, 5)
4. 双边滤波
- 原理:同时考虑空间距离和像素值差异
- 效果:保留边缘同时减少噪声
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
5. 滤波器选择指南
噪声类型 | 推荐滤波器 | 参数示例 |
---|---|---|
高斯噪声 | 高斯滤波 | (5,5), σ=0 |
椒盐噪声 | 中值滤波 | 内核大小=5 |
边缘保护 | 双边滤波 | d=9, σColor=75, σSpace=75 |
6. 完整示例:
import cv2# 读图
lvbo2 = cv2.imread('../images/lvbo2.png')
lvbo3 = cv2.imread('../images/lvbo3.png')# 均值滤波
dst1 = cv2.blur(lvbo2,(3,3))# 方框滤波 底层使用饱和运算 最大255 及白的很多
dst2 = cv2.boxFilter(lvbo2,-1,(3,3),normalize=False)# 高斯滤波
dst3 = cv2.GaussianBlur(lvbo2,(3,3),1)# 中值滤波 非线性(逻辑操作)
dst4 = cv2.medianBlur(lvbo3,3)# 双边滤波
dst5 = cv2.bilateralFilter(lvbo2,7,75,75)cv2.imshow('lvbo2',lvbo2)
# cv2.imshow('lvbo3',lvbo3)
cv2.imshow('dst1',dst1)
# cv2.imshow('dst2',dst2)
# cv2.imshow('dst3',dst3)
# cv2.imshow('dst4',dst4)
cv2.imshow('dst5',dst5)cv2.waitKey(0)
cv2.destroyAllWindows()
图像预处理综合应用
def advanced_preprocessing(image_path):# 1. 读取图像img = cv2.imread(image_path)# 2. 透视变换矫正src_pts = np.float32([[58, 72], [375, 65], [35, 392], [400, 398]])dst_pts = np.float32([[0, 0], [500, 0], [0, 600], [500, 600]])M = cv2.getPerspectiveTransform(src_pts, dst_pts)corrected = cv2.warpPerspective(img, M, (500, 600))# 3. 降噪处理denoised = cv2.bilateralFilter(corrected, 9, 75, 75)# 4. 创建ROI区域roi = denoised[100:400, 150:350]# 5. 添加水印watermark = cv2.imread("watermark.png", cv2.IMREAD_UNCHANGED)watermark = cv2.resize(watermark, (100, 50))roi_watermark = denoised[10:60, 10:110]alpha = watermark[:, :, 3] / 255.0for c in range(3):roi_watermark[:, :, c] = alpha * watermark[:, :, c] + (1 - alpha) * roi_watermark[:, :, c]return denoised
总结
- 插值选择:实时系统用最近邻,质量优先用双三次
- 边缘填充:反射101(BORDER_REFLECT_101)效果最自然
- 透视变换:确保四个点形成凸四边形
- 掩膜应用:结合阈值法创建精确掩膜
- 噪声消除:
- 椒盐噪声 → 中值滤波
- 高斯噪声 → 高斯滤波
- 边缘保护 → 双边滤波