图像
- 计算机中图像就是由像素点组成。
- RGB表示颜色通道,R红色G绿色B蓝色,[0-255]表示该像素点的亮度。
- 黑白图像只有一个通道表示亮度。
- 图像是500*500,则RGB三个通道均是500*500的矩阵,一个图像则为[500,500,3]。
数据读取-图像
cv2.IMREAD_COLOR:彩色图像
cv2.IMREAD_GRAYSCALE:灰色图像
import cv2 #OpenCv,有很多计算机视觉需要的的算法;opencv读取的格式是BGR
import matplotlib.pyplot as plt #Pyplot包含一系列绘图函数的相关函数,用as给它设置一个别名plt
import numpy as np #支持大量的维度数组与矩阵运算 用as给其设置别名np
#%matplotlib inline #用它可以省略plt.show(),vscode用不了
def cv_show(name,img): #定义一个函数,更方便的显示图像
cv2.imshow(name,img) #图像的显示,也可以创建多个窗口
cv2.waitKey(0) #等待时间,毫秒级,0表示任意键终止
cv2.destroyAllWindows()
img=cv2.imread('cat.jpg') #显示一个BGR图像
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows
img=cv2.imread('cat.jpg',cv2.IMREAD_GRAYSCALE) #显示一个灰度图像
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows
cv2.imwrite('mycat.png',img) #保存图片
print(img.shape) #图像基本数值
print(img) #图片每一个像素点(矩阵形式)
print(type(img)) #查看图片底层格式
print(img.size) #计算图片像素点个数
print(img.dtype) #图像数据类型
截取部分图像数据
img=cv2.imread('cat.jpg')
cat=img[0:200,0:200] #截取一段图像
cv_show('cat',cat) #显示截取图像的彩色图
颜色通道提取
B,G,R=cv2.split(img)
print(B)
print(B.shape)
img=cv2.merge((B,G,R))
print(img.shape)
cur_img=img.copy() #只保留R通道
cur_img[:,:,0]=0
cur_img[:,:,1]=0
cv_show('R',cur_img)
cur_img=img.copy() #只保留G通道
cur_img[:,:,0]=0
cur_img[:,:,2]=0
cv_show('G',cur_img)
cur_img=img.copy() #只保留B通道
cur_img[:,:,1]=0
cur_img[:,:,2]=0
cv_show('B',cur_img)
数据读取-视频
cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1。
如果是视频文件,直接指定好路径即可。
import cv2
import matplotlib.pyplot as plt
import numpy as np
#播放一个灰度视频
vc = cv2.VideoCapture('test.mp4')
if vc.isOpened(): #检查是否打开正确
open,frame=vc.read()
else:
open = False
while open:
ret,frame=vc.read()
if frame is None:
break
if ret == True:
gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #此行代码为显示视频是灰度还是彩色的关键
cv2.imshow('result',gray)
if cv2.waitKey(10) & 0xFF == 27:
break
vc.ralease()
cv2.destroyAllWindows()
#显示一个彩色视频
vc = cv2.VideoCapture('test.mp4')
if vc.isOpened():
open,frame=vc.read()
else:
open = False
while open:
ret,frame=vc.read()
if frame is None:
break
if ret == True:
cv2.imshow('result',frame)
if cv2.waitKey(10) & 0xFF == 27:
break
vc.ralease()
cv2.destroyAllWindows()
边界填充
BORDER_REPLICATE:复制法,也就是复制最边缘像素。
BORDER_REFLECT:反射法,对图像中的像素在两边进行复制 fedcba | abcdefgh | hgfedcb
BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称, gfedcb | abcdefgh | gfedcba
BORDER_WRAP:外包装法 cdefgh | abcdefgh | abcdefg
BORDER_CONSTANT:常量法,常数值填充。
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('cat.jpg')
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,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('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()
运行结果:
数值计算
import cv2
import numpy as np
import matplotlib.pyplot as plt
img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')
img_cat2=img_cat+10 #直接相加,两数和超过255时 结果为 两数和 % 256
print(img_cat[:5,:,0])
print(img_cat2[:5,:,0])
print((img_cat+img_cat2)[:5,:,0])
print(cv2.add(img_cat,img_cat2)[:5,:,0]) #用cv2.add相加时,两数和超过255时 结果为255
# print(img_cat+img_dog) #shape不同时,不能相加
print(img_cat.shape)
img_dog=cv2.resize(img_dog,(500,414)) #将'dog.jpg'的shape改成和'cat.jpg'一致
print(img_dog.shape)
res=cv2.addWeighted(img_cat,0.4,img_dog,0.6,0) #将'cat.jpg'和'dog.jpg'融合。'cat.jpg'占比0.4;'dog.jpg'占比0.6;提亮值为0。
plt.imshow(res)
plt.show()
运行结果:
res=cv2.resize(img_cat,(0,0),fx=3,fy=1) #将图片宽伸长三倍,高不变
plt.imshow(res)
plt.show()
运行结果:
res=cv2.resize(img_cat,(0,0),fx=1,fy=3) #将图片宽不变,高伸长三倍
plt.imshow(res)
plt.show()
运行结果 :
形态学
腐蚀操作
腐蚀操作所用函数为cv2.erode(),确定盒子大小的函数是np.ones()。
import cv2
import matplotlib.pyplot as plt
import numpy as np
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#腐蚀操作
img = cv2.imread('dige.png')
cv_show('img',img)
kernel=np.ones((5,5),np.uint8)
erosion=cv2.erode(img,kernel,iterations=1)#迭代 1 次
cv_show('erosion',erosion)
原图:
腐蚀操作,迭代1次后:
对圆进行腐蚀操作:
pie=cv2.imread('pie.png')
cv_show('pie',pie)
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)
原图:
分别迭代 1 2 3
膨胀操作
腐蚀操作所用函数为cv2.dilate()
img = cv2.imread('dige.png')
cv_show('img',img)
kernel=np.ones((5,5),np.uint8)
dige_erosion=cv2.erode(img,kernel,iterations=1)
dige_dilate=cv2.dilate(dige_erosion,kernel,iterations=1)
cv_show('dilate',dige_dilate)
原图:
膨胀操作,迭代一次后:
对圆进行膨胀操作:
pie=cv2.imread('pie.png')
cv_show('pie',pie)
kernel=np.ones((30,30),np.uint8)
dilate_1=cv2.dilate(pie,kernel,iterations=1)
dilate_2=cv2.dilate(pie,kernel,iterations=2)
dilate_3=cv2.dilate(pie,kernel,iterations=3)
res=np.hstack((dilate_1,dilate_2,dilate_3))
cv_show('res',res)
原图:
分别 迭代 1 2 3
开运算与闭运算
开运算:先腐蚀后膨胀
闭运算:先膨胀后腐蚀
import cv2
import numpy as np
import matplotlib.pyplot as plt
#开运算:先腐蚀后膨胀
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img=cv2.imread('dige.png')
cv_show('yuantu',img) #原图
kernel=np.ones((5,5),np.uint8)
opening=cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
cv_show('opening',opening) #先腐蚀后膨胀结果图
#闭运算:先膨胀后腐蚀
img=cv2.imread('dige.png')
cv_show('yuantu',img) #原图
kernel=np.ones((5,5),np.uint8)
closing=cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
cv_show('closing',closing) #先膨胀后腐蚀结果图
原图:
开运算结果图:
闭运算结果图:
梯度运算
梯度=膨胀-腐蚀
#梯度运算
#梯度=膨胀—腐蚀
pie=cv2.imread('pie.png')
kernel=np.ones((7,7),np.uint8)
dilate=cv2.dilate(pie,kernel,iterations=5) #膨胀5次
ersion=cv2.erode(pie,kernel,iterations=5) #腐蚀5次
res=np.hstack((dilate,ersion))
cv_show('res',res)
gradient=cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)
cv_show('gradient',gradient)
将原图分别膨胀5次和腐蚀5次后的结果图:
梯度运算结果图:
礼帽与黑帽
礼帽:原始输入-开运算结果
img=cv2.imread('dige.png')
tophat=cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
cv_show('tophat',tophat)
结果图:
黑帽:闭运算结果-原始输入
img=cv2.imread('dige.png')
blackhat=cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
cv_show('blackhat',blackhat)
结果图:
三种梯度计算的算子
Sobel算子
dst=cv2.Sobel(src,ddepth,dx,dy,ksize)
- ddepth:图像的深度
- dx和dy分别表示水平和竖直方向
- ksize是Sobel算子的大小
img=cv2.imread('D:\pythontupian\pie.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
cv_show(sobelx,'sobelx')
原图 只计算了右-左且未取绝对值
白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要取绝对值。
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx=cv2.convertScaleAbs(sobelx) #取绝对值
cv_show(sobelx,'sobelx')
sobely=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely=cv2.convertScaleAbs(sobely)
cv_show(sobely,'sobely')
下-上的结果截图
分别计算x和y,再求和
sobelxy=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
直接计算(不建议)
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy=cv2.convertScaleAbs(sobelxy)
cv_show(sobelxy,'sobelxy')
"lena" 图片示例:
img=cv2.imread('D:\pythontupian\lena.jpg',cv2.IMREAD_GRAYSCALE)
cv_show(img,'img')
sobelx=cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely=cv2.convertScaleAbs(sobely)
sobelxy=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
原图 先计算右-左,再计算下-上,最后结果相加。
img=cv2.imread('D:\pythontupian\lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy=cv2.convertScaleAbs(sobelxy)
cv_show(sobelxy,'sobelxy')
直接计算梯度
scharr算子
img=cv2.imread('D:\pythontupian\lena.jpg',cv2.IMREAD_GRAYSCALE)
scharrx=cv2.Scharr(img,cv2.CV_64F,1,0)
scharrx=cv2.convertScaleAbs(scharrx)
scharry=cv2.Scharr(img,cv2.CV_64F,0,1)
scharry=cv2.convertScaleAbs(scharry)
scharrxy=cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
laplacian算子
img=cv2.imread('D:\pythontupian\lena.jpg',cv2.IMREAD_GRAYSCALE)
laplacian=cv2.Laplacian(img,cv2.CV_64F)
laplacian=cv2.convertScaleAbs(laplacian)
三种算子计算结果比较
res=np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,'res')
Sobel scharr laplacian
图像平滑处理
均值滤波
简单的平均卷积操作
import cv2
import matplotlib.pyplot as plot
import numpy as np
img=cv2.imread('D:\pythontupian\lenaNoise.png')
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#原图
cv_show('img',img)
#均值滤波
#简单的平均卷积操作
blur=cv2.blur(img,(3,3))
cv_show('blur',blur)
原图:
结果图:
方框滤波
基本和均值一样,可以选择归一化
#基本和均值一样,可以选择归一化
box=cv2.boxFilter(img,-1,(3,3),normalize=True)
cv_show('box',box)
结果图:
不选择归一化的话很容易越界
#未选择归一化,越界后的结果
box=cv2.boxFilter(img,-1,(3,3),normalize=False)
cv_show('box',box)
越界结果图:
高斯滤波
高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
#高斯滤波
#高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
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),)
cv_show('res',res)