Python OpenCV学习第三天:给图片加滤镜原来这么简单
昨天把图像读写和摄像头那点事搞明白了,今天继续往下啃几何变换和图像预处理。本来以为这些都是套公式的函数调用,结果写代码的时候才发现好多细节没注意到,比如旋转个图片居然会被裁掉一半,滤波参数调错了效果差十万八千里。不过今天写出来的滤镜小程序还挺有意思的,能给图片加模糊、锐化、黑白这些效果,终于有点做东西的实感了。
今日目标完成情况
- 图像几何变换(缩放、旋转、平移、翻转)全掌握
- 搞懂简单阈值和自适应阈值的区别及适用场景
- 学会4种常用图像滤波的用法和各自的特点
- 完成了第一个交互型小项目:键盘控制的图像滤镜
一、图像几何变换:想怎么转就怎么转
几何变换就是改变图片的大小、位置和角度,都是后面做目标检测、图像拼接的基础。
1. 缩放:cv2.resize()
最简单也最常用,注意不同插值方法的区别,用错了图片会糊:
import cv2
import numpy as np
img = cv2.imread("test.jpg")
# 按比例缩放(推荐)
img_resized = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
# fx=水平缩放比例,fy=垂直缩放比例,None表示不指定固定尺寸
# 指定尺寸缩放
img_fixed = cv2.resize(img, (400, 300), interpolation=cv2.INTER_CUBIC)这里要特别说明:插值方法选不对会严重影响效果
- 缩小图片:用
cv2.INTER_AREA,效果最自然 - 放大图片:用
cv2.INTER_CUBIC(慢但清晰)或cv2.INTER_LINEAR(快但稍糊) - 默认是
INTER_LINEAR,一般够用
2. 翻转:cv2.flip()
一行代码搞定,做数据增强的时候特别有用:
img_flip_h = cv2.flip(img, 1) # 水平翻转(左右反过来)
img_flip_v = cv2.flip(img, 0) # 垂直翻转(上下反过来)
img_flip_both = cv2.flip(img, -1) # 同时水平+垂直翻转3. 旋转:最容易出问题的一个
我一开始以为传个角度就行,结果转完发现图片缺了一半!原来OpenCV的旋转是保持画布大小不变,超出画布的部分直接被裁掉了。
基础旋转(会裁边)
# 获取图片中心坐标
h, w = img.shape[:2]
center = (w//2, h//2)
# 获取旋转矩阵:(旋转中心, 旋转角度, 缩放比例)
# 角度是逆时针为正,比如90就是逆时针转90度
M = cv2.getRotationMatrix2D(center, 45, 1.0)
# 执行旋转
img_rotated = cv2.warpAffine(img, M, (w, h))不裁边的旋转(进阶)
如果不想裁边,就要自己计算旋转后的新画布大小,我找了个通用写法,直接抄就行:
def rotate_image(img, angle):
h, w = img.shape[:2]
center = (w//2, h//2)
# 获取旋转矩阵
M = cv2.getRotationMatrix2D(center, angle, 1.0)
# 计算旋转后的新尺寸
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
new_w = int(h * sin + w * cos)
new_h = int(h * cos + w * sin)
# 调整旋转矩阵的平移量,让图片居中
M[0, 2] += (new_w / 2) - center[0]
M[1, 2] += (new_h / 2) - center[1]
# 执行旋转
return cv2.warpAffine(img, M, (new_w, new_h))4. 平移
和旋转类似,需要先构造平移矩阵:
# 平移矩阵:[[1, 0, 水平平移量], [0, 1, 垂直平移量]]
# 正数向右/向下平移,负数向左/向上平移
M = np.float32([[1, 0, 50], [0, 1, 30]])
img_translated = cv2.warpAffine(img, M, (w, h))二、图像阈值处理:把图片变成黑白
阈值处理就是把彩色图转成二值图(只有黑和白),是提取目标物体的第一步。
1. 简单阈值:cv2.threshold()
全局用同一个阈值,适合光照均匀的图片:
# 先转成灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 简单阈值:(灰度图, 阈值, 最大值, 阈值类型)
# 阈值类型:
# cv2.THRESH_BINARY:大于阈值变255(白),小于变0(黑)
# cv2.THRESH_BINARY_INV:反过来,大于阈值变0,小于变255
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)2. 自适应阈值:cv2.adaptiveThreshold()
解决光照不均匀的问题!比如拍的书本照片,中间亮两边暗,用简单阈值会有一大块黑的,自适应阈值就不会。
thresh_adaptive = cv2.adaptiveThreshold(
gray, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # 自适应方法:高斯加权平均
cv2.THRESH_BINARY,
11, # 块大小:必须是奇数,越大处理越粗糙
2 # 常数:减去这个值,微调阈值
)我自己测试了一下,用一张窗边拍的照片,简单阈值出来的效果惨不忍睹,自适应阈值就清晰很多,这个真的太有用了。
三、图像滤波:给图片“磨个皮”
滤波主要用来去噪,不同的滤波对应不同的噪声类型,别用混了。
1. 均值滤波:cv2.blur()
最简单的滤波,取周围像素的平均值,去噪的同时会把边缘也弄模糊:
img_blur = cv2.blur(img, (5, 5)) # (5,5)是卷积核大小,必须是奇数2. 高斯滤波:cv2.GaussianBlur()
最常用的滤波,对高斯噪声(比如图片上的颗粒感)效果很好,比均值滤波保留更多边缘:
img_gaussian = cv2.GaussianBlur(img, (5, 5), 0) # 0表示自动计算标准差3. 中值滤波:cv2.medianBlur()
去椒盐噪声神器! 就是图片上那种随机的白点黑点,用这个效果最好:
img_median = cv2.medianBlur(img, 5) # 卷积核大小必须是奇数4. 双边滤波:cv2.bilateralFilter()
最智能的滤波,去噪的同时能保持边缘清晰,就是速度慢一点:
img_bilateral = cv2.bilateralFilter(img, 9, 75, 75)四、今日小项目:键盘控制的图像滤镜
把今天学的所有功能整合起来,写一个能通过键盘切换不同滤镜的小程序,按不同的键就能看到不同的效果:
import cv2
import numpy as np
def main():
img = cv2.imread("test.jpg")
if img is None:
print("图片读取失败!")
return
cv2.imshow("原图", img)
while True:
key = cv2.waitKey(0) & 0xFF
if key == ord('q'):
break
elif key == ord('1'):
# 均值模糊
res = cv2.blur(img, (5, 5))
cv2.imshow("滤镜效果", res)
elif key == ord('2'):
# 高斯模糊
res = cv2.GaussianBlur(img, (5, 5), 0)
cv2.imshow("滤镜效果", res)
elif key == ord('3'):
# 中值模糊
res = cv2.medianBlur(img, 5)
cv2.imshow("滤镜效果", res)
elif key == ord('4'):
# 双边滤波
res = cv2.bilateralFilter(img, 9, 75, 75)
cv2.imshow("滤镜效果", res)
elif key == ord('5'):
# 二值化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, res = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv2.imshow("滤镜效果", res)
elif key == ord('6'):
# 自适应阈值
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
res = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
cv2.imshow("滤镜效果", res)
elif key == ord('r'):
# 重置为原图
cv2.imshow("滤镜效果", img)
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
运行之后按1-6就能切换不同的滤镜,按r重置,按q退出,特别方便。
五、今日学到的关键点
- 缩放图片的时候,缩小用
INTER_AREA,放大用INTER_CUBIC,效果最好 - 默认的旋转会裁掉边缘,不想裁边就要自己计算新的画布大小
- 简单阈值适合光照均匀的图片,光照不均一定要用自适应阈值
- 不同的滤波对应不同的噪声:椒盐噪声用中值滤波,高斯噪声用高斯滤波
- 所有卷积核的大小都必须是奇数,这个是硬性规定
六、明天要学的
- Canny边缘检测和Sobel算子
- Harris和Shi-Tomasi角点检测
- 轮廓检测和轮廓特征计算
- 写一个能自动框出图片中物体的小程序
今天学了大概两个半小时,主要是旋转不裁边的那个函数花了点时间理解,不过搞懂之后就觉得很简单了。现在终于能对图片做一些有意思的处理了.