文章目录
前言
课程学习笔记,BV1PV411774y
ROI–截取
截取部分图像数据
import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
img = cv2.imread('01_Picture/01_cat.jpg')
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cat = img[100:300,0:200] # 选择感兴趣的图片区域
cv_show('cat',cat)
颜色通道提取
b,g,r = cv2.split(img) # 分离 BGR 通道
# b/g/r 单独展示出来均相当于灰度图
img = cv2.merge((b,g,r)) # 组合 BGR 通道
# 只保留 G 通道
cur_img = img.copy()
cur_img[:,:,0] = 0
cur_img[:,:,2] = 0
cv_show('G',cur_img)
边界填充
① 边界填充:对图像进行一些变换,让原始图像扩大
② 边界填充的入口参数:
- BORDER_REPLICATE:复制法,直接复制最边缘像素
- BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制,fedcba|abcdefgh|hgfedcb
- BORDER_REFLECT_101:反射法,以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
- BORDER_WRAP:外包装法,cdefgh|abcdefgh|abcdefg
- BORDER_CONSTANT:常量法,常数值填充
top_size,bottom_size,left_size,right_size = (50,50,50,50) # 上、下、左、右填充的大小
# 方式一:复制法
replicate = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_REPLICATE)
# 方式二:反射法
reflect = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT)
# 方式三:反射法二(不要最边缘的像素)
reflect101 = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_REFLECT_101)
# 方式四:外包装法
wrap = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,borderType=cv2.BORDER_WRAP)
# 方式五:常量法
constant = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_CONSTANT,value=0)
plt.subplot(231), plt.imshow(img,'gray'), plt.title('ORIGINAL')
plt.subplot(232), plt.imshow(replicate,'gray'), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect,'gray'), plt.title('REPLECT')
plt.subplot(234), plt.imshow(wrap,'gray'),plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236), plt.imshow(constant,'gray'),plt.title('CONSTAVI')
plt.show()
简单数值计算
img_cat = cv2.imread('01_Picture/01_cat.jpg')
img_dog = cv2.imread('01_Picture/03_dog.jpg')
img_cat2 = img_cat + 10 # 矩阵中每一个值都加 10
(img_cat + img_cat2)[:5,:,0] # 0-255,若相加越界则取 %256
# [:5,:,0],查看 B 通道的前 5 行
cv2.add(img_cat,img_cat2) # 若相加越界则取 255
图像融合
图像缩放
# 维度不匹配(shape 不同),不能直接相加,需要 resize
img_dog = cv2.resize(img_dog,(500,414))
res = cv2.resize(img,(0,0),fx=3,fy=1) # (0,0):不指定目标大小,而是倍数放缩
# fx=3:行像素 x 乘 3;fy=1:y 乘 1
加权融合
res = cv2.addWeighted(img_cat,0.4,img_dog,0.6,0) # 0.4(img_cat) + 0.6(img_dog) + 0
plt.imshow(res)
形态学操作
腐蚀
- 通常拿二值图像进行操作
- 减少或删除图像中的前景像素(比如物体的边缘或者特定特征),从而达到缩小对象边界或者消除图像中的噪声等效果
img = cv2.imread('01_Picture/05_Dige.png')
kernel = np.ones((5,5),np.uint8) # 卷积核
# 以像素点为中心,5×5 的区域,若存在前景(此图,值为1),则腐蚀(此图,值变为0)
erosion = cv2.erode(img,kernel,iterations=1) # iterations:迭代次数,即反复执行腐蚀
cv_show('erosion',erosion)
pie = cv2.imread('01_Picture/06_pie.png')
kernel = np.ones((30,30),np.uint8)
erosion_1 = cv2.erode(pie,kernel,iterations=1) # 对比不同迭代次数的效果
erosion_2 = cv2.erode(pie,kernel,iterations=2)
erosion_3 = cv2.erode(pie,kernel,iterations=3)
res = np.hstack((erosion_1,erosion_2,erosion_3))
cv_show('res',res)
膨胀
- 相当于腐蚀的逆操作
- 腐蚀去噪会使目标的边界缩小,通过膨胀加以还原,从而去除噪声、保留目标
kernel = np.ones((3,3),np.uint8)
dige_erosion = cv2.erode(img,kernel,iterations=1)
kernel = np.ones((3,3),np.uint8) # 以像素点为中心,3×3 的区域,若存在背景(此图,值为0),则膨胀(此图,值变为1)
dige_dilate = cv2.dilate(dige_erosion,kernel,iterations=1)
cv_show('dilate',dige_dilate)
开运算与闭运算
- 开:先腐蚀,再膨胀;cv2.MORPH_OPEN
- 闭:先膨胀,再腐蚀;cv2.MORPH_CLOSE
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel) # morphology:形态学
cv_show('opening',opening)
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
cv_show('closing',closing)
梯度运算
- 梯度 = 腐蚀-膨胀;cv2.MORPH_GRADIENT
- 获取物体的边界信息
pie = cv2.imread('01_Picture/06_pie.png')
kernel = np.ones((7,7),np.uint8)
gradient = cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)
cv_show('gradient',gradient)
礼帽与黑帽
- 礼帽 = 原始输入-开运算结果;cv2.MORPH_TOPHAT
- 黑帽 = 闭运算结果-原始输入;cv2.MORPH_BLACKHAT
kernel = np.ones((5,5),np.uint8)
tophat = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
cv_show('tophat',tophat)
kernel = np.ones((5,5),np.uint8)
blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
cv_show('blackhat',blackhat)
图像梯度
图像中物体边缘的梯度大,纯色物体内部梯度为0
Sobel算子
cv2.Sobel(src, ddepth, dx, dy, ksize),返回值为Sobel算子处理后的图像
- ddepth:图像的深度
- dx/dy:置为 1 表示选择水平/竖直方向
- ksize:Sobel 算子的大小
G x (水平卷积) = [ − 1 0 1 − 2 0 2 − 1 0 1 ] ∗ A ;右减左 G_x\text{(水平卷积)} = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix} *A \text{ ;右减左} Gx(水平卷积)= −1−2−1000121 ∗A ;右减左
G y (竖直卷积) = [ − 1 − 2 − 1 0 0 0 1 2 1 ] ∗ A ;下减上 G_y\text{(竖直卷积)} = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{bmatrix} *A \text{ ;下减上} Gy(竖直卷积)= −101−202−101 ∗A ;下减上
img = cv2.imread('01_Picture/06_pie.png')
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) # cv2.CV_64F:用于扩展计算位数
# cv2 会将负数截断成 0,所以要取绝对值
sobelx = cv2.convertScaleAbs(sobelx)
cv_show('sobelx',sobelx)
sobely = cv2.Sobel(pie,cv2.CV_64F,0,1,ksize=3) # 竖直方向
sobely = cv2.convertScaleAbs(sobely)
# 分别计算后,再加权求和,不建议直接将 dx 和 dy 同时置为 1
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) # 0.5(sobelx) + 0.5(sobely) + 0
cv_show('sobelxy',sobelxy)
Scharr算子
G x = [ − 3 0 3 − 10 0 10 − 3 0 3 ] ∗ A and G y = [ − 3 − 10 − 3 0 0 0 3 10 3 ] ∗ A G_x = \begin{bmatrix} -3 & 0 & 3 \\ -10 & 0 & 10 \\ -3 & 0 & 3 \end{bmatrix} *A \text{ and } G_y = \begin{bmatrix} -3 & -10 & -3 \\ 0 & 0 & 0 \\ 3 & 10 & 3 \end{bmatrix} *A Gx= −3−10−30003103 ∗A and Gy= −303−10010−303 ∗A
Laplacian算子
- 使用二阶导(一阶导的变化率),对噪音点更敏感,通常与其他方法结合使用
- 如果中心点是边界,它与周围像素点差异的幅度会较大,Laplacian算子根据此特点可以把边界识别出来
G = [ 0 1 0 1 − 4 1 0 1 0 ] G = \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix} G= 0101−41010
img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
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) # 没有 x、y,因为是求中心点与周围点的比较
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show('res',res)
阈值操作
ret, dst = cv2.threshold(src, thresh, maxval, type),返回阈值和输出图
- src: 输入图,只能输入单通道图像,通常来说为灰度图
- thresh: 阈值
- maxval: 当像素值超过了阈值 (或者小于阈值,根据 type 来决定),所赋予的值
- type:二值化操作的类型,包含以下5种类型:
- cv2.THRESH_BINARY 超过阈值部分取maxval,否则取0
- cv2.THRESH_BINARY_INV THRESH_BINARY的反转,超过阈值部分取0,否则取maxval
- cv2.THRESH_TRUNC 大于阈值部分取阈值,否则不变
- cv2.THRESH_TOZERO 大于阈值部分不改变,否则取0
- cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_GRAYSCALE)
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV) # THRESH_BINARY_INV 相对 THRESH_BINARY 黑的变成白的,白的变成黑的
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img_gray, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2,3,i+1), plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
图像平滑
使用滤波,去除噪声
均值滤波
# 平均卷积:卷积核区域的值累加,取平均,作为中心点的值
img = cv2.imread('01_Picture/04_LenaNoise.png') # 带噪音的图像
blur = cv2.blur(img,(3,3)) # (3,3) 为核的大小,通常核都是奇数 3、5、7
cv_show('blur',blur)
方框滤波
# 在 Python 中,-1 表示自适应填充对应的值
# 此处 -1 表示颜色通道数自适应一致
box = cv2.boxFilter(img,-1,(3,3),normalize=False)
cv_show('box',box)
# 做归一化,得到的结果与均值滤波一样
# 不做归一化,则是只累加,不取平均,越界的值取 255
高斯滤波
# 卷积核满足高斯分布:离中心越近的,值越大,离中心越远的,值越小
aussian = cv2.GaussianBlur(img,(5,5),1)
cv_show('aussian',aussian)
中值滤波
# 排序后,将中值作为中心点的值
median = cv2.medianBlur(img,5)
cv_show('median',median)
res = np.hstack((blur,aussian,median)) # 矩阵横着拼接
#res = np.vstack((blur,aussian,median)) # 矩阵竖着拼接
cv_show('res',res)