OpenCV学习笔记

0.杂七杂八

  1. 解决matplotlib中文显示乱码或方块问题

    1. 代码头部加上此段代码即可

    2. def set_ch():
          from pylab import mpl
          mpl.rcParams['font.sans-serif'] =['FangSong']# 指定默认字体
          mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号-显示为方块的问题
      set_ch()
      
  2. OpenCV是BGR通道,plt默认RGB通道,若使用cv2.imread()读入图像,用plt.imshow()展示原始图像或者展示对读入图像进行一系列操作后的图像时,需要进行通道转换,在展示灰度图像时,需要使用plt.imshow(img, camp="gray")

  3. ndarray:(x, y, z[, …]):最外层为0维,向里依次递增,表示0维有x个1维的元素,1维有y个2维的元素,2维有z个3维元素

  4. arr.reshape(x,y):表示先将arr数组平展开,然后按顺序(从左到右,从上到下)填入一个x*y的二维数组中

  5. pts.sum(axis=1)表示将pts(ndarray:(4,2))中第1维的元素相加最后形成一个s数组(ndarray:(4,))

  6. np.diff(pts, axis = 1)表示将pts(ndarray:(4,2))中第1维的中元素依次后一个减去前一个最后形成一个diff数组(ndarray:(4,1))

1. 数字图像

1. 位数

平常接触的图像都是8位数图像,包含0-255灰度,其中0代表最黑,255代表最白

2. 分类

二值图像

二值图像的二维矩阵仅由0、1两个值构成,“0”代表黑色,“1”代白色,或0、255两个值构成

灰度图

单通道表示,每个像素点只有一种颜色灰色,根据亮度的不同,有区间0-255。用(H, W, 1)的二维矩阵构成

彩色图

三通道表示,每个像素点可以为任意颜色,根据颜色和通道的不同,有区间0-255。用(H, W, 3)的二维矩阵构成。RGB图像的数据类型一般为8位无符号整形,通常用于表示和存放真彩色图像,用((H, W, 3), np.uint8)设置

注意

  • OpenCV中读取通道顺序为(B, G, R)
  • matplotlib中读取通道顺序为(R, G, B)

2. OpenCV的模块

  • core模块:实现了最核心的数据结构及其基本运算,如绘图函数、数组操作相关函数等
  • highgui模块:实现了视频与图像的读取、显示、存储等接口
  • imgproc模块:实现了图像处理的基础方法,包括图像滤波、图像的几何变换、平滑、闻值分割、形态学处理、边缘检测、目标检测、运动分析和对象跟踪等
  • features2d模块:用于提取图像特征以及特征匹配
  • obidetect模块:实现了一些目标检测的功能,经典的基于Haar、LBP特征的人脸检测,基于HOG的行人、汽车等目标检测,分类器使用Cascade Classification(级联分类)和Latent SVM等
  • stitching模块:实现了图像拼接功能
  • FLANN模块:包含快速近似最近邻搜索FLANN和聚类Clustering算法
  • photo模块:包含图像修复和图像去噪两部分
  • video模块:针对视频处理,如背景分离,前景检测、对象跟踪等
  • G-API模块:包含超高效的图像处理pipeline引擎

3. 图像基本操作

1. 图像的IO操作

"""
    读取图像
        cv.imread("图像地址", 0|1|-1)
            1表示:cvIMREAD*COLOR:以彩色模式加载图像,任何图像的透明度都将被忽略,默认为1
            0表示:cv.IMREAD*GRAYSCALE:以灰度模式加载图像
            -1表示:CvIMREAD_UNCHANGED:包括alpha通道的加载图像模式
    显示图像
        cv.imshow('显示窗口名称', xxx)
            xxx表示:需要加载显示的图像
        cv.waitKey(0)
            防止显示图像出现闪退现象
    保存图像
        cv.imwrite("文件名,想要保存在哪里", xxx)
            xxx表示:想要保存的图像
"""
import numpy as np
import cv2 as cv

img = cv.imread("G:/Pycharm_workspace/OpenCV_Study/data/color.jpg", 1)
cv.imshow("img_color", img)
cv.waitKey(0)
cv.imwrite("G:/Pycharm_workspace/OpenCV_Study/data/color1.jpg", img)

2. 绘制图形

在OpenCV中图像坐标系:左上角为原点,y轴向下,x轴向右,用于表示坐标点,在代码中的二维数组中,行(高)对应y轴,列(宽)对应x轴

注意:

  • 当通过numpy数组创建图像时(img_black = np.zeros((512, 512, 3), np.uint8)),其中必须标明np.uint8,表示数组元素大小只能在0-255之间
  • 创建矩阵(H, W, 3)时用的是( ),创建修改BGR三原色[B, G, R]时用的是[ ]
"""
    绘制直线
        cv.line(img, start, end, color, thickness)
            img:要绘制直线的图像
            start, end:直线的起点和终点
            color:线条的颜色
            thickness:线条宽度
    绘制圆形
        cv.circle(img, centerpoint, r, color, thickness)
            img:要绘制圆形的图像
            centerpoint, r:圆心和半径
            color:线条的颜色
            thickness:线条宽度,为-1时生成闭合图案并填充颜色
    绘制方形
        cv.rectangle(img, pt1, pt2, color, thickness)
            img:要绘制方形的图像
            pt1, pt2:方形的左上角和右下角的坐标
            color:线条的颜色
            thickness:线条宽度
    添加文字
        cv.putText(img, text, org, fontFace, fontScale, color, thickness=None,lineType=None, bottomLeftOrigin=None)
            img:要添加文字的图像
            text:添加文字的文本内容
            org:文字的位置
            fontFace, fontScale:字体样式、字体大小
            color:线条的颜色
            thickness:线条宽度
"""

import cv2 as cv
import numpy as np
# 创建黑色图像    (512, 512, 3)表示三个大小为512*512的二维数组,分别用来表示RGB三通道
img_black = np.zeros((512, 512, 3), np.uint8)
# 绘制图形
cv.line(img_black, (0, 0), (511, 511), (255, 0, 0), 5)  # opencv中图像的存储是以BGR形式存储,(255, 0, 0)表示蓝色
cv.rectangle(img_black, (384, 0), (510, 128), (0, 255, 0), 3) 
cv.circle(img_black, (447, 63), 63, (0, 0, 255), -1)
font = cv.FONT_HERSHEY_SIMPLEX
cv.putText(img_black, "opencv", (10, 500), font, 4, (255, 255, 255), 2, cv.LINE_AA)
# 展示图像
cv.imshow("opencv_img", img_black)
cv.waitKey(0)

3. 获取并修改图像中的像素点

"""
    通过行和列的坐标值获取该像素点的像素值
    对于BGR图像,它返回一个蓝,绿,红值的数组
    对于灰度图像,仅返回相应的强度值
    使用相同的方法对像素值进行修改
"""
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

img_black = np.zeros((512, 512, 3), np.uint8)
plt.imshow(img_black[:,:,::-1])

# 获取某个像素点
img_black[100, 100]	# array([0, 0, 0], dtype=uint8)

# 仅获取蓝色通道的强度值
img_black[100, 100, 0]	# 0

# 修改某个位置的像素值
img_black[100, 100] = [255, 255, 255]

plt.imshow(img_black[:,:,::-1])

# 获取某个像素点
img_black[100, 100]	# array([255, 255, 255], dtype=uint8)

4. 获取图像的属性

"""
形状:img.shape

图像大小:img.size

数据类型:img.dtyte
"""
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img_black = np.zeros((512, 512, 3), np.uint8)

# 形状:img.shape
img_black.shape	# (512, 512, 3)

# 数据类型:img.dtyte
img_black.dtype	# dtype('uint8')

# 图像大小:img.size
img_black.size	# 512*512*3 = 786432

5. 图像通道的拆分和合并

  • 需要在B,G,R通道图像上单独工作时,可以将BGR图像分割为单个通道
  • 需要将这些单独的通道合并到BGR图像
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/data/color.jpg", 1)

# 通道拆分
b, g, r =cv.split(img_color)
print(b)
print(g)
print(r)

# 通道合并
img_color = cv.merge((b, g, r))

6. 色彩空间的改变

"""
    cv.cvtColor(input_image, flag)
    flag:
        CV.COLOR_BGR2GRAY: BGR->Gray
        CV.COLOR BGR2HSV: BGR->HSV
"""
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/color.jpg", 1)
plt.imshow(img_color[:,:,::-1])

gray_img = cv.cvtColor(img_color, cv.COLOR_BGR2GRAY)
plt.imshow(gray_img,cmap=plt.cm.gray)

hsv_img = cv.cvtColor(img_color, cv.COLOR_BGR2HSV)
plt.imshow(hsv_img)

7. 算数操作

注意:无论是加法、减法、混合操作,要求两个图像具有相同的大小和类型,或者第二个图像可以是标量值

推荐使用OpenCV提供的加减API

  • Opencv加法和Numpy加法之间存在差异。OpenCV的加法是饱和操作,而Numpy的加法是模运算
  • Opencv减法和Numpy减法之间存在差异。OpenCV的减法是保底为0(负数变成0),而Numpy减法是遇负相加(负数+256)

图像的加法

可以使用OpenCV的cv.add函数把两幅图像相加,或者可以简单地通过numpy操作添加两个图像,如res=img1+img2

加法一般用于图像的合并

注意:

  • Opencv加法和Numpy加法之间存在差异。OpenCV的加法是饱和操作,而Numpy的加法是模运算

    • x = np.uint8([250])
      y = np.uint8([10])
      print(cv.add(x + y)) # 250 + 10 = 255
      print(x + y) # 250+ 10 = 260 260 % 256 = 4

    • import cv2 as cv
      import numpy as np
      import matplotlib.pyplot as plt
      
      img_view = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/view.jpeg", 1)
      img_rain = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/rain.jpeg", 1)
      
      plt.imshow(img_view[:,:,::-1])
      
      plt.imshow(img_rain[:,:,::-1])
      
      # cv形式的相加
      img_cv_add = cv.add(img_view, img_rain)
      
      plt.imshow(img_cv_add[:,:,::-1])
      
      # numpy数组形式的相加
      img_np_add = img_view + img_rain
      
      plt.imshow(img_np_add[:,:,::-1])
      

图像的减法

可以使用OpenCV的cv.subtract函数把两幅图像相加,或者可以简单地通过numpy操作添加两个图像,如res=img1-img2

减法一般用于图像的背景消除

注意

  • Opencv减法和Numpy减法之间存在差异。OpenCV的减法是保底为0(负数变成0),而Numpy减法是遇负相加(负数+256)

    • x = np.uint8([250])
      y = np.uint8([255])
      print(cv.subtract(x, y)) # 250 - 255 = 0
      print(x - y) # 250 - 255 = -5 -5 + 256 = 251

    • import cv2 as cv
      import numpy as np
      import matplotlib.pyplot as plt
      
      img_view = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/view.jpeg", 1)
      img_rain = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/rain.jpeg", 1)
      
      plt.imshow(img_view[:,:,::-1])
      plt.imshow(img_rain[:,:,::-1])
      
      #cv形式的相减
      img_cv_subtract = cv.subtract(img_view, img_rain)
      plt.imshow(img_cv_subtract[:,:,::-1])
      
      #numpy数组形式的相加相减
      img_np_subtract = img_view - img_rain
      plt.imshow(img_np_subtract[:,:,::-1])
      

图像的混合

混合即是加法,不同的是两幅图像的权重不同,图像混合的计算公式如下:
g ( x ) = ( 1 − a ) f 1 ( x ) + a f 2 ( x ) g(x) = (1-a)f_1(x)+af_2(x) g(x)=(1a)f1(x)+af2(x)
利用cv.addWeighted(img1, a, img2, b, y)方法进行混合

注意:

  • cv.addWeighted(img1, a, img2, b, y)
  • 其中:
    • a为img1的权重,b为img2的权重,a + b 必须等于1
    • y为偏移权重,普通的图片混合时,一般设置为0
#混合

img_cv_addWeighted = cv.addWeighted(img_view, 0.7, img_rain, 0.3, 0)

plt.imshow(img_cv_addWeighted[:,:,::-1])

4. 图像处理操作

1. 几何变换

图像缩放

cv2.resize(src,dsize,fx=0,fy=0,interpolation=cV2.INTER LINEAR)

  • src:输入图像

  • dsize: 绝对尺寸,直接指定调整后图像的大小

  • fx,fy: 相对真实宽高尺寸,将dsize设置为None,然后将x和fy设置为比例因子即可

  • interpolation:插值方法

    • 插值含义
      CV2.INTER LINEAR双线性插值法
      CV2INTER NEAREST最近邻插值
      CV2INTER AREA像素区域重采样(默认)
      CV2.INTER CUBIC双三次插值
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/color.jpg", 1)
print(img_color.shape)	# (666, 1000, 3)
#绝对尺寸,shape[:2]因为shape为(h,w,3),取前两个即可

rows, cols = img_color.shape[:2]
res1 = cv.resize(img_color, (2*cols, 2*rows), interpolation=cv.INTER_CUBIC)
plt.imshow(res1[:,:,::-1])
print(res1.shape)	# (1332, 2000, 3)

res2 = cv.resize(img_color, None, fx=0.5, fy=0.5, interpolation=cv.INTER_CUBIC)
plt.imshow(res2[:,:,::-1])
print(res2.shape)	# (333, 500, 3)

图像平移

图像平移将图像按照指定方向和距离,移动到相应的位置

cv.warpAffine(img,M,dsize)

  • img:输入图像

  • M:2*3移动矩阵

    • 对于(x,y)处的像素点,要把它移动到(x + t_x, y +t_y)处时,M矩阵应如下设置:

    • M = [ 1 0 t x 0 1 t y ] M=\left[\begin {array}{c} 1 &0 &t_x \\ 0 &1 &t_y \\ & & \\ \end{array}\right] M= 1001txty

    • 注意:将M设置为np.float32类型的Numpy数组

  • dsize: 输出图像的大小

    • 输出图像的大小,它应该是(宽度,高度)的形式。width=列数,height=行数
    • 这里的输出图像大小指的是坐标系大小,不是图像大小
    • 如果坐标系大小依旧为原始图像大小,则平移后图像超出的部分自动截断
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def set_ch():	# set_ch()解决matplotlib图像显示乱码或方块问题
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']# 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号-显示为方块的问题
set_ch()


img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/color.jpg", 1)
print(img_color.shape)

#图像平移
rows, cols = img_color.shape[:2]
M = np.float32([[1, 0, 100], [0, 1, 50]])   #平移矩阵
img_color_dst = cv.warpAffine(img_color, M, (cols, rows))

#图像显示
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10,8), dpi=100)
axes[0].imshow(img_color[:,:,::-1])
axes[0].set_title(u"原图")
axes[1].imshow(img_color_dst[:,:,::-1])
axes[1].set_title(u"移动后")

图像旋转

图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持这原始尺寸。图像旋转后图像的水平对称轴、垂直对称轴及中心坐标原点都可能会发生变换,因此需要对图像旋转中的坐标进行相应转换

调用cv2.getRotationMatrix2D(center,angle, scale)方法,获得旋转矩阵M,然后调用cv.warpAffine完成图像的旋转

  • center:旋转中心
  • angle:旋转角度,正数为逆时针旋转,负数为顺时针旋转
  • scale:缩放比例
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']	# 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/color.jpg", 1)
print(img_color.shape)

rows, cols = img_color.shape[:2]

#生成旋转矩阵
M = cv.getRotationMatrix2D((cols/2,rows/2), -90, 1)

#进行旋转变换
img_color_dst = cv.warpAffine(img_color, M, (cols, rows))

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10,8), dpi=100)
axes[0].imshow(img_color[:,:,::-1])
axes[0].set_title(u"原图")
axes[1].imshow(img_color_dst[:,:,::-1])
axes[1].set_title(u"移动后")

仿射变换

仿射变换主要是对图像的缩放,旋转,翻转和平移等操作的组合

一般用于当深度学习数据集不够时,对数据集进行仿射变换,以达到扩充数据集的作用

仿射变换矩阵为2*3的矩阵
M = [ A B ] = [ a 00 a 01 b 0 a 10 a 11 b 1 ] M =\left[\begin {array}{c} A &B\\ \end{array}\right] =\left[\begin {array}{c} a_{00} &a_{01} &b_{0} \\ a_{10} &a_{11} &b_{1} \\ \end{array}\right] M=[AB]=[a00a10a01a11b0b1]
其中左边的2x2子矩阵A是线性变换矩阵,右边2x1子矩阵是平移项
A = [ a 00 a 01 a 10 a 11 ] , B = [ b 0 b 1 ] A =\left[\begin {array}{c} a_{00} &a_{01} \\ a_{10} &a_{11} \\ \end{array}\right], B =\left[\begin {array}{c} b_{0} \\ b_{1} \\ \end{array}\right] A=[a00a10a01a11]B=[b0b1]
对于图像上的任意位置(x,y),仿射变换执行的是如下操作:
T a f f i n e = A [ x y ] + B = M [ x y 1 ] T_{affine} =A\left[\begin {array}{c} x \\ y \\ \end{array}\right]+B =M\left[\begin {array}{c} x \\ y \\ 1 \\ \end{array}\right] Taffine=A[xy]+B=M xy1
在仿射变换中,原图中所有的平行线在结果图像中同样平行,从原图像中找到三个点以及他们在输出图像中的位置,利用cv2.getAfineTransform(pts1, pts2) API创建一个2x3的M矩阵,最后将M矩阵传如cv2.warpAffine

其中:

  • pts1:变换前在图像中找到的三个点的坐标
  • pts2:变换后在图像中找到的三个点的坐标
  • pts形式:pts=([[x1, y1], [x2,y2], [x3, y3]])
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']# 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/color.jpg", 1)
print(img_color.shape)
rows, cols = img_color.shape[:2]

#仿射变换
pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[100,100],[200,50],[100,450]])
M = cv.getAffineTransform(pts1, pts2)
img_color_dst = cv.warpAffine(img_color, M, (rows, cols))

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10,8), dpi=100)
axes[0].imshow(img_color[:,:,::-1])
axes[0].set_title(u"原图")
axes[1].imshow(img_color_dst[:,:,::-1])
axes[1].set_title(u"移动后")

透射变换

将图像投影到一个新的视平面的过程,称为透射变换,通用透射变换公式:
[ x ‘ y ‘ z ‘ ] = [ u v w ] [ a 00 a 01 a 02 a 10 a 11 a 12 a 20 a 21 a 22 ] \left[\begin {array}{c} x^` &y^` &z^`\\ \end{array}\right] =\left[\begin {array}{c} u &v &w\\ \end{array}\right] \left[\begin {array}{c} a_{00} &a_{01} &a_{02} \\ a_{10} &a_{11} &a_{12} \\ a_{20} &a_{21} &a_{22} \\ \end{array}\right] [xyz]=[uvw] a00a10a20a01a11a21a02a12a22
其中(u,v)是原始图像像素坐标,w取值为1,(x=x’/z’, y=y’/z’)是透射变换后的结果

透射变换矩阵一般分为三部分:
T = [ a 00 a 01 a 02 a 10 a 11 a 12 a 20 a 21 a 22 ] = [ T 1 T 2 T 3 a 22 ] T =\left[\begin {array}{c} a_{00} &a_{01} &a_{02} \\ a_{10} &a_{11} &a_{12} \\ a_{20} &a_{21} &a_{22} \\ \end{array}\right] =\left[\begin {array}{c} T1 &T2 \\ T3 &a_{22} \\ \end{array}\right] T= a00a10a20a01a11a21a02a12a22 =[T1T3T2a22]
其中:

  • T1表示对图像进行线性变换
  • T2表示对图像进行平移
  • T3表示对图像进行投射变换
  • a 2 2 a_22 a22一般设置为1

投射变换时需要找四个点,其中3个点不共线即可,通过cv.getPerspectiveTransform(pst1, pst2)得到转换矩阵M,然后将转换矩阵M传入cv.warpPerspective

其中:

  • pts1:变换前在图像中找到的四个点的坐标
  • pts2:变换后在图像中找到的四个点的坐标
  • pts形式:pts=([[x1, y1], [x2,y2], [x3, y3], [x4, y4]])
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']# 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题


img_road = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/road.jpeg", 1)
print(img_road.shape)
rows, cols = img_road.shape[:2]
plt.imshow(img_road[:,:,::-1])

#透射变换
pst1 = np.float32([[80, 600], [990, 600], [400, 450], [720, 450]])
pst2 = np.float32([[200, 650], [800, 650], [200, 300], [800, 300]])
M = cv.getPerspectiveTransform(pst1, pst2)
img_road_dst = cv.warpPerspective(img_road, M, (rows, cols))

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10,8), dpi=100)
axes[0].imshow(img_road[:,:,::-1])
axes[0].set_title(u"原图")
axes[1].imshow(img_road_dst[:,:,::-1])
axes[1].set_title(u"移动后")

注意:

  • 仿射变换需要两个1×3维矩阵,分别表示变换前和变换后点的坐标,每个1×3维矩阵里面嵌套三个1×2维矩阵,分别表示3个点的坐标
  • 透射变换需要两个1×4维矩阵,分别表示变换前和变换后点的坐标,每个1×4维矩阵里面嵌套三个1×2维矩阵,分别表示4个点的坐标

图像金字塔

图像金字塔是图像多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构
一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样
金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似,层级越高,图像越小,分辨率越低

  • cv.pyrUp(img):对图像进行上采样(提高图像分辨率)
  • cv.pyrDown(img):对图像进行下采样(降低图像分辨率)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']# 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/color.jpg", 1)
print(img_color.shape)
rows, cols = img_color.shape[:2]

img_color_Up = cv.pyrUp(img_color)	# 只进行一次上采样过程,一般是将分辨率扩大2倍
img_color_Down = cv.pyrDown(img_color)	# 只进行一次下采样过程,一般是将分辨率缩小1/2倍

plt.imshow(img_color[:,:,::-1])

plt.imshow(img_color_Up[:,:,::-1])

plt.imshow(img_color_Down[:,:,::-1])

一般下采样至图像大小64×64或者任意一条边小于64时停止下采样

边界填充

  • cv.BORDER_REPLICATE:复制法,也就是复制最边缘像素
  • cv.BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制,例如: fedcba<-abcdefgh->hgfedcb
  • cv.BORDER_REFLECT101:反射法,也就是以最边缘像素为轴,对称,例如: gfedcb<-abcdefgh->gfedcba
  • cv.BORDER_WRAP:外包装法,例如: cdefgh<-abcdefgh->abcdefg
  • cv.BORDER_CONSTANT: 常量法,常数值填充,value设置常数值
top_size,bottom_size,left_size,right_size= (50,50,50,50)
replice = cv.copyMakeBorder(img_color, top_size, bottom_size, left_size, right_size, cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(img_color, top_size, bottom_size, left_size, right_size, cv.BORDER_REFLECT)
reflect_101 = cv.copyMakeBorder(img_color, top_size, bottom_size, left_size, right_size, cv.BORDER_REFLECT101)
wrap = cv.copyMakeBorder(img_color, top_size, bottom_size, left_size, right_size, cv.BORDER_WRAP)
constant = cv.copyMakeBorder(img_color, top_size, bottom_size, left_size, right_size, cv.BORDER_CONSTANT, value=0)

2. 形态学

形态学转换是基于图像形状的一些简单操作,通常在二值图上进行

连通性

在图像中,最小的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有3种:4邻接、8邻接和D邻接

在这里插入图片描述

  • 4邻接:像素p(x,y)的4邻域是: (x+1,y);(x-1,y);(x,y+1);(x,y1),用N_4§表示像素p的4邻接
  • D邻接:像素p(x,y)的D邻域是:对角上的点(x+1,y+1);(x+1,y-1);(x-1,y+1);(x-1,y-1),用N_D§表示像素p的D邻域
  • 8邻接:像素p(x,y)的8邻域是: 4邻域的点 + D域的点,用N_8§表示像素p的8邻域

连通性是描述区域和边界的重要概念,两个像素连通的两个必要条件是:

  1. 两个像素的位置是否相邻,即相互在对方的邻域中
  2. 两个像素的灰度值是否满足特定的相似性准则 (或者是否相等)

根据连通性的定义,有4联通、8联通和m联通三种:

  1. 4联通:对于具有值V的像素p和q,如果q在集合N_4§中,则称这两个像素是4连通
  2. 8联通(重要):对于具有值V的像素p和q,如果q在集合N_8§中,则称这两个像素是8连通
  3. m联通
    • 对于具有值V的像素p和q,如果:
    • q集合N_4§中 或 q在集合N_D§中,并且N_4§与N_4(g)的交集为空 (没有值V的像素)
    • 则称这两个像素是m连通的,即4连通和D连通的混合连通

在这里插入图片描述

在这里插入图片描述

腐蚀、膨胀

腐蚀和膨胀为两个基本的形态学运算符,变体形式有开运算、闭运算、礼帽、黑帽等

腐蚀和膨胀都是针对白色部分(高亮部分)而言的

  • 膨胀就是使图像中高亮部分扩张,效果图拥有比原图更大的高亮区域
  • 腐蚀是原图中的高亮区域被蚕食,效果图拥有比原图更小的高亮区域
  • 膨胀是求局部最大值的操作,腐蚀是求局部最小值的操作
腐蚀

具体操作是:用一个结构元素扫描图像中的每一个像素,用结构元素中的每一个像素与其覆盖的像素做“与”操作,如果都为1,则该像素为1,否则为0

如下图所示,结构A被结构B腐蚀后:

在这里插入图片描述

用结构B的中心点去扫描结构A的每一个像素点,每次比较时,用结构B中所有像素值为1的与结构A中对应点做与操作,做完与操作后,如果所有结果都为1,则此次比较结果为1,此时赋值给中心点像素值为1,依次比较所有像素点即可

注意只添加结构B的中心点像素

腐蚀的作用是消除物体边界点,是目标缩小,可以消除小于结构元素的噪声点

cv.erode(img, kernel, iterations)

  • img:要处理的图像
  • kernel:核结构
  • iterations:腐蚀的次数,默认为1
膨胀

具体操作是:用一个结构元素扫描图像中的每一个像素,用结构元素中的每一个像素与其覆盖的像素做“与”操作,如果都为0,则该像素为0,否则为1

如下图所示,结构A被结构B腐蚀后:

在这里插入图片描述

用结构B的中心点去扫描结构A的每一个像素点,每次比较时,用结构B中所有像素值为1的与结构A中对应点做与操作,做完与操作后,如果所有结果不都为0,则此次比较结果为1,此时赋值给中心点像素值为1,依次比较所有像素点即可

注意只添加结构B的中心点像素

膨胀的作用是将与物体接触的所有背景点合并到物体中,是目标增大,可填补目标中的孔洞(4邻域都有像素,中心没有像素->中心填补像素)

cv.dilate(img, kernel, iterations)

  • img:要处理的图像
  • kernel:核结构
  • iterations:腐蚀的次数,默认为1
膨胀和腐蚀
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

#读取图像、图像二值化
img_color = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/color.jpg", 0)
ret2,img_color_mask_OTSU=cv.threshold(img_color,0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

#创建核结构
kernerl = np.ones((5, 5), np.uint8)

#腐蚀与膨胀
img_color_erosion = cv.erode(img_color_mask_OTSU, kernerl, iterations=1)
img_color_dilate = cv.dilate(img_color_mask_OTSU, kernerl, iterations=1)

#灰度显示二值化图像
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10,8), dpi = 100)
axes[0].imshow(img_color_mask_OTSU, cmap='gray')
axes[0].set_title("原图")
axes[1].imshow(img_color_erosion, cmap='gray')
axes[1].set_title("腐蚀后")    # 高亮区变小
axes[2].imshow(img_color_dilate, cmap='gray')
axes[2].set_title("膨胀后")    # 高亮区变大

plt.show()

开运算、闭运算

开运算和闭运算是将腐蚀和膨胀按照一定的次序进行处理。 两者不可逆,即先开后闭并不能得到原来的图像

开运算

开运算是先腐蚀后膨胀

作用是:分离物体,消除小区域

特点:消除噪点,去除小的干扰块,而不影响原来的图像

在这里插入图片描述

kernel使用的是9×9的绿色十字结构

闭运算

闭运算是先膨胀后腐蚀

作用是消除“闭合”物体里面的孔洞

特点:可以填充闭合区域

在这里插入图片描述

kernel使用的是9×9的绿色十字结构

开运算和闭运算

cv.morphologyEx(img,op, kernel)
参数:

  • img:要处理的图像
  • op:处理方式
    • 若进行开运算,则设为cv.MORPH_OPEN
    • 若进行闭运算,则设为cv.MORPH_CLOSE
  • Kernel:核结构
#%%

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

#读取图像
img_num_open = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/img_num_open.png")
img_num_close = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/img_num_close.png")

#kenrnel
kernel = np.ones((10, 10), np.uint8)

img_num_open_open = cv.morphologyEx(img_num_open, cv.MORPH_OPEN, kernel)
img_num_close_close = cv.morphologyEx(img_num_close, cv.MORPH_CLOSE, kernel)

fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10,8), dpi = 100)
axes[0,0].imshow(img_num_open)
axes[0,0].set_title("img_num_open原图")
axes[0,1].imshow(img_num_open_open)
axes[0,1].set_title("img_num_open开运算后")	# 消除了噪点

axes[1,0].imshow(img_num_close)
axes[1,0].set_title("img_num_close原图")
axes[1,1].imshow(img_num_close_close)
axes[1,1].set_title("img_num_close_close闭运算后")	# 填补了孔洞

在这里插入图片描述

礼帽、黑帽

礼帽运算

原图像与“开运算“的结果图之差,dst = tophat(src, element) = src - open(src, element)

因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中 减去 开运算后的图 得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关

礼帽运算作用:分离比邻近点亮一些的斑块

黑帽运算

原图像与“闭运算“的结果图之差,dst = blackhat(src, element)= close(src, element)- src

因为闭运算带来的结果是填充了闭合区域中的孔洞,因此,从闭运算后的图 减去 原图 得到的效果图突出了原图中闭合区域的孔洞,突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关

黑帽运算作用:分离比邻近点暗一些的斑块

礼帽运算和黑帽运算

cv.morphologyEX(img, op, kernel)

  • img:要处理的图像,注意是原图像不是开运算或闭运算后的图像

  • op:处理方式

    • 参数功能
      CUMORPH_CLOSE闭运算
      CVMORPH_OPEN开运算
      CMORPH_TOPHAT礼帽运算
      CVMORPH_BLACKHAT黑帽运
  • kernel:核结构

#%%

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

#读取图像
img_num_open = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/img_num_open.png")
img_num_close = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/img_num_close.png")

#kenrnel
kernel = np.ones((9, 9), np.uint8)

img_num_open_open = cv.morphologyEx(img_num_open, cv.MORPH_OPEN, kernel)
img_num_close_close = cv.morphologyEx(img_num_close, cv.MORPH_CLOSE, kernel)
img_num_topHat = cv.morphologyEx(img_num_open, cv.MORPH_TOPHAT, kernel)
img_num_blackHat = cv.morphologyEx(img_num_close, cv.MORPH_BLACKHAT, kernel)

fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(10,8), dpi = 100)
axes[0,0].imshow(img_num_open)
axes[0,0].set_title("img_num_open原图")
axes[0,1].imshow(img_num_open_open)
axes[0,1].set_title("img_num_open开运算后")
axes[0,2].imshow(img_num_topHat)
axes[0,2].set_title("礼帽运算后")

axes[1,0].imshow(img_num_close)
axes[1,0].set_title("img_num_close原图")
axes[1,1].imshow(img_num_close_close)
axes[1,2].set_title("img_num_close_close闭运算后")
axes[1,2].imshow(img_num_blackHat)
axes[1,2].set_title("黑帽运算后")

在这里插入图片描述

3. 梯度运算

图像梯度代表图像灰度值变化的速度,对于一副图像而言,其边缘部分两侧灰度值相差较大,梯度值大,所以对一副图像求梯度可以突出图像边缘的信息。由于像素值为离散值,所以其梯度其是就是差分运算,其本质上也就是一种空间滤波。

#%%

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

#读取图像
img_circle = cv.imread("G:/Pycharm_workspace/OpenCV_Study/image/circle.jpg")
kernel = np.ones((3,3), np.uint8)
img_circle_dilate = cv.dilate(img_circle, kernel, iterations=5)
img_circle_erosion = cv.erode(img_circle, kernel, iterations=5)

# 组合膨胀后图像和腐蚀后图像
res = np.hstack((img_circle_dilate, img_circle_erosion))
# 梯度运算 = 膨胀 - 腐蚀 默认iterations=1
img_circle_gradient = cv.morphologyEx(img_circle, cv.MORPH_GRADIENT, kernel)

fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(10,10), dpi = 100)
axes[0,0].imshow(img_circle)
axes[0,0].set_title("img_circle")
axes[0,1].imshow(img_circle)
axes[0,1].set_title("img_circle")
axes[1,0].imshow(img_circle_dilate)
axes[1,0].set_title("img_circle_dilate")
axes[1,1].imshow(img_circle_erosion)
axes[1,1].set_title("img_circle_erosion")
axes[2,0].imshow(res)
axes[2,0].set_title("res")
axes[2,1].imshow(img_circle_gradient)
axes[2,1].set_title("img_circle_gradient")

resule = cv.subtract(img_circle_dilate, img_circle_erosion)	# cv图像相减方法
plt.imshow(resule)
plt.title("subtract")

在这里插入图片描述

在这里插入图片描述

Sobel算子

G x = A ∗ [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] a n d G y = A ∗ [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] G_x =A* \left[\begin {array}{c} -1 &0 &+1\\ -2 &0 &+2 \\ -1 &0 &+1 \\ \end{array}\right] \quad and\quad G_y =A* \left[\begin {array}{c} -1 &-2 &-1\\ 0 &0 &0 \\ +1 &+2 &+1 \\ \end{array}\right] Gx=A 121000+1+2+1 andGy=A 10+120+210+1

注意:这里的*是卷积运算, 默认步长为1,不是矩阵乘法

卷积运算

dst = cv2.Sobel(src, ddepth, dx, dy, ksize)

  • ddepth:图像的深度,默认值-1
  • dx和dy:分别表示水平和竖直方向,取值为0或1,表示是沿x方向还是y方向
  • ksize:Sobel算子的大小,一般为3*3大小,即设置为ksize=3
# sobel算子
# convertScaleAbs取绝对值,当白到黑是正数,黑到白则为负数(为防止负数截断为0,此时需要去绝对值)
# 水平方向求梯度时,是原图的右边减左边
# 竖直方向求梯度时,是原图的下边减上边

img_circle_sobel_x = cv.Sobel(img_circle, cv.CV_64F, 1, 0, ksize=3)
img_circle_sobel_x_abs = cv.convertScaleAbs(img_circle_sobel_x)
img_circle_sobel_y = cv.Sobel(img_circle, cv.CV_64F, 0, 1, ksize=3)
img_circle_sobel_y_abs = cv.convertScaleAbs(img_circle_sobel_y)
# 先分别求x方向和y方向的梯度,然后进行求和
img_circle_sobel_xy_ABS = cv.addWeighted(img_circle_sobel_x_abs, 0.5, img_circle_sobel_y_abs, 0.5, 0)
# 直接求x方向和y方向的梯度和
img_circle_sobel_xy_abs = cv.Sobel(img_circle, cv.CV_64F, 1, 1, ksize=3)

"""
    建议:
        先分别求x方向和y方向的梯度,然后进行求和,比,直接求x方向和y方向的梯度和,的效果要好
"""

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(10,20), dpi = 100)
axes[0,0].imshow(img_circle)
axes[0,0].set_title("img_circle")
axes[0,1].imshow(img_circle)
axes[0,1].set_title("img_circle")
axes[1,0].imshow(img_circle_sobel_x)
axes[1,0].set_title("img_circle_sobel_x")
axes[1,1].imshow(img_circle_sobel_y)
axes[1,1].set_title("img_circle_sobel_y")
axes[2,0].imshow(img_circle_sobel_x_abs)
axes[2,0].set_title("img_circle_sobel_x_abs")
axes[2,1].imshow(img_circle_sobel_y_abs)
axes[2,1].set_title("img_circle_sobel_y_abs")
axes[3,0].imshow(img_circle_sobel_xy_ABS)
axes[3,0].set_title("img_circle_sobel_xy_ABS")
axes[3,1].imshow(img_circle_sobel_xy_abs)
axes[3,1].set_title("img_circle_sobel_xy_abs")

在这里插入图片描述

Scharr算子

G x = A ∗ [ − 3 0 + 3 − 10 0 + 10 − 3 0 + 3 ] a n d G y = A ∗ [ − 3 − 10 − 3 0 0 0 + 3 + 10 + 3 ] G_x =A* \left[\begin {array}{c} -3 &0 &+3\\ -10 &0 &+10 \\ -3 &0 &+3 \\ \end{array}\right] \quad and\quad G_y =A* \left[\begin {array}{c} -3 &-10 &-3\\ 0 &0 &0 \\ +3 &+10 &+3 \\ \end{array}\right] Gx=A 3103000+3+10+3 andGy=A 30+3100+1030+3

相较于Sobel算子对梯度变化更明显

cv.Scharr(src, ddepth, dx, dy)

  • ddepth:图像的深度,默认值-1
  • dx和dy:分别表示水平和竖直方向,取值为0或1,表示是沿x方向还是y方向

Laplacian算子

G = A ∗ [ 0 − 1 0 1 − 4 1 0 1 0 ] G =A* \left[\begin {array}{c} 0 &-1 &0\\ 1 &-4 &1 \\ 0 &1 &0 \\ \end{array}\right] G=A 010141010

相当于二阶导,对梯度变化更明显,对噪音点更敏感

cv.Laplacian(src, ddepth)

  • ddepth:图像的深度,默认值-1

三个算子差距

在这里插入图片描述

4. 图像噪声

常见的图像噪声有高斯噪声、椒盐噪声等

椒盐噪声

椒盐噪声(也称为脉冲噪声),是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)

椒盐噪声的成因可能是影像讯号受到突如其来的强烈干扰而产生、类比数位转换器或位元传输错误等。例如失效的感应器导致像素值为最小值,饱和的感应器导致像素值为最大值

在这里插入图片描述

高斯噪声

高斯噪声(也称为正态噪声),是指噪声密度函数服从高斯分布的一类噪声,在空间和频域中数学上的易处理性。

高斯随机变量z的概率密度函数:
p ( z ) = 1 2 π σ e − ( z − μ ) 2 σ 2 p(z)=\frac{1}{\sqrt{2π}σ}e{\frac{-(z-μ)}{2σ^2}} p(z)=2π σ1e2σ2(zμ)

  • z:表示灰度值
  • μ:表示z的平均值或期望值
  • σ:表示z的标准差
  • σ^2:表示z的方差

在这里插入图片描述

在这里插入图片描述

5. 平滑处理

均值滤波

cv.blur(img, (x,y))

  • (x,y):设置一个x*y的矩阵,对img进行卷积操作,对值求均值,然后存入

方框滤波

cv.boxFilter(img, -1, (x,y), normalize=True)

  • (x,y):设置一个x*y的矩阵,对img进行卷积操作
  • normalize:
    • 值为True时进行均值操作,和均值滤波作用一致
    • 值为False时不进行均值操作,此时卷积操作后的值为多少就存入多少,如果大于255,则存入255即可

高斯滤波

高斯滤波器是一种线性滤波器,能够有效的抑制噪声,平滑图像。其作用原理和均值滤波器类似,都是取滤波器窗口内的像素的均值作为输出。但其窗口模板的系数和均值滤波器不同,均值滤波器的模板系数都是相同的为1,而高斯滤波器的模板系数则随着距离模板中心的增大而减小。所以,高斯滤波器相比于均值滤波器对图像个模糊程度较小

cv.GaussianBlur(img, (x,y), sigma)

  • (x,y):设置一个x*y的矩阵,设置设置中间的卷积核值为1,设置周围卷积核值为相较于中间值的比例,然后对图像进行卷积操作,最后存入值
  • sigma:标准差

中值滤波

cv.medianBlur(img, ksize)

  • ksize:卷积核大小,找到图像在卷积矩阵的中间值,当做最终值存入

综合

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_people_nosie = cv.imread("../../image/img_people_salt_noise.jpg")

# 均值滤波
# 简单的平均卷积操作
blur = cv.blur(img_people_nosie, (5,5))

# 方框滤波
# 基本和均值一样,可以选择归一化
box_True = cv.boxFilter(img_people_nosie, -1, (5,5), normalize=True)
box_False = cv.boxFilter(img_people_nosie, -1, (5,5), normalize=False)

# 高斯滤波
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间
guassian = cv.GaussianBlur(img_people_nosie, (5,5), 0.1)

# 中值滤波
# 相当于用中值代替
median = cv.medianBlur(img_people_nosie, 5)
plt.imshow(median[:,:,::-1])

fig, axes = plt.subplots(nrows=1, ncols=5, figsize=(10,20), dpi=100)
axes[0].imshow(img_people_nosie[:,:,::-1])
axes[0].set_title("img_people_nosie")
axes[1].imshow(blur[:,:,::-1])
axes[1].set_title("blur")
axes[2].imshow(box_True[:,:,::-1])
axes[2].set_title("box_True")
axes[3].imshow(guassian[:,:,::-1])
axes[3].set_title("guassian")
axes[4].imshow(median[:,:,::-1])
axes[4].set_title("median")

在这里插入图片描述

6. 图像阈值(二值化)

图像的阈值处理又称为二值化,将一幅图转换为感兴趣的部分(前景)和不感兴趣的部分(背景),二值化可以剔除掉图像中一些低于或高于一定值(即阈值)的像素,从而提取图像中的物体,通常将超过阈值的像素作为前景。阈值又称为临界值,它的目的是确定出一个范围,然后这个范围内的像素点使用同一种方法处理,而阈值之外的部分则使用另一种处理方法或保持原样

ret, dst = cv2.threshold(src, thresh, maxval, type)

  • ret:获得设置的阈值值
  • dst:输出图
  • src:输入图,只能输入单通道图像,通常来说为灰度图
  • thresh:阈值
  • maxval:当像素值超过了闻值(或者小于值,根据type来决定),所赋予的值
  • type:二化操作的类型,包含以下5类型
    • CV2.THRESH_BINARY:大于阈值的部分取值为maxval,小于阈值的部分取值为0
    • CV2.THRESH_BINARY_INVCV2.THRESH_BINARY的反转
    • CV2.THRESH_TRUNC:大于阈值的部分取值为thresh,小于阈值的部分取值不变
    • CV2.THRESH_TOZERO:大于阈值的部分取值为不变,小于阈值的部分取值为thresh
    • CV2.THRESH_TOZERO_INVCV2.THRESH_TOZERO的反转
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_sketch = cv.imread("../../image/img_sketch.jpg")

ret, thresh1 = cv.threshold(img_sketch, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img_sketch, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(img_sketch, 127, 255, cv.THRESH_TRUNC)
ret, thresh4 = cv.threshold(img_sketch, 127, 255, cv.THRESH_TOZERO)
ret, thresh5 = cv.threshold(img_sketch, 127, 255, cv.THRESH_TOZERO_INV)

fig, axes = plt.subplots(nrows=1, ncols=6, figsize=(10,20), dpi = 100)
axes[0].imshow(img_sketch)
axes[0].set_title("img_sketch")
axes[1].imshow(thresh1)
axes[1].set_title("thresh1")
axes[2].imshow(thresh2)
axes[2].set_title("thresh2")
axes[3].imshow(thresh3)
axes[3].set_title("thresh3")
axes[4].imshow(thresh4)
axes[4].set_title("thresh4")
axes[5].imshow(thresh5)
axes[5].set_title("thresh5")

在这里插入图片描述

7. Canny边缘检测

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

高斯滤波器

e = H ∗ A = [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] ∗ [ a b c d e f g h i ] = s u m ( [ a ∗ h 11 b ∗ h 12 c ∗ h 13 d ∗ h 21 e ∗ h 22 f ∗ h 23 g ∗ h 31 h ∗ h 32 i ∗ h 33 ] ) e =H*A =\left[\begin {array}{c} h_{11} &h_{12} &h_{13}\\ h_{21} &h_{22} &h_{23}\\ h_{31} &h_{32} &h_{33}\\ \end{array}\right] * \left[\begin {array}{c} a &b &c\\ d &e &f\\ g &h &i\\ \end{array}\right] =sum( \left[\begin {array}{c} a*h_{11} &b*h_{12} &c*h_{13}\\ d*h_{21} &e*h_{22} &f*h_{23}\\ g*h_{31} &h*h_{32} &i*h_{33}\\ \end{array}\right] ) e=HA= h11h21h31h12h22h32h13h23h33 adgbehcfi =sum( ah11dh21gh31bh12eh22hh32ch13fh23ih33 )

梯度和方向

G = G x 2 + G y 2 θ = a r c t a n ( G y / G x ) G = \sqrt{G^2_x+G^2_y} \quad \quad θ=arctan(G_y/G_x) \quad \quad G=Gx2+Gy2 θ=arctan(Gy/Gx)

S x = [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] S y = [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] S_x= \left[\begin {array}{c} -1 &0 &+1\\ -2 &0 &+2 \\ -1 &0 &+1 \\ \end{array}\right] \quad \quad S_y= \left[\begin {array}{c} -1 &-2 &-1\\ 0 &0 &0 \\ +1 &+2 &+1 \\ \end{array}\right] Sx= 121000+1+2+1 Sy= 10+120+210+1

G x = S x ∗ A = [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] ∗ [ a b c d e f g h i ] = s u m ( [ − a 0 c − 2 d 0 2 f − g 0 i ] ) G_x =S_x*A =\left[\begin {array}{c} -1 &0 &+1\\ -2 &0 &+2 \\ -1 &0 &+1 \\ \end{array}\right] * \left[\begin {array}{c} a &b &c\\ d &e &f\\ g &h &i\\ \end{array}\right] =sum( \left[\begin {array}{c} -a &0 &c\\ -2d&0 &2f\\ -g &0 &i\\ \end{array}\right] ) Gx=SxA= 121000+1+2+1 adgbehcfi =sum( a2dg000c2fi )

G y = S y ∗ A = [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] ∗ [ a b c d e f g h i ] = s u m ( [ a 2 b c 0 0 0 − g − 2 h − i ] ) G_y =S_y*A =\left[\begin {array}{c} -1 &-2 &-1\\ 0 &0 &0 \\ +1 &+2 &+1 \\ \end{array}\right] * \left[\begin {array}{c} a &b &c\\ d &e &f\\ g &h &i\\ \end{array}\right] =sum( \left[\begin {array}{c} a&2b &c\\ 0&0 &0\\ -g &-2h&-i\\ \end{array}\right] ) Gy=SyA= 10+120+210+1 adgbehcfi =sum( a0g2b02hc0i )

非极大值抑制

线性插值法:设g1的梯度幅值M(g1),g2的梯度幅值M(g2),则dtmp1可以很得到:M(dtmp1)=w * M(g2) + (1-w) * M(g1)
其中w=distance(dtmp1,g1)/distance(g1,g2),distance(g1,g2) 表示两点之间的距离,如果c比dtmp1、dtmp2的值都大,则c是极大值保留下来

在这里插入图片描述

在这里插入图片描述
上图中,在已知梯度方向为CAB时,求离A最近的两个像素点的值,如果A比B、C的值都大,则判定A为极大值,即为边界

双阈值检测

在这里插入图片描述

  • 梯度值>maxVal:则处理为边界
  • minVal<梯度值<maxVal:连有边界则保留,否则舍弃
  • 梯度值<minVal:则舍弃

综合

cv.Canny(img, minVal, maxVal)

  • minVal,maxVal:梯度值的最小和最大值
    • 在minVal相同的情况下,maxVal越大,显示的边界信息更少
    • 在maxVal相同的情况下,minVal越小,显示的边界信息更多
    • 一般设置minVal值比较小,maxVal值比较小,此时显示的边界信息比较多
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_sketch = cv.imread("../../image/img_people.jpg")

img_sketch_canny1 = cv.Canny(img_sketch, 80, 150)
img_sketch_canny2 = cv.Canny(img_sketch, 50, 100)

fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(10,20), dpi=100)
axes[0].imshow(img_sketch[:,:,::-1])
axes[0].set_title("img_sketch")
axes[1].imshow(img_sketch_canny1)
axes[1].set_title("img_sketch_canny1")
axes[2].imshow(img_sketch_canny2)
axes[2].set_title("img_sketch_canny2")

在这里插入图片描述

8. 图像轮廓

获取轮廓信息

contours, hierarchy = cv2.findcontours(img,mode,method)

  • mode:轮廓检索模式
    • RETR_EXTERNAL:只检索最外面的轮廓
    • RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中
    • RETR_CCOMP:检索所有的轮廓,并将他们组织为两层: 顶层是各部分的外部边界,第二层是空洞的边界
    • RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次,一般推荐只用这种模式
  • method:轮廓逼近方法
    • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)
    • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分
  • contours:返回的list表,保存轮廓信息
  • hierarchy:返回的是层级结构

利用获取到的轮廓信息画轮廓

cv.drawContours(img, contours, x, (0,0,255), pix)

  • img:在哪张图上画轮廓
  • contours:获取到的轮廓信息
  • x:
    • -1:默认,表示画出全部轮廓
    • 0~…:表示画第几个轮廓
  • (0,0,255):(B,G,R)表示用那个颜色画轮廓
  • pix:线条像素
  • 注意:这种方法是在原始图像的基础上画轮廓,输出画好轮廓的图像,并且原始图像也会画上轮廓,所以一般先将原始图像copy,然后在copy的图像基础上画轮廓,此时就不会对原始图像进行修改

步骤:

  1. 读取图像
  2. 转化图像->灰度图->二值图
  3. 将二值图传入cv.findContours方法,获取contours(轮廓信息)和hierarchy(层级信息)两个值
  4. 利用``copy`方法复制原始图像
  5. 将copy图像传入cv.drawContours方法,在copy的图像上画轮廓
#%%
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_contours = cv.imread("../../image/img_contours.jpg")
img_contours_gray = cv.cvtColor(img_contours, cv.COLOR_BGR2GRAY)
ret, img_contours_gray_thresh = cv.threshold(img_contours_gray,127, 255, cv.THRESH_BINARY)
plt.imshow(img_contours_gray_thresh, cmap="gray")

contours, hierarchy = cv.findContours(img_contours_gray_thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

img_contours_copy1 = img_contours.copy()
img_contours_gray_thresh_contours_res1 = cv.drawContours(img_contours_copy1, contours, -1, (0,0,255), 2)

img_contours_copy2 = img_contours.copy()
img_contours_gray_thresh_contours_res2 = cv.drawContours(img_contours_copy2, contours, 0, (0,0,255), 2)


fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8,8), dpi=100)
axes[0,0].imshow(img_contours[:,:,::-1])
axes[0,0].set_title("img_contours")
axes[0,1].imshow(img_contours[:,:,::-1])
axes[0,1].set_title("img_contours")
axes[1,0].imshow(img_contours_gray_thresh_contours_res1[:,:,::-1])
axes[1,0].set_title("img_contours_gray_thresh_contours_res1")
axes[1,1].imshow(img_contours_gray_thresh_contours_res2[:,:,::-1])
axes[1,1].set_title("img_contours_gray_thresh_contours_res2")

在这里插入图片描述

轮廓特征

步骤:

  1. 在获取的contours中取响应的轮廓cont = contours[x]
  2. 利用cv.contourArea(cont)获取轮廓面积
  3. 利用cv.arcLength(cont, True)获取轮廓周长,True表示闭合
  4. epslion = x*cv.arcLength(contours_xx[0], True)
    1. x值越小,越近似轮廓,不能大于1,
  5. approx = cv.approxPolyDP(contours_xx[0], epslion, True)
  6. 复制原图像
  7. cv.drawContours(img_copy, [approx], -1, (0,0,255), 2)
# 轮廓近似
img_circle = cv.imread("../../image/circle.jpg")
img_circle_gray = cv.cvtColor(img_circle, cv.COLOR_BGR2GRAY)
ret, img_circle_gray_thresh = cv.threshold(img_circle_gray, 125, 255, cv.THRESH_BINARY)

contours_xx, hierarchy_xx = cv.findContours(img_circle_gray_thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

img_circle_copy = img_circle.copy()
res = cv.drawContours(img_circle_copy, contours_xx[0], -1, (0,0,255), 2)

epslion = 0.1*cv.arcLength(contours_xx[0], True)
approx = cv.approxPolyDP(contours_xx[0], epslion, True)
img_circle_copy1 = img_circle.copy()
res1 = cv.drawContours(img_circle_copy1, [approx], -1, (0,0,255), 2)

epslion = 0.005*cv.arcLength(contours_xx[0], True)
approx = cv.approxPolyDP(contours_xx[0], epslion, True)
img_circle_copy2 = img_circle.copy()
res2 = cv.drawContours(img_circle_copy2, [approx], -1, (0,0,255), 2)

fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8,8), dpi=100)
axes[0,0].imshow(res[:,:,::-1])
axes[0,0].set_title("res")
axes[0,1].imshow(res[:,:,::-1])
axes[0,1].set_title("res")
axes[1,0].imshow(res1[:,:,::-1])
axes[1,0].set_title("res1")
axes[1,1].imshow(res2[:,:,::-1])
axes[1,1].set_title("res2")

在这里插入图片描述

9. 模板匹配

模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在penc里有6种,然后将每次计算的结果放入一个短阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结里的短阵是(A-a+1)x(B-b+1)

min_val, max_val, min_loc, max_loc = cv.matchTemplate(img, img_template, penc)

  • penc:
    • TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
    • TM_CCORR:计算相关性,计算出来的值越大,越相关
    • TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
    • TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
    • TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
    • TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
  • min_val、min_loc:像素差的最小值,以及最小值的位置
  • max_val、max_loc:像素差的最大值,以及最大值的位置
  • 一般位置指的是矩形框的左上角,然后再利用h, w = template.shape[:2]进行切片,获得模板的长和宽,然后画矩形框即可
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np

def set_ch():
    from pylab import mpl
    mpl.rcParams['font.sans-serif'] =['FangSong']   # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号-显示为方块的问题
set_ch()
# set_ch()解决matplotlib图像显示乱码或方块问题

img_sketch = cv.imread("../../image/img_sketch.jpg")
img_sketch_part = cv.imread("../../image/img_sketch_part.jpg")
h, w = img_sketch_part.shape[:2]
methods = ['cv.TM_CCOEFF','cv.TM_CCOEFF_NORMED',
           'cv.TM_CCORR','cv.TM_CCORR_NORMED',
           'cv.TM_SQDIFF','cv.TM_SQDIFF_NORMED']
for meth in methods:
    img_sketch_copy1 = img_sketch.copy()
    method = eval(meth)
    res = cv.matchTemplate(img_sketch, img_sketch_part, method)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
    # 如果是平方差匹配TM_SQDIFF或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
    if method in ['TM_SQDIFF','TM_SQDIFF_NORMED']:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    cv.rectangle(img_sketch_copy1, top_left, bottom_right, (0,0,255), 2)
    fig, axes = plt.subplots(nrows=1, ncols=4, figsize=(10,20), dpi=100)
    axes[0].imshow(img_sketch)
    axes[0].set_title("原图")
    axes[1].imshow(img_sketch_part)
    axes[1].set_title("截取部分")
    axes[2].imshow(res, cmap="gray")
    axes[2].set_title("res")
    axes[3].imshow(img_sketch_copy1[:,:,::-1])
    axes[3].set_title(meth)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

# 匹配多个对象
img_game = cv.imread("../../image/img_game.jpg")
img_game_gray = cv.cvtColor(img_game, cv.COLOR_BGR2GRAY)

img_game_template = cv.imread("../../image/img_game_part.jpg")
img_game_template_gray = cv.cvtColor(img_game_template, cv.COLOR_BGR2GRAY)
h, w = img_game_template.shape[:2]
res = cv.matchTemplate(img_game_gray, img_game_template_gray, cv.TM_CCOEFF_NORMED)
threshold = 0.8
# 取匹配程度大于80%的坐标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
    bottom_right1 = (pt[0]+w, pt[1]+h)
    cv.rectangle(img_game, pt, bottom_right1, (0,0,255), 1)
plt.imshow(img_game[:,:,::-1])

在这里插入图片描述

5. 识别项目

步骤:

  1. 读取图像
  2. 灰度化
    1. 方便以后的二值化、礼帽、黑帽、梯度、开运算、闭运算使用
  3. 二值化
    1. 作用是:剔除掉图像中一些低于或高于一定值(即阈值)的像素,从而提取图像中的物体
    2. ret, dst= cv2.threshold(src, thresh, maxval, type)
      • dst:二值化后返回的图像,如果没有设置,则默认返回src
      • thresh:最低阈值
      • maxval:最高阈值
      • type:二化操作的类型,包含以下5类型
        • CV2.THRESH_BINARY:大于阈值的部分取值为maxval,小于阈值的部分取值为0
        • CV2.THRESH_BINARY_INVCV2.THRESH_BINARY的反转
        • CV2.THRESH_TRUNC:大于阈值的部分取值为thresh,小于阈值的部分取值不变
        • CV2.THRESH_TOZERO:大于阈值的部分取值为不变,小于阈值的部分取值为thresh
        • CV2.THRESH_TOZERO_INVCV2.THRESH_TOZERO的反转
  4. 形态学操作
    • cv.morphologyEX(img, op, kernel)
      1. 膨胀
        • 作用:填补图像中的孔洞
        • op:cv.MORPH_DILATE
      2. 腐蚀
        • 作用:消除物体边界点,是目标缩小,可以消除小于结构元素的噪声点
        • op:cv.MORPH_ERODE
      3. 开运算
        • 作用:先腐蚀后膨胀,分离物体,消除小区域,消除噪点,去除小的干扰块,而不影响原来的图像
        • op:cv.MORPH_OPEN
      4. 闭运算
        • 作用:先膨胀后腐蚀,消除“闭合”物体里面的孔洞
        • op:cv.MORPH_CLOSE
      5. 礼帽运算
        • 作用:原图像与“开运算“的结果图之差,显示高亮部分,突出了比原图轮廓周围的区域更明亮的区域
        • op:cv.MORPH_TOPHAT
      6. 黑帽运算
        • 作用:原图像与“闭运算“的结果图之差,显示孔洞部分,突出了比原图轮廓周围的区域更暗的区域
        • op:cv.MORPH_BLACKHAT
      7. 梯度运算
        • 作用:膨胀减去腐蚀后的效果,代表图像灰度值变化的速度,对于一副图像而言,其边缘部分两侧灰度值相差较大,梯度值大,对一副图像求梯度可以突出图像边缘的信息
        • op:cv.MORPH_GRADIENT
        • 一般使用cv.Sobel(src, ddepth, dx, dy, ksize)而不使用cv.morphologyEX(img, op, kernel)
          • ddepth一般设置为-1
          • dx、dy:表示求x方向或y方向的梯度
          • ksize:卷积核的大小一般设置为3
          • 一般使用Sobel算子先求x、y方向上的梯度,然后通过convertScaleAbs方法分对两梯度求绝对值,然后通过cv.addWeighted(gradX, 0.5, gradY, 0.5, 0)求总的梯度
      8. 平滑操作
        • 必要时需要平滑操作,模糊图像,减少噪声
        • 高斯平滑:高斯滤波器是一种线性滤波器,能够有效的抑制噪声,平滑图像
        • cv.GaussianBlur(img, (x,y), sigma)
          • (x,y):设置一个x*y的矩阵,设置设置中间的卷积核值为1,设置周围卷积核值为相较于中间值的比例,然后对图像进行卷积操作,最后存入值
          • sigma:标准差
  5. 轮廓运算
    1. 找轮廓
      • contours, hierarchy = cv2.findcontours(img,mode,method)
        • contours:表示图像的轮廓信息
        • mode:轮廓检索模式
          • RETR_EXTERNAL:只检索最外面的轮廓
          • RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中
          • RETR_CCOMP:检索所有的轮廓,并将他们组织为两层: 顶层是各部分的外部边界,第二层是空洞的边界
          • RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次,一般推荐用这种模式
        • method:轮廓逼近方法
          • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)
          • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分
    2. 画轮廓
      • cv.drawContours(img, contours, x, (0,0,255), pix)
        • x:
          • -1:默认,表示画出全部轮廓
          • 0~…:表示画第几个轮廓
    3. 注意:轮廓计算中,必须先通过img.copy()将原图像复制,在两个方法中传入的是复制后的图像不是原图像(为了防止直接修改原图像)
  • 作用:先腐蚀后膨胀,分离物体,消除小区域,消除噪点,去除小的干扰块,而不影响原来的图像
    - op:cv.MORPH_OPEN
    4. 闭运算
    - 作用:先膨胀后腐蚀,消除“闭合”物体里面的孔洞
    - op:cv.MORPH_CLOSE
    5. 礼帽运算
    - 作用:原图像与“开运算“的结果图之差,显示高亮部分,突出了比原图轮廓周围的区域更明亮的区域
    - op:cv.MORPH_TOPHAT
    6. 黑帽运算
    - 作用:原图像与“闭运算“的结果图之差,显示孔洞部分,突出了比原图轮廓周围的区域更暗的区域
    - op:cv.MORPH_BLACKHAT
    7. 梯度运算
    - 作用:膨胀减去腐蚀后的效果,代表图像灰度值变化的速度,对于一副图像而言,其边缘部分两侧灰度值相差较大,梯度值大,对一副图像求梯度可以突出图像边缘的信息
    - op:cv.MORPH_GRADIENT
    - 一般使用cv.Sobel(src, ddepth, dx, dy, ksize)而不使用cv.morphologyEX(img, op, kernel)
    - ddepth一般设置为-1
    - dx、dy:表示求x方向或y方向的梯度
    - ksize:卷积核的大小一般设置为3
    - 一般使用Sobel算子先求x、y方向上的梯度,然后通过convertScaleAbs方法分对两梯度求绝对值,然后通过cv.addWeighted(gradX, 0.5, gradY, 0.5, 0)求总的梯度
    8. 平滑操作
    - 必要时需要平滑操作,模糊图像,减少噪声
    - 高斯平滑:高斯滤波器是一种线性滤波器,能够有效的抑制噪声,平滑图像
    - cv.GaussianBlur(img, (x,y), sigma)
    - (x,y):设置一个x*y的矩阵,设置设置中间的卷积核值为1,设置周围卷积核值为相较于中间值的比例,然后对图像进行卷积操作,最后存入值
    - sigma:标准差
  1. 轮廓运算
    1. 找轮廓
      • contours, hierarchy = cv2.findcontours(img,mode,method)
        • contours:表示图像的轮廓信息
        • mode:轮廓检索模式
          • RETR_EXTERNAL:只检索最外面的轮廓
          • RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中
          • RETR_CCOMP:检索所有的轮廓,并将他们组织为两层: 顶层是各部分的外部边界,第二层是空洞的边界
          • RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次,一般推荐用这种模式
        • method:轮廓逼近方法
          • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)
          • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分
    2. 画轮廓
      • cv.drawContours(img, contours, x, (0,0,255), pix)
        • x:
          • -1:默认,表示画出全部轮廓
          • 0~…:表示画第几个轮廓
    3. 注意:轮廓计算中,必须先通过img.copy()将原图像复制,在两个方法中传入的是复制后的图像不是原图像(为了防止直接修改原图像)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,OpenCV是一个开源计算机视觉和机器学习软件库,用于开发图像和视频处理应用程序。通过使用OpenCV,您可以读取摄像头并显示实时图像,打开视频文件或摄像头文件,并获取视频的相关信息,例如帧宽度、帧高度、帧率和总帧数。 对于学习OpenCV,你可以按照以下步骤进行: 1. 安装OpenCV库:在开始学习OpenCV之前,您需要从OpenCV官方网站下载和安装OpenCV库。根据您的操作系统和编程语言选择合适的版本。 2. 学习基本概念:熟悉OpenCV的基本概念和术语,例如图像和视频的加载、显示、保存以及常用的图像处理操作,如滤波、边缘检测和特征提取等。 3. 掌握OpenCV函数和类:深入了解OpenCV提供的函数和类,例如cv::Mat用于图像和矩阵操作,cv::VideoCapture用于读取和处理视频,以及cv::imshow和cv::waitKey等用于显示图像的函数。 4. 实践项目:通过完成一些实践项目来应用您所学到的知识。例如,利用OpenCV实现人脸检测、目标追踪、图像识别等。 5. 学习资料和资源:查找和阅读OpenCV的官方文档、教程和示例代码,参与开源社区讨论和交流,加入相关的论坛和邮件列表等。 总结起来,学习OpenCV包括安装OpenCV库、学习基本概念、掌握OpenCV函数和类、实践项目以及查找和阅读相关资料和资源。通过不断实践和学习,您将能够更好地理解和应用OpenCV库来开发图像和视频处理应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值