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反向操作一下

    当对速度需求很大的时候,可以进行傅里叶变换