OpenCV轻松入门(学习笔记)
参考:
文章目录
声明
本博客内容中根据个人项目需求对上述参考材料进行改写 ,
非抄袭,也非原创 (但大部分原创)
若有侵权行为务必告知
E-mail: yuansh3354@163.com
本文内容仅为笔记,方便自己后续做DL时候进行参考,不会用于其他任何用途
安装opencv-python:
pip install opencv-python
pip install opencv-contrib-python
图像的读取和显示和保存
import cv2 # opencv读取的格式是BGR
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
plt.rcParams['savefig.dpi'] = 500 #图片像素
plt.rcParams['figure.dpi'] = 500 #分辨率
img = cv2.imread('cat.jpg') # 读取图片,默认导入是BGR格式
#博文中支持使用这种方法显示图片,但是我个人不喜欢,因此注释掉
# def cv_show(img_name='image', img=None, show_time=0):
# cv2.namedWindow(img_name, cv2.WINDOW_NORMAL) #输出的图片可以任意缩放
# cv2.imshow(img_name, img) # 显示图片
# cv2.waitKey(show_time*1000)
# # 设置图片显示时长,当为0时显示时间无穷大(按任意键退出)。
# # 显示时长单位:秒
# cv2.destroyAllWindows() # 关闭所有窗口
# cv_show('1', img, 10)
# cv2.imwrite('cat_gray.jpg', img) # 保存图片
def to_rgb(img):
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) # image 格式转化
return(img)
def imshow(img=None, img_name=None, save=False):
if save:
cv2.imwrite(img_name,img)
print("image size: ", img.shape)
img = to_rgb(img) # opencv读取的格式是BGR,plt是以rgb格式显示图片
plt.xticks([])
plt.yticks([])
plt.imshow(img)
通道的剥离和组合
通道与合并拆分:
- cv2.split(img)
- cv2.merge((r,g,b))
注意:这里可以使用切片法对图像进行修改或者截取
import cv2
img = cv2.imread('lena.jpg')
b,g,r = cv2.split(img)# cv2.split(img)[0] 等同于 img[:,:,0]取第一个通道
b[100:200,50:200] = 0
g[100:200,50:200] = 0
img_new = cv2.merge((b,g,r))# 处理完某个通道,再重新组合b g r3个通道
# 图像截取
ids = np.hstack((img,img_new))
imshow(ids)
图像融合
融合一般分为两种:
- 通道融合
- 简单融合(矩阵加法)
下面展示的是简单融合
从左到右:原图、直接加法、使用add方法
import cv2
img = cv2.imread('lena.jpg')
img_add = img + img
img_add_ = cv2.add(img,img)
ids = np.hstack((img,img_add,img_add_))
imshow(ids)
加权图像融合
- cv2.addWeighted(src1, alpha, src2, beta, gamma)
这四个参数都是必须参数
γ \gamma γ:可以认为是亮度调节系数
公式具体执行如下:
i
m
g
o
u
t
=
i
m
g
1
∗
α
+
i
m
g
2
∗
β
+
γ
img_{out} = img_1 * \alpha + img_2 * \beta + \gamma
imgout=img1∗α+img2∗β+γ
# 直接加(务必注意image_size必须一样)
img_cat = cv2.imread('cat.jpg') # 414x500x3
img_dog = cv2.imread('dog.jpeg')
img_dog = cv2.resize(img_dog,(img_cat.shape[1],img_cat.shape[0]))
ids1 = cv2.addWeighted(img_cat,0.5,img_dog,0.5,1)
ids2 = cv2.addWeighted(img_cat,0.5,img_dog,0.5,10)
ids3 = cv2.addWeighted(img_cat,0.5,img_dog,0.5,100)
ids = np.hstack((ids1,ids2,ids3))
imshow(ids)
填充
- BORDER_REPLICATE:复制法,也就是复制最边缘像素。
- BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
- BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
- BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
- BORDER_CONSTANT:常量法,常数值填充。
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('lena.jpg')
top_size,bottom_size,left_size,right_size = (50,50,50,50)
img_replace = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REPLICATE)
img_reflect = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT)
img_reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
img_wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
img_constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_CONSTANT, value=0)
img = to_rgb(img)
img_replace = to_rgb(img_replace)
img_reflect = to_rgb(img_reflect)
img_reflect101 = to_rgb(img_reflect101)
img_wrap = to_rgb(img_wrap)
img_constant = to_rgb(img_constant)
plt.figure(figsize=(10,10))
plt.subplot(231),plt.imshow(img,'gray'),plt.title('ORIGINAL'),plt.xticks([]),plt.yticks([])
plt.subplot(232),plt.imshow(img_replace,'gray'),plt.title('Replace'),plt.xticks([]),plt.yticks([])
plt.subplot(233), plt.imshow(img_reflect, 'gray'), plt.title('REFLECT'),plt.xticks([]),plt.yticks([])
plt.subplot(234), plt.imshow(img_reflect101, 'gray'), plt.title('REFLECT_101'),plt.xticks([]),plt.yticks([])
plt.subplot(235), plt.imshow(img_wrap, 'gray'), plt.title('WRAP'),plt.xticks([]),plt.yticks([])
plt.subplot(236), plt.imshow(img_constant, 'gray'), plt.title('CONSTANT'),plt.xticks([]),plt.yticks([])
plt.subplots_adjust( wspace=0.1, hspace=-.15)
#wspace 子图横向间距, hspace 代表子图间的纵向距离,left 代表位于图像不同位置
plt.show();
按位运算
- cv2.bitwise_and()
个人认为这个比较实用
通常来说,在语义分割模型中,我们通过使用 mask
和 raw_imag
的按位运算可以直接截取蒙版内的 ROI 区域
import cv2
import numpy as np
img1 = cv2.imread('TCGA_CS_4941_19960909_12.tif')
img2 = cv2.imread('TCGA_CS_4941_19960909_12_mask.tif')
img = cv2.bitwise_and(img1, img2)
ids = np.hstack((img1,img2,img))
imshow(ids)
查看所有逻辑运算的差异
通过以下的结果可以看到;
- 与运算可以得到蒙版部分
- 或运算可以得到除了蒙版以外的部分
import cv2
import numpy as np
img1 = cv2.imread('TCGA_CS_4941_19960909_12.tif')
img2 = cv2.imread('TCGA_CS_4941_19960909_12_mask.tif')
ids1 = cv2.bitwise_and(img1, img2) # 与
ids2 = cv2.bitwise_not(img1)# 取反
ids3 = cv2.bitwise_or(img1, img2)# 或
ids4 = cv2.bitwise_xor(img1, img2)# 异或
ids = np.hstack((img1,ids1,ids2,ids3,ids4))
imshow(ids)
Image 平面位分解
8位灰度图中,每个像素的值可以如下按位分解
i m g o u t = a 7 ∗ 2 7 + a 6 ∗ 2 6 + a 5 ∗ 2 5 + a 4 ∗ 2 4 + a 3 ∗ 2 3 + a 2 ∗ 2 2 + a 1 ∗ 2 1 + a 0 ∗ 2 0 img_{out} = a_7∗2^7+a_6∗2^6+a_5∗2^5+a_4∗2^4+a_3∗2^3+a_2∗2^2+a_1∗2^1+a_0∗2^0 imgout=a7∗27+a6∗26+a5∗25+a4∗24+a3∗23+a2∗22+a1∗21+a0∗20
按每位分解可以得到位图(位平面),a7是最高有效位平面,a0是最低有效位平面
平面位越高,与原图的相似性越强
位面分解原理(课本中用了好几个篇幅进行介绍(P53-58),我这里做个总结):
- 将像素点转化为8位的2进制(上面的公式)
- 根据8位的2进制,从右到左分别位0-7位
- 将对应的位的值拿出来就是对应的位面图了
# img 的位面图展示
# 根据公式计算位面图,默认输出第8位
# rgb的位面分解单独进行即可
def Get_Potential_Plane(img=None,plane_id=7):
r,c = img.shape # 提取image尺寸
x = np.zeros((r,c,1),dtype=np.uint8) # 构建全为0的矩阵
x[:,:,0]=2**plane_id #表示2的i次方,这里是2的7 次方
r=np.zeros((r,c,1),dtype=np.uint8)
r[:,:,0]=cv2.bitwise_and(img, x[:,:,0])
"""
将图片中的像素点转为2进制的值,然后和全为2的7次方按位与。
由于2的7次方,只有在第8位为1其余全为零,按位与后除第8位为1的其余位置全为0,
也就是像素点中,值小于128的全为0,反之全为1
"""
mask=r[:,:,0]>0
r[mask]=255 #将像素值大于0的都设为255,目的在于提高亮度,否在值过低就接近黑色了
return r
import cv2
import numpy as np
img = cv2.imread('lena.jpg',0)
for i in range(8):
locals()['img_'+str(i)] = Get_Potential_Plane(img,i)
ids = np.hstack((img_0,img_1,img_2,img_3,img_4,img_5,img_6,img_7))
plt.figure(figsize=(12,16))
imshow(ids)
RGB版本
# RGB按位运算
img = cv2.imread('lena.jpg')
img_b, img_g, img_r = cv2.split(img)
for i in range(8):
locals()['img_r_'+str(i)] = Get_Potential_Plane(img_r, i)
locals()['img_g_'+str(i)] = Get_Potential_Plane(img_g, i)
locals()['img_b_'+str(i)] = Get_Potential_Plane(img_b, i)
locals()['img_'+str(i)] = cv2.merge((locals()['img_b_'+str(i)],
locals()['img_g_'+str(i)],
locals()['img_r_'+str(i)]))
ids = np.hstack((img_0, img_1, img_2, img_3, img_4, img_5, img_6, img_7))
plt.figure(figsize=(12, 16))
imshow(ids)
阈值处理
返回值是:阈值和图
阈值的处理,主要分为两种:
-
固定阈值处理
-
二值化:大于阈值的设为255,小于阈值的设为0
-
反向二值化
-
截断阈值:超过阈值部分为阈值,其余不变
-
超阈值零处理: 超过阈值的部分设为0
-
低阈值零处理
-
-
自适应阈值处理
-
区域加权平均
-
权值相等-mean
-
高斯加权-gaus
-
-
遍历-otsi
-
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('lena.jpg',0)
# 二值化阈值处理
ret,img_bi = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# 反向二值化阈值处理
ret,img_bi_inv = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
# 截断阈值处理
ret,img_tr = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
# 低阈值零处理
ret,img_zero = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
# 超阈值零处理
ret,img_zero_inv = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
# 自适应阈值处理(权重相等)
athdmean = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,5,3)
# 自适应阈值处理(距离变化)
athdgaus = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,5,3)
# otsu处理(自适应阈值)
# 该方法会遍历所有的可能值,然后输出最佳的结果
t2,otsu = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
titles = ['二值化阈值处理','反向二值化阈值处理','截断阈值处理',
'低阈值零处理','超阈值零处理','自适应阈值处理(权重相等)',
'自适应阈值处理(距离变化)','otsu处理(自适应阈值)']
images = [img_bi,img_bi_inv,img_tr,img_zero,img_zero_inv,athdmean,athdgaus,otsu]
for i in range(8):
plt.subplot(2,4,i+1)
plt.imshow(images[i],'gray')
#plt.title(titles[i])
plt.xticks([]),plt.yticks([]) # 不显示坐标轴
plt.show();
平滑处理
作用:降噪,其主要原理是将噪声像素点改为与周围像素点临近的近似值
常用的方法分为:
- 均值波滤(标准化后的方框波滤,是一个特例)
- 方框波滤
- 高斯波滤(用的是高斯核)
- 中值波滤
- 双边波滤(综合考虑到空间和色彩信息,可以有效的处理边缘信息,值太大会产生卡通效果)
- 2D卷积
import cv2
import numpy as np
img = cv2.imread('lenaNoise.png')
plt.rcParams['figure.figsize'] = (20,20)
# 波滤操作
blur = cv2.blur(img,(3,3)) # 均值
boxFilter = cv2.boxFilter(img,-1,(3,3),normalize=False) # 方框,当选择标准化后,方框和均值的操作结果相同
gussian = cv2.GaussianBlur(img,(3,3),1)#高斯波滤
median = cv2.medianBlur(img,5) # 中值波滤
binary = cv2.bilateralFilter(img,55,150,150)#双边波滤
ids = np.hstack((img,blur,boxFilter,gussian,median,binary))
imshow(ids)
梯度计算
sobel 算子
scharry 算子
laplacian 算子
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) # 计算x梯度
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3) # 计算y梯度
sobelx = cv2.convertScaleAbs(sobelx)# 取绝对值
sobely = cv2.convertScaleAbs(sobely)# 去绝对值
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((img,sobelxy,scharrxy,laplacian))
imshow(res)
Canny 边缘检测
STEP:
去燥
计算梯度方向
非极大值抑制
双阈值确定边缘
抑制孤立的弱边缘
img = cv2.imread("cat.jpg", cv2.IMREAD_GRAYSCALE)
v1 = cv2.Canny(img, 80, 150)
v2 = cv2.Canny(img, 50, 100) # 阈值设置的合适,就可以把细节信息展示更多,发丝和细纹理都显现出来了
res = np.hstack((img, v1, v2))
imshow(res)