OpenCV学习笔记
一、基础
1.图像读取
import cv2
import numpy as np
def release_windows():
cv2.waitKey()
cv2.destroyAllWindows()
def name_window(): # 创建一个窗口,让图像在窗口中显示
cv2.namedWindow("hello")
apple = cv2.imread("apple.jpg")
cv2.imshow("hello", apple)
release_windows()
# 但是实际上不创建窗口,在调用imshow的时候会自动创建
def img_reader():
img = cv2.imread("path+file_name")
cv2.imshow("window_name", img)
cv2.waitKey()
cv2.destroyAllWindows()
if __name__ == "__mainn__":
img_reader()
2.视频读取
import cv2
def read_video():
video = cv2.VideoCapture("test.mp4")
ret, frame = video.read()
while ret:
ret, frame = video.read() # 自动迭代读取每一帧
if ret:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow("frame", gray)
cv2.waitKey(1) # 设置每一帧之间的间隔
if __name__ == "__mainn__":
read_video()
3.BGR通道
import cv2
def bgr(): # 显示一个BGR
image = np.zeros((300, 300, 3), dtype=np.uint8)
image[:, 0:100, 0] = 255
image[:, 100:200, 1] = 255
image[:, 200:300, 2] = 255
cv2.imshow("bgr", image)
release_windows()
if __name__ == "__mainn__":
bgr()
4.生成随机图像
def random_image():
rand = np.random.randint(0, 255, size=[1024, 1024, 3], dtype=np.uint8)
cv2.imshow("random_image", rand)
release_windows()
if __name__ == "__mainn__":
random_image()
5.通道分离
def depart_bgr_index():
# 使用索引分离bgr通道
apple = cv2.imread("apple.jpg", cv2.IMREAD_UNCHANGED) # 以不可变模式打开图像
b = apple[:, :, 0]
cv2.imshow("b", b)
release_windows()
g = apple[:, :, 1]
cv2.imshow("g", g)
release_windows()
r = apple[:, :, 2]
cv2.imshow("r", r)
release_windows()
# 使用函数分离bgr通道
def depart_bgr_function():
apple = cv2.imread("apple.jpg")
b = cv2.split(apple)[0]
cv2.imshow("b", b)
release_windows()
g = cv2.split(apple)[1]
cv2.imshow("g", g)
release_windows()
r = cv2.split(apple)[2]
cv2.imshow("r", r)
release_windows()
apple[:, :, 2] = 0
cv2.imshow("apple without red", apple)
release_windows()
6.通道合并
def bgr_combine():
apple = cv2.imread("apple.jpg")
b = cv2.split(apple)[0]
g = cv2.split(apple)[1]
r = cv2.split(apple)[2]
rgb = cv2.merge([r, g, b]) # 改变通道顺序bgr——>rgb
cv2.imshow("combine", rgb)
release_windows()
7.获取图像信息
def get_info():
apple_gray = cv2.imread("apple.jpg", 0)
apple_color = cv2.imread("apple.jpg")
print("gray:", apple_gray.shape, "color", apple_color.shape)
print("shape's type:", type(apple_color.shape))
print(apple_gray.size, apple_color.size)
print(apple_gray.dtype, apple_color.dtype)
# 运行结果
# gray: (797, 1200) color (797, 1200, 3) # 分别对应宽(width)、高(height)、通道数(channel)
# shape's type: <class 'tuple'> # 通过访问图像对象的shape属性得到一个三元组
# 956400 2869200
# uint8 uint8 数据类型是无符号8位整数
二、进阶一点的基础知识
1.图像相加
import cv2
import numpy as np
def show_and_release():
cv2.waitKey()
cv2.destroyAllWindows()
def image_add():
# 直接相加,结果会对256取模
apple = cv2.imread("apple.jpg")[0:512, 0:512, 2]
pear = cv2.imread("pear.jpg")[0:512, 0:512, 2]
add = apple + pear
cv2.imshow("add", add)
show_and_release()
# 使用用函数相加,结果不会对256取模,而是达到255就不变了
def image_add_function():
apple = cv2.imread("apple.jpg")[0:512, 0:512, 2]
pear = cv2.imread("pear.jpg")[0:512, 0:512, 2]
add = cv2.add(apple, pear)
cv2.imshow("add", add)
show_and_release()
if __name__ == "__main__":
image_add()
image_add_function()
2.权重和
def add_weighted():
apple = cv2.imread("apple.jpg")[0:512, 0:512]
pear = cv2.imread("pear.jpg")[0:512, 0:512]
add = cv2.addWeighted(apple, 0.3, pear, 0.6, 3) # 在相加时为每一个图像添加权重
cv2.imshow("add", add)
show_and_release()
3.图像的逻辑运算
(1)逻辑与
以BGR模式打开图像,每一个图像中的像素点都是一个uint8,可以写为八位二进制数
逻辑与运算就是把两张图片的每个像素的每一位进行逻辑与运算生成一个新的像素,如
11101010
&
10101101
10101000
# 只有两个图片大小相同,通道数相同才能进行逻辑运算
def bit_and():
apple = cv2.imread('apple.jpg', 0)
pear = cv2.imread('pear.jpg', 0)
res = cv2.bitwise_and(apple[100:500, 100:500], pear[100:500, 100:500])
cv2.imshow("res", res)
cv2.imwrite("test_picture/bit_and.jpg", res)
show_and_release()
(2)逻辑或
同理
def bit_or():
apple = cv2.imread('apple.jpg', 0)
pear = cv2.imread('pear.jpg', 0)
res = cv2.bitwise_or(apple[100:500, 100:500], pear[100:500, 100:500])
cv2.imshow("res", res)
show_and_release()
(3)逻辑异或
当两个算子值相同时返回1,不同时返回0
def bit_xor():
apple = cv2.imread('apple.jpg', 0)
pear = cv2.imread('pear.jpg', 0)
res = cv2.bitwise_xor(apple[100:500, 100:500], pear[100:500, 100:500])
cv2.imshow("res", res)
show_and_release()
(4)逻辑非
对每一位取反
def bit_not():
apple = cv2.imread("apple.jpg")
pear = cv2.imread("pear.jpg")
res = cv2.bitwise_not(apple)
res2 = cv2.bitwise_not(pear)
cv2.imshow("not apple", res)
cv2.imshow("not pear", res2)
show_and_release()
4.掩码
图像运算中通常会有三个参数:img_0, img_1, [, mask]
其中mask就是掩码,表示在计算时只在掩码中值为真的像素上进行计算
def add_mask():
apple = cv2.imread("apple.jpg")[0:512, 0:512]
pear = cv2.imread("pear.jpg")[0:512, 0:512]
mask = np.zeros((512, 512), dtype=np.uint8)
mask[300:500, 300:500] = 255 # 只在300-500这个区域进行相加
res = cv2.bitwise_and(apple, pear, mask=mask)
cv2.imshow("res", res)
show_and_release()
5.位平面分解
每一个像素都可以写为一个八位的二进制数,把每一位提取出来可以形成一个新的图像,根据提取的位次不同,所得到的图片与原图的相关度也不同
def bit_depart():
apple = cv2.imread("pear.jpg", 0)
cv2.imshow("apple", apple)
show_and_release()
h, w = apple.shape
bit = np.zeros((h, w, 8), dtype=np.uint8) # 创建提矩阵
for i in range(8):
bit[:, :, i] = 2 ** i # 给提取矩阵标记
for i in range(8):
temp = bit[:, :, i]
res = cv2.bitwise_and(temp, apple)
mask = res[:, :] > 0
res[mask] = 255 # 阈值处理
cv2.imshow("res", res)
show_and_release()
应用:给图像打上数字水印
各个位平面与原图像的相关度呈现递增趋势,第0个位平面相关度最小,去掉的话人的肉眼也无法分辨,可以将这个平面用一串数字水印替代,达到防伪和版权声明的作用
6.色彩空间
①常见色彩空间
(1)GRAY
(2)XYZ
(3)YCrCb
(4)HSV
(5)HLS
②色彩空间的类型转换函数:
cv2.cvtColor(img, code)
def convert_colorspace():
apple = cv2.imread("apple.jpg") # 默认打开为BGR色彩空间
apple = cv2.cvtColor(apple, cv2.COLOR_BGR2GRAY) # 将当前的图像转换为
cv2.imshow("apple", apple)
show_close()
7.HSV(one of Human Visual System)色彩空间细节
(1)H(Hue) 色调
取值范围 [0, 360]
取不同的值表示不同的颜色
因为8位图像最大能表示256个灰度级,因此使用8位图像表示HSV图像时需要将其映射到[0, 255]范围内,在opencv中采取除以2的策略表示
(2)S(Saturation)饱和度
取值范围 [0, 1]
同样需映射到[0, 255]
(3)V (Value)亮度
取值范围 [0, 1]
需映射到[0, 255]
8.alpha通道
在BGR图像中还可增加一个A(alpha)通道表示透明度,取值[0, 255]
从透明-不透明
def alpha():
apple = cv2.imread("apple.jpg", 1)
print(apple.shape, '\n\n')
apple = cv2.cvtColor(apple, cv2.COLOR_BGR2BGRA)
print(apple.shape, '\n\n')
print(apple) # alpha默认值为255
三、进阶一点的操作
1.几何变换
(1)缩放
def resize():
apple = cv2.imread("apple.jpg")
cv2.imshow("apple-org", apple)
dst = cv2.resize(apple, [512, 512]) #实际上后面还有一个插值方式的参数,具体用到的时候再说吧
cv2.imshow("apple", dst)
show_release()
(2)翻转
def flip():
apple = cv2.imread("pear.jpg")
# cv2.imshow("apple", apple)
dst = cv2.flip(apple, 0)
# 0 上下对称翻转,任意正数左右对称翻转,任意负数沿对角线对称翻转
cv2.imshow("flip", dst)
show_release()
(3)仿射
这个有点复杂,暂时搁置
2.阈值处理
(1)手动阈值处理
def threshold():
apple = cv2.imread("apple.jpg")
cv2.imshow("apple", apple)
t, res = cv2.threshold(apple, 80, 180, cv2.THRESH_BINARY)
# 参数说明
# t 是返回的阈值,res是处理好的图像
# apple是待处理的图像,200是阈值,20是大于阈值的处理值,cv2.[]是对未到阈值的值的处理方式
cv2.imshow("res", res)
release_windows()
(2) 自动阈值处理
def adaptive_threshold():
# 自适应阈值处理
apple = cv2.imread("apple.jpg", 0)
res1 = cv2.adaptiveThreshold(apple, 127, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 5, 3)
res2 = cv2.adaptiveThreshold(apple, 127, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 3)
# 参数解释 apple 是待处理图像,255 是自适应的大于阈值的处理值,ADAPTIVE_是自适应方式,
# THRESH是阈值处理方式,5是邻域尺寸,3是常量在计算时被减去
cv2.imshow("res1", res1)
cv2.imshow("res2", res2)
release_windows()
(3)自动最佳阈值处理
def otsu_method():
# otsu方法会遍历所有阈值,找到最佳的
# 适用于双通道图像
apple = cv2.imread("apple.jpg", 0)
t, otsu = cv2.threshold(apple, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
print("最佳阈值:", t)
cv2.imshow("best", otsu)
release_windows()
3.平滑处理
def smoothing():
apple = cv2.imread("apple.jpg")
# cv2.imshow("apple_0", apple)
mess = np.zeros(apple.shape, dtype=np.uint8) + np.random.randint(0, 5, size=apple.shape, dtype=np.uint8)
apple += mess
# 制造噪点
cv2.imshow("apple_mess", apple)
# blur 均值滤波
# res1 = cv2.blur(apple, (3, 3))
# cv2.imshow("apple_blur", res1)
# release_windows()
# boxFilter 方框滤波
# res2 = cv2.boxFilter(apple, -1, (3, 3), normalize=1)
# # 大多数情况下使用默认参数就行
# # 参数依次是待处理图像 处理深度 核大小 是否归一化(取1和blur没差别,取0表示使用邻域面积的和)
# cv2.imshow("res2", res2)
# release_windows()
# # GaussianBlur 高斯滤波
# # 按照离中心的距离权重进行滤波
# res3 = cv2.GaussianBlur(apple, (3, 3), 0, 0)
# # 参数依次是待处理的图像 滤波核大小 sigmaX, sigmaY,后两个一般默认0
# cv2.imshow("res3", res3)
# release_windows()
# # medianBlur 中值滤波
# # 使用邻域的中间值代替当前的像素
# res4 = cv2.medianBlur(apple, 5)
# # 参数: 待处理图像,核大小
# cv2.imshow("res4", res4)
# release_windows()
# # bilateralFilte 双边滤波
# # 可以很好地保留边界信息
# res5 = cv2.bilateralFilter(apple, 5, 220, 220)
# cv2.imshow("res5", res5)
# release_windows()
# filter2D 自定义卷积核
kernel = np.ones((5, 5,), np.float32)/25
res6 = cv2.filter2D(apple, -1, kernel=kernel)
# 参数: 待处理图像,处理深度(-1表示使用和原图相同的深度),自定义的卷积核
cv2.imshow("res6", res6)
release_windows()
4.形态学操作
一般处理图像都将其转化为二值图像
(1)腐蚀
能够腐蚀掉图像的毛疵,实现分离边界
原理:
卷积核遍历图像中的每一个像素,只有卷积核全部位于同一值中才将核的中心设置为该前景图,否则设置为背景图
ef erode():
# 一般用于处理二值图像,起分割作用
kernel = np.ones((3, 3), np.uint8)
pic = cv2.imread("erode.bmp", cv2.IMREAD_UNCHANGED)
cv2.imshow("pic", pic)
res = cv2.erode(pic, kernel=kernel, iterations=5)
# 参数: 待处理图像,核大小(越大腐蚀越多),迭代次数(越大腐蚀越多)
cv2.imshow("res", res)
release_windows()
(2)膨胀
能够放大细节,实现融合,消除边界
原理:同样是卷积核遍历每一个像素点,只要核的中有点位于前景图像中,就将该核的其它位置处前景图像
def dilate():
pic = cv2.imread("acid.bmp")
cv2.imshow("pic", pic)
kernel = np.ones((9, 9), dtype=np.uint8)
res = cv2.dilate(pic, kernel=kernel, iterations=5)
# 参数:待处理图像,核(也叫结构元),迭代次数
cv2.imshow("dilate", res)
release_windows()
(3)通用形态学函数
opencv提供了通用的形态学处理函数,能够进行所有的形态学变换
# # 形态学梯度运算,膨胀-腐蚀,获取前景边缘信息
# res3 = cv2.morphologyEx(dic, cv2.MORPH_GRADIENT, kernel, iterations=3)
# cv2.imshow("res3", res3)
# release_windows()
# # tophat 礼帽运算, 原图-开运算,获取图像噪声、或更亮的边缘信息
# res4 = cv2.morphologyEx(dic, cv2.MORPH_TOPHAT, kernel, iterations=3)
# cv2.imshow("res4", res4)
# release_windows()
# blackhat 黑帽运算,闭运算-原图,获取图像内部小孔,更暗的边缘信息
res5 = cv2.morphologyEx(dic, cv2.MORPH_BLACKHAT, kernel,iterations=2)
cv2.imshow("res5", res5)
release_windows()
(4)核生成函数
对于不同的任务需要使用不同的核,opencv提供了和生成函数
def getSE():
# 核生成函数
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
# 参数:核的形状(RECT矩形,CROSS十字,ELLIPSE椭圆), 大小
print(kernel)
5.图像梯度
(1)Sobel算子
用于获取图像的边界
算子1,计算水平方向的梯度
-1 0 1
-2 0 2
-1 0 1
算子2,计算垂直方向的梯度
-1 -2 -1
0 0 0
1 2 1
同过对原图像进行卷积操作可获取图像的边缘信息
def soble():
apple = cv2.imread("apple.jpg", 0)
res0 = cv2.Sobel(apple, cv2.CV_64F, 0, 1)
res1 = cv2.Sobel(apple, cv2.CV_64F, 1, 0)
res2 = cv2.Sobel(apple, cv2.CV_64F, 1, 1)
res3 = cv2.addWeighted(res0, 0.5, res1, 0.5, 0)
# 卷积操作时可能会出现负数,使用cv2.CV_64F可以避免8位图象个的负数位被截断
# 参数: 待处理图像 输出图像深度
# x方向的求导阶数(进行几次水平方向的卷积操作)
# y方向的求导阶数(进行几次垂直方向上的卷积操作)
# abs_res = cv2.convertScaleAbs(res)
# 对卷积结果取绝对值,转换为8位的图像
cv2.imshow("res0", res0)
cv2.imshow("res1", res1)
cv2.imshow("res2", res2)
cv2.imshow("res3", res3)
release_windows()
(2)Scharr算子
x方向
-3 0 3
-10 0 10
-3 0 3
y方向
-3 -10 -3
0 0 0
3 10 3
和sobel算子道理一样,不过更加精确
def Scharr():
apple = cv2.imread("apple.jpg", 0)
res0 = cv2.Scharr(apple, cv2.CV_64F, 0, 1)
res1 = cv2.Scharr(apple, cv2.CV_64F, 1, 0)
# 错误用法: res2 = cv2.Scharr(apple, cv2.CV_64F, 1, 1)
res3 = cv2.addWeighted(res0, 0.5, res1, 0.5, 0)
# 卷积操作时可能会出现负数,使用cv2.CV_64F可以避免8位图象个的负数位被截断
# 参数: 待处理图像 输出图像深度
# x方向的求导阶数(进行几次水平方向的卷积操作)
# y方向的求导阶数(进行几次垂直方向上的卷积操作)
cv2.imshow("res0", res0)
cv2.imshow("res1", res1)
cv2.imshow("res3", res3)
release_windows()
四、小小总结
学到这里,我自认为opencv处理图像的核心思想我已经掌握了,就是通过对图像进行卷积操作,通过不同的卷积核来达到不同的处理效果,卷积是opencv图像处理中的核心概念。
“使用什么样的卷积核开出什么样的花”