侧边栏壁纸
博主头像
xiaoming 博主等级

累死自己,卷死别人,为了小刘而努力!!!

  • 累计撰写 34 篇文章
  • 累计创建 7 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

OpenCV 基本操作和函数

Administrator
2023-07-11 / 0 评论 / 0 点赞 / 4 阅读 / 0 字 / 正在检测是否收录...

【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程(附带课程课件资料+课件笔记)图像处理|深度学习人工智能计算机视觉python+AI_哔哩哔哩_bilibili

1、图像的读取与显示

img = cv2.imread(img, type)
* img:图片的路径
* type(可选):cv2.IMREAD_COLOR 彩色图像
	    cv2.IMREAD_GRAYSCALE 灰度图像
* 返回值
  * 图像数据

ref = cv2.cvtColor(img, type)
* img:图片的路径
* type:cv2.COLOR_BGR2GRAY 灰度图像
* 返回值
  * 图像数据
  • 图像的读取

    img = cv2.imread('01_Picture/01_cat.jpg') 
    print(type(img)) # img 的类型为 numpy.ndarray 类型
    print(img)              # uint8 的取值范围在 0-255 之间
    
  • 图像的显示

    # opencv 读取并用 opencv 自带的展示函数不需要进行通道转换,但 opencv 读取后用其他库展示图片需要通道转换  
    # 图像显示时,可以创建多个窗口
    
    # 第一个入口参数为展示图像窗口的名字
    # 第二个入口参数为展示图像窗口中所展示的图像
    img = cv2.imread('01_Picture/01_cat.jpg') 
    cv2.imshow('image_cat',img)  
    
    # 等待时间,毫秒级,0表示任意键终止,5000ms表示5s
    cv2.waitKey(5000)  
    
    # 销毁图像窗口
    cv2.destroyAllWindows()
    
  • 视频的读取并逐帧转成灰度图显示

    vc = cv2.VideoCapture('02_Video/00_Scenery.mp4')
    if vc.isOpened():   # 检查是否打开正确
        open, frame = vc.read() # 这里的 vc.read() 相当于读取图像的第一帧
                                # 若循环不断的执行 vc.read,则不断的读取第二帧、第三帧....
        print(open) # 正常打开时,open会返回 True
        cv_show('image_scenery',frame)
    else:
        open = False
    
    
    while open: # 如果正常打开,则不停循环读取,这里可替换成 i 值,来确定读取 i 帧   
        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: # cv2.waitKey(10)为等多少时间执行下一帧,0xFF为退出键ESC
                break
    vc.release() # release()完成与 open() 相反的工作.释放 open() 向内核申请的所有资源
    cv2.destroyAllWindows() # 销毁所有窗口
    

2、灰度图的处理与保存

  • 读取灰度图

    img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_GRAYSCALE) # 读取灰度图   
    img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_COLOR)  # 读取BGR图
    
    print('type(img_gray):',type(img_gray))
    print('img_gray.size: ',img_gray.size)  # 414 × 500 = 20700
    print('img_gray.dtype:',img_gray.dtype)
    print('img_gray.shape:',img_gray.shape) # 只有一个通道
    
  • 保存图片

    cv2.imwrite('01_Picture/02_cat_gray.jpg',img_gray) # 保存图片
    

3、ROI区域

  • 位置提取ROI

    img = cv2.imread('01_Picture/01_cat.jpg')
    cat = img[0:200,0:200] # 选择图片感兴趣的区域
    cv_show('cat',cat)
    
  • 分离BGR通道

    img = cv2.imread('01_Picture/01_cat.jpg')
    b,g,r = cv2.split(img)
    cv_show('cat_b',b)        # 显示B通道灰度图
    print('b.shape:',b.shape) # B通道,单通道,灰度图
    cv_show('cat_g',g)
    print('g.shape:',g.shape) # G通道,单通道,灰度图
    cv_show('cat_r',r)
    print('r.shape:',r.shape) # R通道,单通道,灰度图
    img = cv2.merge((b,g,r))
    print('img.shape:',img.shape) # 3 通道,彩色图
    
  • 只展示R通道

    img = cv2.imread('01_Picture/01_cat.jpg')
    b,g,r = cv2.split(img)
    img = cv2.merge((b,g,r))
    cur_img = img.copy()
    cur_img[:,:,0] = 0 
    cur_img[:,:,1] = 0
    cv_show('R',cur_img)
    
  • 只展示G通道

    img = cv2.imread('01_Picture/01_cat.jpg')
    cur_img = img.copy()
    cur_img[:,:,0] = 0 
    cur_img[:,:,2] = 0
    cv_show('G',cur_img)
    
  • 只展示B通道

    img = cv2.imread('01_Picture/01_cat.jpg')
    cur_img = img.copy()
    cur_img[:,:,1] = 0 
    cur_img[:,:,2] = 0
    cv_show('B',cur_img)
    

4、边界填充

① 边界填充就是对图像进行一些变换,让原始图像进行扩大。

② 边界填充的入口参数:

  • BORDER_REPLICATE:复制法,也就是复制最边缘像素。
  • BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
  • BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
  • BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
  • BORDER_CONSTANT:常量法,常数值填充。
img = cv2.imread('01_Picture/01_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,borderType=cv2.BORDER_WRAP)
# 方式五:常量法
constant = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,cv2.BORDER_CONSTANT,value=0)

5、数值越界处理

img_cat = cv2.imread('01_Picture/01_cat.jpg')
img_dog = cv2.imread('01_Picture/03_dog.jpg')

img_cat2 = img_cat + 10 # 将 img_cat 矩阵中每一个值都加 10
print(img_cat[:5,:,0])
print(img_cat2[:5,:,0])
print((img_cat+img_cat2)[:5,:,0])  # 0-255 若相加越界后 294 用 294%256 获得余数 38   


cv2.add(img_cat,img_cat2)[:5,0] # cv2.add 是越界后取最大值 255

6、图像融合与缩放

  • 图像融合

    • 不同长宽的图像不能进行融合操作

      img_cat = cv2.imread('01_Picture/01_cat.jpg')
      img_dog = cv2.imread('01_Picture/03_dog.jpg')
      
      print(img_cat.shape)
      print(img_dog.shape)
      img_cat+img_dog # 不同数据大小不能执行数值计算操作
      
    • 调整图像大小

      print(img_cat.shape)
      print(img_dog.shape)
      img_dog = cv2.resize(img_dog,(500,414))
      img_dog.shape
      
    • 图像融合

      res = cv2.addWeighted(img_cat,0.4,img_dog,0.6,0) # img_cat 的权重为 0.4,img_dog 的权重为 0.6, 0 代表整个图像添加的系数
      print(img_dog.shape)
      plt.imshow(res)
      
  • 图像的缩放

    • 倍数缩放

      img = cv2.imread('01_Picture/01_cat.jpg') 
      res = cv2.resize(img,(0,0),fx=3,fy=1) # (0,0)表示不确定具体值,fx=3 相当于行像素 x 乘 3,fy=1 相当于 y 乘 1   
      plt.imshow(res)
      
    • 等比例缩放

      res = cv2.resize(img,(0,0),fx=1.5,fy=1.5) # 同比例放缩
      plt.imshow(res)
      

7、图像的阈值处理

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方法的反转
	* cv2.THRESH_TRUNC            大于阈值部分设为阈值,否则不变
	* cv2.THRESH_TOZERO          大于阈值部分不改变,否则设为0
	* cv2.THRESH_TOZERO_INV  THRESH_TOZERO的反转
* 返回值:
	* dst:转换后的输出图
	* ret:阈值
img = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_COLOR)
img_gray = cv2.imread('01_Picture/01_cat.jpg',cv2.IMREAD_GRAYSCALE)
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
print(ret)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV) # THRESH_BINARY_INV 相对THRESH_BINARY 黑的变成白的,白的变成黑的
print(ret)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
print(ret)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
print(ret)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
print(ret)

8、图像平滑处理

  • 均值滤波

    # 均值滤波
    # 简单的平均卷积操作,方框中的值相加,取平均,替换掉中心204的值
    
    blur = cv2.blur(img,(3,3)) # (3,3) 为卷积核的大小,通常情况核都是奇数 3、5、7
    cv2.imshow('blur',blur)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 方框滤波

    # 在 Python 中 -1 表示自适应填充对应的值,这里的 -1 表示与颜色通道数自适应一样
    box = cv2.boxFilter(img,-1,(3,3),normalize=True)  # 方框滤波如果做归一化,得到的结果和均值滤波一模一样
    cv2.imshow('box',box)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    
    box = cv2.boxFilter(img,-1,(3,3),normalize=False)  # 越界的值取 255
    cv2.imshow('box',box)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 高斯滤波

    # 高斯函数,越接近均值时,它的概率越大。
    # 离中心值越近的,它的权重越大,离中心值越远的,它的权重越小。
    
    aussian = cv2.GaussianBlur(img,(5,5),1)
    cv2.imshow('aussian',aussian)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 中值滤波

    # 中值滤波
    # 排序后拿中值替代中间元素值的大小
    
    median = cv2.medianBlur(img,5)
    cv2.imshow('median',median)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 展示所有滤波后的图像

    # 展示所有的
    
    res = np.hstack((blur,aussian,median)) # 矩阵横着拼接
    #res = np.vstack((blur,aussian,median)) # 矩阵竖着拼接
    print(res)
    cv2.imshow('median vs average', res)  
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

9、腐蚀和膨胀与开运算、闭运算、梯度运算与礼帽和黑帽

  • 腐蚀操作

    # 只要框里有黑色,中心点的值就变为黑色,即原来的白色被黑色腐蚀掉
    kernel = np.ones((5,5),np.uint8)    # 选取一个都是1的卷积核
    erosion = cv2.erode(img,kernel,iterations=1)  # 递归运行的次数
    
    cv2.imshow('erosion',erosion)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 膨胀操作

    # 先腐蚀 后膨胀,抵消腐蚀造成的损害
    kernel = np.ones((3,3),np.uint8)
    dige_erosion = cv2.erode(img,kernel,iterations=1)   
    cv2.imshow('erosion',dige_erosion)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    kernel = np.ones((3,3),np.uint8)
    dige_dilate = cv2.dilate(dige_erosion,kernel,iterations=1)
    cv2.imshow('dilate',dige_dilate)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 开运算

    # 开:先腐蚀,再膨胀
    img = cv2.imread('01_Picture/05_Dige.png')
    
    kernel = np.ones((5,5),np.uint8)
    opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel) 
    
    cv2.imshow('opening',opening)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 闭运算

    # 闭:先膨胀,再腐蚀
    img = cv2.imread('01_Picture/05_Dige.png')
    
    kernel = np.ones((5,5),np.uint8)
    closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel) 
    
    cv2.imshow('closing',closing)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 梯度运算

    # 梯度 = 腐蚀-膨胀   可以获取边界信息
    pie = cv2.imread('01_Picture/06_pie.png')
    
    kernel = np.ones((7,7),np.uint8)
    dilate = cv2.dilate(pie,kernel,iterations=5) 
    erosion = cv2.erode(pie,kernel,iterations=5) 
    
    res = np.hstack((dilate,erosion))
    
    cv2.imshow('res',res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    gradient = cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)   # 进行梯度运算
    cv2.imshow('gradient',gradient)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 礼帽

    礼帽 = 原始输入-开运算

    # 原始带刺,开运算不带刺,原始输入-开运算 = 刺
    img = cv2.imread('01_Picture/05_Dige.png')
    kernel = np.ones((5,5),np.uint8)
    tophat = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
    cv2.imshow('tophat',tophat)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 黑帽

    黑帽 = 闭运算-原始输入

    # 原始带刺,闭运算带刺并且比原始边界胖一点,闭运算-原始输入 = 原始整体
    img = img = cv2.imread('01_Picture/05_Dige.png')
    kernel = np.ones((5,5),np.uint8)
    blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
    cv2.imshow('blackhat',blackhat)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

10、Sobel算子、Scharr算子与Laplacian算子

  • Sobel算子

    image-20230717225315545

    ① Sobel算子函数:cv2.Sobel(src, ddepth, dx, dy, ksize),返回值为Sobel算子处理后的图像。

    - ddepth:图像的深度

    - dx 和 dy 分别表示水平和竖直方向

    - ksize 是 Sobel 算子的大小

    ② 靠近最近点的左右和上下的权重最高,所以为±2。

    img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE)
    cv_show(img,'img')
    
    img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE)
    sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) # 计算x方向的边界
    sobelx = cv2.convertScaleAbs(sobelx)           # 取绝对值
    sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3) # 计算y方向的边界
    sobely = cv2.convertScaleAbs(sobely)           # 取绝对值
    sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)  # 合并xy方向的边界图像
    cv_show(sobelxy,'sobelxy')
    
  • Scharr算子与Laplacian算子

    image-20230717230209551

    Scharr算子对结果的差异更敏感一些。

    ① Laplacian算子用的是二阶导,对噪音点更敏感一些。

    ② 如果中心点是边界,它与周围像素点差异的幅度会较大,Laplacian算子根据此特点可以把边界识别出来。

    # 不同算子的差异
    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')
    

    image-20230717230331916

11、Canny边缘检测

  • 流程介绍

    • 使用高斯滤波器,以平滑图像,滤除噪声。
    • 计算图像中每个像素点的梯度强度和方向。
    • 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
    • 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
    • 通过抑制孤立的弱边缘最终完成边缘检测。
  • 梯度和方向

    image-20230718193213315

  • 非极大值抑制

    • 方法一:

      ① C 点的梯度和方向可以通过前一步算出来。

      ② C 点的梯度是不是一个极大值点,应该是去跟它的临近点去比较。

      ③ 利用 C 点梯度的方向,可以得到上面有一个交点 Q,下面有一个交点 Z,如果 C 点的梯度比 Q 和 Z 都大,那么 C 就是极大值点,其中 Q 和 Z 的梯度值通过线性差值法来计算。

      ④ 如果 C 的梯度是极大值点,那么 C 就是边缘点。否则 C 不是极大值点,就会被抑制。

      image-20230718193305292

    • 方法二:

      ① 简单计算将像素点周围固定为八个像素,当梯度角度相交的点与哪个方向近,就哪个方向的两个点。

      ② 例如,梯度方向是 43° 就取上下两个像素来做极大值判断,如果梯度方向是 46°,就取左下、右上两个像素来做极大值判断。

      ③ 如下图所示,如果 A 的梯度值比 B 和 C 都要大,那么 A 就是边界,由于边界与梯度方向垂直,所以如下图所示黑色为边界。

      image-20230718193412181

  • 双阈值检测

    ① C 在 minVal 与 maxVal 之间,是候选边界,若 C 的左右或上下两边连有 A,而 A 是边界,那么定义 C 也是边界。

    ② B 在 minVal 与 maxVal 之间,是候选边界,若B的左右或上下像素都不是边界,那么 B 就被舍弃,不定义为边界。

    image-20230718193448310

  • 使用方式

    def cv_show(img,name):
        cv2.imshow(name,img)
        cv2.waitKey()
        cv2.destroyAllWindows()
    
    img = cv2.imread('01_Picture/07_Lena.jpg',cv2.IMREAD_GRAYSCALE) 
    # 第二个参数为minVal,第三个参数为maxVal 值越大代表越严格,梯度信息越少
    v1 = cv2.Canny(img,80,150)
    v2 = cv2.Canny(img,50,100)
    res = np.hstack((v1,v2))
    cv_show(res,'res')
    

12、图像金字塔

① 金字塔的底层是比较大,越往上越小,图像金字塔就是把图像组合成金字塔的形状。

② 图像金字塔可以做图像特征提取,做特征提取时有时可能不光对原始输入做特征提取,可能还会对好几层图像金字塔做特征提取。可能每一层特征提取的结果是不一样的,再把特征提取的结果总结在一起。

③ 常用的两种图像金字塔形式:

- 高斯金字塔

- 拉普拉斯金字塔

  • 高斯金字塔

    • 向下采样方法 ( 缩小 )

      image-20230718193835909

    • 向上采样方法 ( 放大 )

      image-20230718193859776

    • 使用方式

      img = cv2.imread('01_Picture/09_AM.png')
      up = cv2.pyrUp(img)       #上采样
      up_down = cv2.pyrDown(up) #下采样
      cv_show(np.hstack((img,up_down)),'up_down')
      
  • 拉普拉斯金字塔

    ① 拉普拉斯金字塔的每一层图像尺寸不变。

    ② 拉普拉斯金字塔的每一层操作都是上一层处理后作为输入,该输入减去该输入缩小放大后的图像,获得该层的输出。

    image-20230718194111078

    使用方式

    img = cv2.imread('01_Picture/09_AM.png')
    domn = cv2.pyrDown(img)
    down_up = cv2.pyrUp(down)
    L_1 = img - down_up
    cv_show(L_1,'L_1')
    print(L_1.shape)
    

13、图像轮廓

  • 轮廓绘制

    ① 边缘有一些零零散散的线段也可以当做边缘,反正梯度上下左右发生差异,就把它当做边缘了。

    ② 图像的轮廓必须是一个整体,不是零零散散的,而是连在一块的。

    ③ 图像轮廓函数:cv2.findContours(img,mode,method)

    mode:轮廓检索模式

    • RETR_EXTERNAL :只检索最外面的轮廓。
    • RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中。
    • RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界。
    • RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。( 最常用 )

    method:轮廓逼近方法

    • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,如下图左所示。所有其他方法输出多边形 ( 顶点的序列 ),如下图右所示。
    • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分,如下图右所示。

    ④ 为了更高的准确率,轮廓检测使用二值图像。

    img = cv2.imread('01_Picture/10_contours.png')
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 图像二值化  大于17的取255,小于127的取0   
    
    binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) # 轮廓检测
    
    draw_img = img.copy() # 若不用拷贝后的,而是用原图画轮廓,则画轮廓图绘把原始的输入图像重写,覆盖掉 
    res = cv2.drawContours(draw_img,contours,3,(0,0,255),2) # 轮廓绘制    原始图像 轮廓层次 第几个轮廓-1为显示全部 绘制的轮廓的颜色 线条的宽度
    cv_show(res,'res')
    
  • 轮廓近似

    ① 正常轮廓展示是最右边的图,但是当我们需要轮廓没有那么不规则,而是想要轮廓近似成规则的形状,这就叫轮廓近似,近似成下图中中间图像的轮廓。

    ② 一条呈抛物线的曲线的端点为 A、B 两点,取曲线上到直线 AB 距离最大的点,该点为 C 点,若 C 点到直线的距离小于设置的阈值,则可以把直线 AB 当做曲线的近似,若 C 点到直线的距离大于设置的阈值,那么曲线不能用直线 AB 来近似,而 AC 曲线可能用 AC 直线来代替、BC曲线可能用 BC 直线来代替。再通过阈值来判断是否可以代替。

    image-20230720101539100

    • 轮廓近似展示

      img = cv2.imread('01_Picture/11_contours2.png')
      
      gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
      ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 大于17的取255,小于127的取0   
      binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
      cnt = contours[0]
      
      draw_img = img.copy()
      res = cv2.drawContours(draw_img,[cnt],-1,(0,0,255),2) 
      cv_show(res,'res')
      
      epsilon = 0.1 * cv2.arcLength(cnt,True) # 周长的百分比,这里用 0.1 的周长作阈值
      approx = cv2.approxPolyDP(cnt,epsilon,True) # 第二个参数为阈值
      draw_img = img.copy()
      res = cv2.drawContours(draw_img,[approx],-1,(0,0,255),2)
      cv_show(res,'res')
      
    • 外界矩形

      img = cv2.imread('01_Picture/10_contours.png')
      
      gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
      ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 大于17的取255,小于127的取0   
      binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
      cnt = contours[6]
      
      x,y,w,h = cv2.boundingRect(cnt) # 可以得到矩形四个坐标点的相关信息
      img = cv2(img,(x,y),(x+w,y+h),(0,255),2)
      cv_show(img,'img')
      
    • 外接圆

      img = cv2.imread('01_Picture/10_contours.png')
      
      gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
      ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 大于17的取255,小于127的取0   
      binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
      cnt = contours[0]
      
      draw_img = img.copy()
      (x,y),redius = cv2.minEnclosingCircle(cnt)
      center = (int(x),int(y))
      redius = int(redius)
      img = cv2.circle(draw_img,center,redius,(0,255,0),2)
      cv_show(img,'img')
      

14、模板匹配

① 模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度(例如值127与值190的区别),这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。

② 假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)。

③ 模板匹配计算方式6种方式 ( 用归一化后的方式更好一些 ):

  • TM_SQDIFF:计算平方不同,计算出来的值越小,越相关。
  • TM_CCORR:计算相关性,计算出来的值越大,越相关。
  • TM_CCOEFF:计算相关系数,计算出来的值越大,越相关。
  • TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关。
  • TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关。
  • TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关。

模板匹配单个对象

import cv2 #opencv的缩写为cv2
import matplotlib.pyplot as plt # matplotlib库用于绘图展示
import numpy as np   # numpy数值计算工具包

template = cv2.imread('01_Picture/12_Face.jpg',0)  # 0 表示以灰度图方式读取
img = cv2.imread('01_Picture/13_Lena.jpg',0) 
h, w = template.shape[:2] # 获得模板的宽和高
print(img.shape)
print(template.shape)

methods = ['cv2.TM_CCOEFF','cv2.TM_CCOEFF_NORMED','cv2.TM_CCORR',
          'cv2.TM_CCORR_NORMED','cv2.TM_SQDIFF','cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
print(res.shape) # 返回的矩阵大小 (A-a+1)x(B-b+1)
min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res) # 返回模板匹配后最小值、最大值的位置
print(min_val) # cv2.TM_SQDIFF方法中,越小的值表示像素点的差异越小
print(max_val)
print(min_loc) # 当获得最小值对应的模板左上角的位置,加上模板自身的长、宽,可以在原图像中画出最匹配的区域
print(max_loc)

for meth in methods:
    img2 = img.copy()
    # 匹配方法的真值
    method = eval(meth) # 提取字符串中的内容,不能用字符串的形式
    print(method)
    res = cv2.matchTemplate(img,template,method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
  
    # 如果是平方差匹配 TM_SQDIFF 或归一化平方差匹配 TM_SQDIFF_NORMED,取最小值
    if method in [cv2.TM_SQDIFF,cv2.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0]+w,top_left[1]+h)
  
    # 画矩形
    cv2.rectangle(img2,top_left,bottom_right,255,2)
  
    plt.subplot(121), plt.imshow(res, cmap='gray')
    plt.xticks([]), plt.yticks([]) # 隐藏坐标轴
    plt.subplot(122),plt.imshow(img2,cmap='gray')
    plt.xticks([]),plt.yticks([])
    plt.suptitle(meth)
    plt.show()

模板匹配多个对象

img_rgb = cv2.imread('01_Picture/14_Mario.jpg')
img_gray = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2GRAY)
print('img_gray.shape:',img_gray.shape)
template = cv2.imread('01_Picture/15_Mario_coin.jpg',0)
print('template.shape:',template.shape)
h, w = template.shape[:2]

# res 是 result 的简称
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) # res 是返回每一个小块窗口得到的结果值
print('res.shape:',res.shape)
threshold = 0.8

# 取匹配程度大于 80% 的坐标
loc = np.where(res >= threshold) # np.where 使得返回 res 矩阵中值大于 0.8 的索引,即坐标
print('type(loc):',type(loc)) # loc 为元组类型
print('len(loc):',len(loc))  # loc 元组有两个值
print('len(loc[0]):',len(loc[0]),'len(loc[1]):',len(loc[1]))   # loc 元组每个值 120 个元素
print('type(loc[0]):',type(loc[0]),'type(loc[1]):',type(loc[1])) # loc 元组每个值的类型为 numpy.array 
print("loc[::-1]:",loc[::-1]) # loc[::-1] 表示顺序取反,即第二个 numpy.array 放在第一个 numpy.array 前面   

i = 0
# zip函数为打包为元组的列表,例 a = [1,2,3] b = [4,5,6] zip(a,b) 为 [(1, 4), (2, 5), (3, 6)]  
for pt in zip(*loc[::-1]): # 当用 *b 作为传入参数时, b 可以为列表、元组、集合,zip使得元组中两个 numpy.array 进行配对   
    bottom_right = (pt[0] + w, pt[1] + h)
    cv2.rectangle(img_rgb, pt, bottom_right, (0,0,255),2)
    i = i + 1
print('i:',i)

cv2.imshow('img_rgb',img_rgb)
cv2.waitKey(0)

15、图像直方图

16、傅里叶变换

image-20230720124132733

作用:

① 高频:变化剧烈的灰度分量,例如边界礁石。

② 低频:变化缓慢的灰度分量,例如一片大海。

③ 高通滤波器:只保留高频,会使得图像细节增强。高频边界锐化了,增强了,细节更明显了。

④ 低通滤波器:只保留低频,会使得图像模糊。低频信息保留下来了,高频信息没了,图像边界会变得模糊了。

① opencv 中主要就是 cv2.dft() 执行傅里叶变换到频域中 和 cv2.idft() 执行逆傅里叶变换,输入图像需要先转换成 np.float32 格式。

② 得到的结果中频率为 0 的部分会在左上角,通常要转换到中心位置,可以通过 shift 变换来实现。

③ cv2.dft() 返回的结果是双通道的 ( 实部,虚部 ),通常还需要转换成图像格式才能展示(0,255)像素值。

  • 高通滤波器

    import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    
    img = cv2.imread('01_Picture/13_Lena.jpg',0)
    img_float32 = np.float32(img)
    
    # DFT ( 傅里叶变换 )
    dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)
    
    rows, cols = img.shape
    crow, ccol = int(rows/2), int(cols/2) # 中心位置
    
    # 低通滤波
    mask = np.zeros((rows,cols,2),np.uint8)
    mask[crow-30:crow+30,ccol-30:ccol+30] = 1 # 只保留中心点周围的区域,中心点为最低频的
    
    # IDPT (傅里叶逆变换)
    fshift = dft_shift * mask # 用掩码提取 dft_shift 中相应区域,是 1 就保留,不是 1 就过滤了
    f_ishift = np.fft.ifftshift(fshift) # 把拉到中心位置的频谱区域给还原回去,依旧回到左上角
    img_back = cv2.idft(f_ishift)
    img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1]) # 将实部和虚部结合起来,才能将傅里叶变换的结果显示出来  
    
    plt.subplot(121),plt.imshow(img,cmap='gray')
    plt.title('Input Image'), plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(img_back,cmap='gray')
    plt.title('Result'), plt.xticks([]), plt.yticks([])
    

    image-20230720125801992

  • 低通滤波器

    import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    
    img = cv2.imread('01_Picture/13_Lena.jpg',0)
    img_float32 = np.float32(img)
    
    dft = cv2.dft(img_float32,flags=cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)
    
    rows, cols = img.shape
    crow, ccol = int(rows/2), int(cols/2) # 中心位置
    
    # 高通滤波
    mask = np.ones((rows,cols,2),np.uint8)
    mask[crow-30:crow+30,ccol-30:ccol+30] = 0 # 中间区域置 0,外面的区域置 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])
    
    plt.subplot(121),plt.imshow(img,cmap = 'gray')
    plt.title('Input Image'), plt.xticks([]),plt.yticks([])
    plt.subplot(122),plt.imshow(img_back,cmap='gray')
    plt.title('Result'),plt.xticks([]),plt.yticks([])
    

    image-20230720125838377

17、

0

评论区