OpenCV笔记
第一节 计算机眼中的图像
1.读取图像 :cv2.imread('路径',转换形式)
2.图像显示:cv2.imshow('name',img)
cv2.waitKey(0) #0就是任意按一个按键结束显示,不是0别的数字是ms级别
3.图片.shape #显示图像尺寸(H,W,C)
4.将图片转为灰度图像:img = cv2.imread('路径',cv2.IMREAD_GRAYSCALE) #灰度图像的通道(C)为1,0-255表示的亮度,dtype=uint8就足够了,不管RGB还是灰度图
5.图像保存:cv2.inwrite('名字或路径', 保存的图片)
6.type(img)为类型,是ndarray类型;img.size图片元素的个数或者说图片的大小;img.dtype为数据类型,一班是uint8
第二节 视频的读取
1.cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1
2.vc = cv2.VideoCapture('视频路径')
#检查是否打开正确
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(1) & 0xFF == 27:#27 是Esc的ASCII码,waitKey为每一个帧数停留的时间
break
vc.release()
cv2.destroyAllwindows()
第三节 ROI(range of interest)
1.img = cv2.imread('文件路径')
cat = img[0:200,0:200]#切片,获取感兴趣的像素点
cv_show(’cat',cat)
2.颜色通道的提取
第一种:b,g,r = cv2.split(img)
第二种:b=img[:,:,0]#冒号冒号代表读取整张图,0,1,2分别代表BGR通道
3.合并:img = cv2.merge((b,g,r))
4.读不同的颜色通道,可以将其余两个通道设置成为0
eg:img[:,:,0]=0就是将B通道设置为0
第四节 边界填充
API cv2.copyMakeBorder(img,上填充,下填充,左,右,boderType=?)
boderType的种类:
1.BORDER_REPLICATE:复制法,复制最边缘的像素
2.BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制,边界对称映射
3.BORDER_REFLECT_101:反射法,以最边缘的像素为轴,对称
4.BORDER_WRAP:外包装法,将最外层的像素循环下去
5.BORDER_CONSTANT:常量法,常数值填充,给定常数值,比如0就是黑,要在后面指定value值,前面不用
第五节 数值计算
1.加值的话为每一个像素点都加值
img_cat2 = img_cat+10 (shape不变)
2.超过255会进行对2的八次方取余的操作,uint8类型只会存255个数(0-255)
3.OpenCV自带API,cv2.add(img1,img2),越界取255,没越界取自身
4.图像融合:图像融合要shape一样。
cv2.resize('图片',(重置的大小))
cv2.resize('图片',(0,0),fx=?,fy=?) #如果把值设置为(0,0),后面fx,fy代表放缩倍数
cv2.addWeighted(图片1,图片1的权重,图片2,图片2的权重,偏置项(或者说亮度值)) #图片1的元素值*图片1的权重+图片2的元素值*图片2的权重+b,b代表的亮度
第六节 图像阈值
ret,dst=cv2.threshold(src,thresh,maxval,type)
· src: 输入图,只能输入单通道图像, 通常是灰度图
·dst :输出图
·thresh:阈值,是一个具体的数
·maxval:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
·type: 二值化操作的类型,包含以下5种类型:
(1)cv2.THRESH_BINARY: 超过阈值部分取maxval, 否则取0
(2)cv2.THRESH_BINARY_INV : THRESH_BINARY的反转,超过取0,否则取maxval
(3) cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变。(假如说阈值为127,那大于127的像素点都变为127,小于的就不变。对图像进行截断)
(4)cv2.THRESH_TOZERO: 大于阈值的不改变,否则设为0
(5) cv2.THRESH_TOZERO_INV : THRESH_TOZERO的反转
!INV是inverse,是反转的意思,很常见
第七节 图像平滑处理(滤波操作)
图像平滑处理可以说是为了去除焦点噪音
1.均值滤波,其实就是平均池化。CV怎么实现?
blur = cv2.blur(img,(3,3))#(3,3)卷积核的大小,卷积核的值都是1,然后对像素点求内积再除以9
2.方框滤波,基本和均值一样,可以选择归一化
box = cv2.boxFilter(img,-1,(3,3),normalize=True) #normalize为True就和均值滤波一样
#如果为False,就是不除以9了,大于255的直接为255,高亮
#-1一班不变
3.高斯滤波
高斯分布就是正态分布,距离中心点越近,发挥的作用点越强,(上下左右近,对角远)
aussian = cv2.GaussianBlur(img,(5,5),1)#(5,5)为滤波器的大小;1为滤波器的标准差,如果标准差这个参数设置为0,则程序会根据滤波器大小自动计算得到标准差
4.中值滤波
对选中区域进行从小到大排序,找到中间值。滤波之后区域的中间值就是从小到大的找到的中值,处理噪音点比较强大
第八节 形态学
-腐蚀操作和膨胀操作
腐蚀操作:一般二值化的图像才用腐蚀操作,腐蚀操作笼统将叫去毛刺,卷积核在做卷积操作的时候有两块区域(黑与白)就会把白变成黑给腐蚀掉
img不特别之处一般指读取的图像(cv2.imread)
1.cv2.erode(img,kernel,iterations=1) #iteration-迭代,即为腐蚀的次数,此外卷积核的大小也关系着腐蚀多少
膨胀操作:腐蚀操作的逆操作
cv2.dilate(img,kernel,iterations=?)
综上:当我们的图像有一些毛刺等我们不需要的突起,用腐蚀操作;腐蚀操作之后用膨胀操作能消除过腐蚀影响
-开运算与闭运算
开:先腐蚀,再膨胀 cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
闭:先膨胀,后腐蚀 cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
-梯度运算
梯度=膨胀-腐蚀 (取轮廓)
cv2morphologyEx(img,cv2.MORPH_GRANDIENT,kernel)
-礼帽和黑帽
礼帽=原始输入-开运算结果 cv2.morphology(img,cv2.MORPH_TOPHAT,kernel)
黑帽=闭运算-原始输入 cv2.morphology(img,cv2.MORPH_BLACKHAT,kernel)
第九节 图像梯度
求梯度,梯度在数学中是变化率的意思,在图像领域可以理解为求边界,Gx和Gy一个是求水平方向上一个是竖直方向上
-Sobel算子
-1 0 +1 -1 -2 -1
Gx = [-2 0 +2]*A and Gy=[0 0 0 ]*A
-1 0 +1 +1 +2 +1
以上矩阵代表的卷积核就是Sobel算子。是右边-左边,如果求了之后小于0,OpenCv对其进行截断
dst = cv2.Sobel(src,ddepth,dx,dy,ksize)
- ddepth:图像的深度(一般为-1,表示输入和输出深度一样)
- dx 和 dy分别表示水平和竖直方向
- ksize是Sobel算子的大小(卷积核的大小)
cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)#cv2.CV_64F表示64位浮点数,1表示算水平的,0表示不算竖直的。
cv2.convertScaleAbs(Sobel处理得到的图像)#对结果取绝对值,如果是负数不会进行截断
分别计算Gx和Gy,再求和
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0) #sobelx和sobely分别代表Gx和Gy,0.5代表的权重,0是偏置项,易班为0
不建议直接dx=1, dy=1这样直接计算
-Scharr算子
-3 0 3 -3 -10 -3
Gx = [-10 0 10]*A and Gy = [ 0 0 0]*A
-3 0 3 3 10 3
-laplacian算子
G = [0 1 0]
1 -4 1
0 1 0
cv2.Scahrr #更敏感
cv2.Laplacian #一般和其他的工具一起使用,后续会讲到
同Sobel过程一样
Laplacian算子没有x,y那些
第十节 边缘检测
-Canny边缘检测
-使用高斯滤波器,以平滑图像,滤除噪声
-计算图像中没个像素点的梯度强度和方向
-应用非极大值抑制(NMS:Non-Maximum Suppression),以消除边缘检测带来的杂散响应
-应用双阈值检测来确定真实的和潜在的边缘
-通过抑制孤立的弱边缘最终完成边缘检测
1.高斯滤波器
对高斯滤波器进行归一化处理,然后进行卷积操作,滤除噪音
2.梯度和方向
梯度上面有讲,这里用Sobel算子
方向直接arctan(Gy/Gx)
3.非极大值抑制
(1)线性插值法:两个像素点之间插值。公式:插的这个点靠近哪个像素点,这个插的像素点占的权重就大。
(2) 为了简化计算,由于一个像素周围有八个像素,把一个像素的梯度方向离散为八个方向,这样就只需计算前后即可,不用插值
4.双阈值检测
梯度值>maxVal: 则处理为边界
minVal<梯度值
梯度值
cv2.Canny(img,minVal,maxVal)阈值越小,边缘信息得到的越多;阈值越大,边缘信息得到的越少
第十一节 图像金字塔
-高斯金字塔:
-向下采样法(缩小)
-将Gi与高斯内核卷积,高斯内核在滤波那里讲了
-将所有偶数行和列去除
-向上采样(放大)
-将图像再每个方向扩大为原来的两倍,新增的行和列以0填充
-使用先前同样的内核(乘以4)与放大后的图像卷积,获得近似值
-OpenCV怎么执行:
up = cv2.pyrUp(img) #上采样
down = cv2.pyrDown(img) #下采样
-拉普拉斯金字塔:
down = cv2.pyrDown(img)
down_up=cv2.pyrUp(down)
l_1=img-down_up
cv_show(l_1,'l_1')
第十一节 图像轮廓
cv2.findContours(img,mode,method)#最新版OpenCV有两个返回值,这只是找到轮廓
mode:轮廓检索模式
-RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次
method:轮廓的逼近方法
-CHAIN_APPROX_NONE: 以Freeman链码的方式输出轮廓,所有其他方法输出多边形
-CHAIN_APPROX_SIMPLE: 压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分
-绘制轮廓:
cv2.drawContours(draw_img,contours,-1,(0,0,255),2) #参数:传入绘制图像,轮廓,轮廓索引,颜色模式轮廓的颜色(B,G,R),线条宽度
用之前要draw_img = img.copy()
-轮廓特征计算:
1.取特征:cnt = contours[?]
2.求面积:cv2.contourArea(cnt)
3.周长: cv2.arcLength(cnt,True) #True代表闭合的
-轮廓近似:
不需要那些毛毛刺刺的,只要那些轮廓。假设一个曲线,取一条直线连接,找到曲线离直线最远的那个点,看是否小于设定阈值,如果小于,则说明可以用直线来代替。
approx=cv2.approxPolyDP(cnt,epsilon,True) #cnt :轮廓图 epsilon:阈值
-边界矩形
x,y,w,h=cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
可以做边界矩形和轮廓比
-外接圆:
cv2.minEnclosingCircle(cnt)
第十二节 模板匹配
提取特征,然后进行对比,看是否匹配。和卷积神经网络差不多。
-流程:
图像和模板读进来;
模板匹配:cv2.matchTemplate(img,template,1)#1:可以改成判断的方法
模板匹配和卷积很像,模板在原图像上从原点开始滑动,计算模板与图像被模板覆盖的地方的差别程度,这个差别程度的计算方法在OpenCV里有6种。计算后将每次结果放入一个矩阵里,作为结果输出。加入原图形是AXB,模板是aXb,输出结果的矩阵是(A-a+1)*(B-b+1)
min_val,max_val,min_loc,max_loc = cv2.minMaxloc(res) #返回值和位置,关注最小值,和最小值的坐标位置。可以做人脸识别
min_loc读进来为左上角的值,那么我们就知道了模板的位置,因为模板的大小我们是已知的,直到了Location,就知道了位置
top_left = min_loc
top_left = max_loc
画选定矩形:
cv2.rectangle(img2,top_left,bottom_right,255,2)
-匹配多个对象:
设一个阈值:归一化就设个0.8之类的
模型匹配之后传回来的值来自cv2.matchTemplate(),假设为res
loc = np.where(res>=0.8)
for pt in zip(*loc[::-1]):
bottom_right = (pt[0] + w, pt[1]+h)
cv2.rectangle(img_rgb,pt,bottom_right,(0,0,255),2)
cv2.imshow('img_rgb',img_rgb)
第十三节 直方图
直方图和像素点的关系,比如一个图中有好几个像素值为155或者100的,统计一下,化成直方图
在OpenCV中怎么进行统计
-cv2.calcHist(images,channels,mask,histSize,ranges)
-images: 原图像图像格式为uint8或者float32。当传入函数时应用中括号[]括起来例如[img]
-channels: 同样用中括号括住,代表输入通道数,[0]代表灰度图,[0][1][2]代表BGR
-mask:掩模图像,就是创建一个掩模图像并使用它,把整个图框起来进行直方图
-histSize:BIN的数目。也应用中括号括起来.就是直方图的自变量的取值范围,比如一个直方图你可以表示0-10或者说0-20的范围
-ranges:像素值范围常为[0-256]
-举例:
img = cv2.imread('cat.jpg',0) #0灰度图
hist = cv2.calcHist([img],[0],None,[256],[0,256])
输出(256,1)#256个值,1代表二维的
-创建掩码(mask)
掩码黑白,就是取框,拿出我希望拿出的东西
需要保存的部分是白色;OpenCV规定
mask = np.zeros(img.shape[:2],np.uint8)#把mask设为和img一样大,然后设置为全黑
mask[100:300,100:400] =255 #需要保存的部分
-直方图均衡化(对比度更强,不过会丢失细节)
直方图均衡化的目的是将直方图变胖
累积概率:后面的概率要加上前面的概率(概率分布)
根据函数映射后灰度值:累积概率*255
上一个值取整:四舍五入
-均衡化函数:equ = cv2.equalizeHist(img)
-丢失细节解决方案:分块来进行均衡化
-OpenCV现成函数:自适应直方图均衡化#可忽略
-clahe = cv2.createCLAHE(cliplimit = 2.0,tileGridSize = (8,8))
第十三节 傅里叶变换
频域中一切都是静止的,傅里叶变换关注频域
-傅里叶变换的作用
-高频:变化剧烈的灰度分量,例如边界,图像中的边界比如海上的船。
-低频:变化缓慢的灰度分量,例如一片大海
-滤波:
-低通滤波:只保留低频,图像变得模糊,因为边界没被保留
-高通滤波:只保留高频,图像细节增强,边界增强
-OpenCV中主要就是cv2.dft()和cv2.idft()#前者为傅里叶变换后者为逆变换,输入图像需要先转换成np.float32格式
-得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现
-cv2.dft()返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能展示(0,255)
img_float32 = np.float32(img)# 转换成float32
dft= cv2.dft(img_float32,flags=?)
np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1])) #得到灰度图能表示的形式
-低通和高通滤波:
就是通过创建掩模图像,作用在频域图像上。
低通的话就是白色的能通过,黑色不能通过。
mask = np.zeros((rows, cols,2),np.uint8) #取中心位置
mask[crow-30:crow+30,ccol-30:ccol+30] = 1
#IDFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back=cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1]) #图像模糊处理
高通原理相同,mask反向操作一下
当对速度需求很大的时候,可以进行傅里叶变换