• Python 使用OpenCV计算机视觉(一篇文章从零毕业)【附带停车场车位智能识别项目】预计7月初更新完毕


    OpenCV计算机视觉

    附带:《停车场车位智能识别》项目

    1、参考文档

    官网

    下载地址

    图像与滤波-阮一峰

    LENA.JS-将滤波器拖到图像上,产生过滤后的效果

    浏览器实现滤波的GitHub范例代码

    2、环境详情

    • MacOS-10.14.6

    • Python3.9

    • numpy-1.22.4

      • NumPy(Numerical Python)是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多(该结构也可以用来表示矩阵(matrix)),支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。
    • matplotlib-3.5.2

      • Matplotlib是一个 Python 的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。
    • opencv-python-3.4.11.45

      • opencv是用于快速处理图像处理、计算机视觉问题的工具,支持多种语言进行开发如c++、python、java等。
    • opencv-contrib-python-3.4.11.45

      • opencv-contrib-python包含了主要模块以及扩展模块,扩展模块主要是包含了一些带专利的收费算法(如shift特征检测)以及一些在测试的新的算法(稳定后会合并到主要模块)。相当于加了一些额外的扩展,比如特征提取的一些算法,这些是OpenCV中没有的。

    3、安装

    安装opencv-python

    pip3 install opencv-python
    
    • 1

    报错install pyproject.toml-based projects

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vrOsBkIc-1656167586384)(../../pic/image-20220623151209970.png)]

    • 访问opencv的镜像文件的网站,下载whl文件安装

    https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/opencv-python/

    • 找到适合自己的whl文件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q70AcUCX-1656167586385)(../../pic/image-20220623151342574.png)]

    ​ 我是MacOS10.14.6,Python3.9所以直接用下面链接下载即可:

    https://mirrors.tuna.tsinghua.edu.cn/pypi/web/packages/1f/e0/187bf6941ad68e1c12d4e11d473d0841257aa388e3e88f1541f1f0c9a5dd/opencv_python-3.4.11.45-cp39-cp39-macosx_10_13_x86_64.whl#sha256=87015ea21e3f2faa7923cc3505e671b5e99b791fc812630f5d5ca4474387b242

    • 下载完成后,进入opencv_python-3.4.11.45-cp39-cp39-macosx_10_13_x86_64.whl文件所在目录执行安装
    pip3 install opencv_python-3.4.11.45-cp39-cp39-macosx_10_13_x86_64.whl
    
    • 1

    Successfully installed numpy-1.22.4 opencv-python-3.4.11.45

    • 验证是否可以在Python中使用OpenCV
    import cv2
    
    • 1

    安装opencv-contrib-python

    注意需要和opencv-python版本号相同!

    pip3 install opencv-contrib-python==3.4.11.45
    
    • 1

    Successfully installed opencv-contrib-python-3.4.11.45

    到此为止,OpenCV和opencv-contrib都装好了。

    4、图像基本操作

    ​ 计算机是由每一个小格构成像素点来组成图像的。

    ​ 像素点就是一个值,是在0~255之间的值,共计256个值,表示该点的亮度,0代表黑的,255代表最亮的。

    ​ RGB是三元色,叫图像的颜色通道。

    ​ 黑白图也叫灰度图,只有一个通道,来表示亮度就足够了。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7trRXkJ-1656167586385)(../../pic/image-20220623154112687.png)]

    读取图像

    这里用到的cat.jpg 是h=414,w=500,分辨率=72

    # import matplotlib需要先安装,执行下面命令
    pip3 install matplotlib
    
    • 1
    • 2

    Successfully installed cycler-0.11.0 fonttools-4.33.3 kiwisolver-1.4.3 matplotlib-3.5.2 packaging-21.3 pillow-9.1.1 pyparsing-3.0.9

    • 读取成彩色图像:cv2.imread('cat.jpg', cv2.IMREAD_COLOR)
    • 读取成灰度图像:cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
    import cv2  # opencv默认读取的格式是BGR,不是RGB
    
    img = cv2.imread('cat.jpg')  # 读取彩色图像
    # print(type(img))  # <class 'numpy.ndarray'>
    print(img)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    [[[142 151 160] # 从左上角开始,第一行,B G R
      [146 155 164]
      [151 160 170]
      ...
      [156 172 185]
      [155 171 184]
      [154 170 183]]
    
     [[108 117 126] # 第二行
      [112 123 131]
      [118 127 137]
      ...
      [155 171 184]
      [154 170 183]
      [153 169 182]]
    
     ...
    
     [[140 164 176] # 第n-1行
      [147 171 183]
      [139 163 175]
      ...
      [169 187 188]
      [125 143 144]
      [106 124 125]]
    
     [[154 178 190] # 第n行
      [154 178 190]
      [121 145 157]
      ...
      [183 198 200]
      [128 143 145]
      [127 142 144]]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    # 读取灰度图
    img = cv2.imread("cat.jpg", cv2.IMREAD_GRAYSCALE)
    print(img)
    
    • 1
    • 2
    • 3
    [[153 157 162 ... 174 173 172]
     [119 124 129 ... 173 172 171]
     [120 124 130 ... 172 171 170]
     ...
     [187 182 167 ... 202 191 170]
     [165 172 164 ... 185 141 122]
     [179 179 146 ... 197 142 141]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    图像展示,cv2.imshow()

    # 图像的显示,也可以创建多个窗口
    cv2.imshow('image', img) # image是窗口的名字
    # 等待时间,毫秒级(1000ms=1s),0表示任意键终止
    cv2.waitKey(1000)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    获取图像hwc三个属性

    # 获取图像的hwc三个属性,h=height,w=width,c=3是RGB
    print(img.shape)  # (414, 500, 3)
    
    • 1
    • 2

    图像保存,cv2.imwrite()

    cv2.imwrite("mycat.png", img)
    
    • 1

    计算像素点个数

    print(img.size) # 207000
    
    • 1

    数据类型

    print(img.dtype) # uint8
    
    • 1

    截取部分图像数据

    # ROI(region of interest,感兴趣区域)
    cat = img[0:50, 0:200]  # 从左上角:截取h=50,w=200像素点的区域
    cv2.imshow('image', cat)
    
    • 1
    • 2
    • 3

    颜色通道提取,cv2.split()

    b, g, r = cv2.split(img)
    print(b)  # 打印每一个像素点中的blue
    # print(g)
    # print(r)
    # print(b.shape)  # (414, 500)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    [[142 146 151 ... 156 155 154] # 从左上角开始,第一行所有像素点中全部的Blue值 
     [108 112 118 ... 155 154 153] # 第二行
     ...
     [140 147 139 ... 169 125 106] # 第n-1行
     [154 154 121 ... 183 128 127]] # 第n行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    # 只保留R
    cur_img = img.copy()
    cur_img[:,:,0]=0
    cur_img[:,:,1]=0
    
    • 1
    • 2
    • 3
    • 4

    颜色通道组合,cv2.merge()

    # 接上提取操作后,把b、g、r组合成一张图片
    img = cv2.merge((b, g, r))  # 这里的结果img还是原图
    
    • 1
    • 2

    拷贝图像

    cur_img = img.copy()
    
    • 1

    只保留单通道颜色

    # 只保留R
    cur_img = img.copy()
    # []里是hwc三个属性,:代表取所有,h=height,w=width,c=0是B c=1是G c=2是R
    cur_img[:, :, 0] = 0  # 设置B为0
    cur_img[:, :, 1] = 0  # 设置G为0
    
    cv2.imshow('image', cur_img)  # image是窗口的名字
    # 等待时间,毫秒级(1000ms=1s),0表示任意键终止
    cv2.waitKey(1000)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    # 只保留G
    cur_img = img.copy()
    cur_img[:, :, 0] = 0  # 设置B为0
    cur_img[:, :, 2] = 0  # 设置R为0
    
    • 1
    • 2
    • 3
    • 4
    # 只保留B
    cur_img = img.copy()
    cur_img[:, :, 1] = 0  # 设置G为0
    cur_img[:, :, 2] = 0  # 设置R为0
    
    • 1
    • 2
    • 3
    • 4

    边界填充,cv2.copyMakeBorder()

    在这里插入图片描述

    • ORIGINAL:原图
    • BORDER_REPLICATE:复制法,也就是复制最边缘像素。
    • BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制,边界重复,例如:fedcba|abcdefgh|hgfedcb
    • BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,边界不重复,gfedcb|abcdefgh|gfedcba
    • BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
    • BORDER_CONSTANT:常量法,常数值填充。
    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)
    
    cv2.imshow('image', constant)  # image是窗口的名字
    # 等待时间,毫秒级(1000ms=1s),0表示任意键终止
    cv2.waitKey(10000)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    每一个像素点加10

    print(img)
    img2 = img + 10
    print(img2)
    
    • 1
    • 2
    • 3
    array([[142, 146, 151, ..., 156, 155, 154],
           [107, 112, 117, ..., 155, 154, 153],
           [108, 112, 118, ..., 154, 153, 152],
           [139, 143, 148, ..., 156, 155, 154],
           [153, 158, 163, ..., 160, 159, 158]], dtype=uint8)
    # 加10以后
    array([[152, 156, 161, ..., 166, 165, 164],
           [117, 122, 127, ..., 165, 164, 163],
           [118, 122, 128, ..., 164, 163, 162],
           [149, 153, 158, ..., 166, 165, 164],
           [163, 168, 173, ..., 170, 169, 168]], dtype=uint8)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    两个相同hw的图相加

    如果相加超过255,则需要减去256,因为是0~255,还有个0,所以要减256

    print(img + img2)
    
    • 1
    array([[ 38,  46,  56, ...,  66,  64,  62],
           [224, 234, 244, ...,  64,  62,  60],
           [226, 234, 246, ...,  62,  60,  58],
           [ 32,  40,  50, ...,  66,  64,  62],
           [ 60,  70,  80, ...,  74,  72,  70]], dtype=uint8)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    cv2.add()

    和直接+运算不同的是,如果相加超过255,就取255

    print(cv2.add(img, img2))
    
    • 1
    array([[255, 255, 255, ..., 255, 255, 255],
           [224, 234, 244, ..., 255, 255, 255],
           [226, 234, 246, ..., 255, 255, 255],
           [255, 255, 255, ..., 255, 255, 255],
           [255, 255, 255, ..., 255, 255, 255]], dtype=uint8)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    图像拉伸,重新设置hw,cv2.resize()

    print(img.shape)  # (414, 500, 3)
    # 设置shape值,相当于是图像拉伸操作。
    img_cat = cv2.resize(img, (1000, 814))
    print(img_cat.shape)  # (814,1000,3)
    
    • 1
    • 2
    • 3
    • 4
    # 以倍数拉伸
    print(img.shape)  # (414, 500, 3)
    res = cv2.resize(img, (0, 0), fx=0.4, fy=2) # x轴即w拉伸0.4倍,y轴即h拉伸2倍
    print(res.shape)  # (828, 200, 3)
    
    • 1
    • 2
    • 3
    • 4

    图像融合,cv2.addWeighted()

    把两个图像叠加成一张图像。

    注意:如果两张图片的shape值是不同的,是不可以直接用+运算的。

    图像融合公式: R = α x 1 + β x 2 + b R=\alpha x_1+\beta x_2+b R=αx1+βx2+b

    α \alpha α x 1 x_1 x1的权重

    β \beta β x 2 x_2 x2的权重

    b b b:偏置项

    x 1 x_1 x1:图像1

    x 2 x_2 x2:图像2

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPbrbSjS-1656167586387)(../../pic/image-20220624131552081.png)]

    print(img_cat + img_dog)
    
    • 1
    ValueError: operands could not be broadcast together with shapes (414,500,3) (429,499,3)
    
    • 1
    # 第一步:调整两张图片shape值为相等。
    print(img_cat.shape)  # (414,500,3)
    print(img_dog.shape)  # (429,499,3)
    # 设置shape值,相当于是图像拉伸操作。
    img_dog = cv2.resize(img_dog, (500, 414))
    print(img_dog.shape)  # (414,500,3)
    
    # 第二步:根据融合公式进行融合
    res = cv2.addWeighted(img_cat, 0.3, img_dog, 0.7, 0)
    
    cv2.imshow('image', res)  # image是窗口的名字
    # 等待时间,毫秒级(1000ms=1s),0表示任意键终止
    cv2.waitKey(10000)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5、视频基本操作

    ​ 视频是由很多帧组成的,每一帧都可以当作是静止的图像,把很多张静止的图像连在一起就形成视频。

    ​ 帧(frame);视频都是由一帧一帧组成的,每一帧其实是一个包含有音频视频信息的基本单位。

    ​ 每秒传输的图片帧数,也可以理解为图形处理器每秒刷新的次数,通常以fps(Frames Per Second)表示。

    读取视频,cv2.VideoCapture()

    • cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1

    • 如果是视频文件,直接指定好路径即可。

    import cv2  # opencv默认读取的格式是BGR,不是RGB
    
    # cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1
    # 如果是视频文件,直接指定好路径即可。
    vc = cv2.VideoCapture("test.mp4")
    
    # 检查是否打开正确
    if vc.isOpened():
        """
            is_open: bool,True/False
            frame: 当前这一帧图像的像素点值,<class 'numpy.ndarray'>
            read(): 读取视频的每一帧,可以写循环就读取所有帧了
        """
        is_open, frame = vc.read()
    else:
        is_open = False
       
    # 读取每一帧实现读取视频的效果
    while is_open:
        ret, frame = vc.read()
        if frame is None:
            break
        if ret is True:
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # 转化成灰度图
            cv2.imshow('result', gray)  # 图像展示
            """cv2.waitKey(1)只能是integer,1代表PC能处理多快就多快,即处理完一帧要等少毫秒,所以视频是加速播放的。
               27为ESC键,我们可以按ESC退出
               13为ENTER键
               9为TAB
               20为Caps Lock键
            """
            if cv2.waitKey(1) & 0xFF == 27:
                break
    vc.release()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    6、图像阈值

    ​ 首先,我们要求图像为灰度图,像素点值越接近255该点越亮。

    ​ 其次,图像是由像素点组成,每一个像素点都是灰度的值,对每一个值进行判断,如果该值大于阈值,我们要怎么处理,小于要怎么处理。

    # 灰度图的每一个像素点的list
    [[153 157 162 ... 174 173 172]
     [119 124 129 ... 173 172 171]
     [120 124 130 ... 172 171 170]
     ...
     [187 182 167 ... 202 191 170]
     [165 172 164 ... 185 141 122]
     [179 179 146 ... 197 142 141]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    阈值处理函数,cv2.threshold()

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

    • src: 输入图,只能输入单通道图像,通常来说为灰度图

    • dst: 输出图

    • thresh: 阈值,不是百分比,是在0~255之前确定的值,比较常见的是127

    • maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值,最大的值也就是255

    • type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV

      • cv2.THRESH_BINARY
        • 超过阈值部分取maxval(最大值),否则取0
      • cv2.THRESH_BINARY_INV
        • THRESH_BINARY的反转
      • cv2.THRESH_TRUNC
        • 大于阈值部分设为阈值,否则不变。相当于指定一个截断值。
      • cv2.THRESH_TOZERO
        • 大于阈值部分不改变,否则设为0
      • cv2.THRESH_TOZERO_INV
        • THRESH_TOZERO的反转
    • ret:你设置的thresh阈值的值,一般来说是用不到该返回变量的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3aPzFIWC-1656167586387)(../../pic/image-20220624135910538.png)]

    import cv2  # opencv默认读取的格式是BGR,不是RGB
    import matplotlib.pyplot as plt
    
    img = cv2.imread("cat.jpg", cv2.IMREAD_GRAYSCALE)
    
    # BINARY,亮点的全为白,暗点的全为黑
    ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
    print(ret)  # 127.0
    # BINARY_INV,是BINARY的反转,即:亮点的全为黑,暗点的全为白
    ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
    # TRUNC,大于阈值部分设为阈值,否则不变,相当于指定一个截断值
    ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
    # TOZERO,大于阈值则不变,小于阈值全为黑
    ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
    # TOZERO_INV,是TOZERO的反转,大于阈值全为黑,小于阈值则不变
    ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
    
    titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
    images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
    
    for i in range(6):
        plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    7、图像平滑

    四种滤波:

    • 均值滤波:cv2.blur()
    • 方框滤波: cv2.boxFilter()
    • 高斯滤波:cv2.GaussianBlur()
    • 中值滤波:cv2.medianBlur()

    图像与滤波

    图像其实是一种波,可以用波的算法处理图像

    ​ 我们知道,图像由像素组成。下图是一张 400 x 400 的图片,一共包含了 16 万个像素点。

    ​ 每个像素的颜色,可以用红、绿、蓝、透明度四个值描述,大小范围都是0 ~ 255,比如黑色是[0, 0, 0, 255],白色是[255, 255, 255, 255]。通过 Canvas API 就可以拿到这些值。

    ​ 如果把每一行所有像素(上例是400个)的红、绿、蓝的值,依次画成三条曲线,就得到了下面的图形。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2dGBpLG-1656167586387)(../../pic/bg2017121302.png)]

    ​ 可以看到,每条曲线都在不停的上下波动。有些区域的波动比较小,有些区域突然出现了大幅波动(比如 54 和 324 这两点)。

    ​ 对比一下图像就能发现,曲线波动较大的地方,也是图像出现突变的地方。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vl2j15Vw-1656167586388)(../../pic/bg2017121303.png)]

    ​ 这说明波动与图像是紧密关联的。图像本质上就是各种色彩波的叠加。


    两种常见的滤波器:

    • 低通滤波器(lowpass):减弱或阻隔高频信号,保留低频信号
    • 高通滤波器(highpass):减弱或阻隔低频信号,保留高频信号

    lowpass使得图像的高频区域变成低频,即色彩变化剧烈的区域变得平滑,也就是出现模糊效果。

    在这里插入图片描述

    highpass正好相反,过滤了低频,只保留那些变化最快速最剧烈的区域,也就是图像里面的物体边缘,所以常用于边缘识别。

    在这里插入图片描述


    ​ 图像平滑:即对图像进行各种滤波操作。

    ​ 下图即lenaNoise.png图片的原图,可以看到图片有一些白色的噪点。

    ​ 现在我们想通过滤波或者是平滑处理操作,来尽可能去掉这些噪点。。

    假设上图的像素点的矩阵为下图所示:

    均值滤波

    ​ 下述代码中的(3, 3)代表着上图黄色的区域(称为“核”,核一般是奇数,每3x3要进行一次计算),即处理值为204这个像素点的值,应该是和其周围像素点的值是比较相关的。所以,要使用该区域的9个像素点的均值来作为204这个像素点处理完后的值。

    ​ 即: 121 + 75 + 78 + 24 + 204 + 113 + 154 + 104 + 235 9 \frac{121+75+78+24+204+113+154+104+235}{9} 9121+75+78+24+204+113+154+104+235

    import cv2
    
    img = cv2.imread("lenaNoise.png")
    # 均值滤波
    # 简单的平均卷积操作
    blur = cv2.blur(img, (3, 3))
    
    cv2.imshow('blur', blur)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方框滤波

    ​ 和均值滤波一样,可以把boxFilter()和blur()当成一个,只不过boxFilter()多了一个参数。

    import cv2
    
    img = cv2.imread("lenaNoise.png")
    # 方框滤波
    # -1不用管是固定的,normalize=True代表做归一化操作(除9),此时和均值滤波一模一样
    box = cv2.boxFilter(img,-1,(3,3), normalize=True)  
    
    cv2.imshow('box', box)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    import cv2
    
    img = cv2.imread("lenaNoise.png")
    # 方框滤波
    # normalize=False不做归一化操作(不除9),容易越界(值超过255则取255当作结果)
    box = cv2.boxFilter(img,-1,(3,3), normalize=False)  
    
    cv2.imshow('box', box)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    高斯滤波

    ​ 高斯函数的意思就是越接近均值的时候,可能性越大。越接近x=0的时候y的值越大,即下图所示。

    ​ 所以,要处理204这个像素点的值,那么离204点越近的点,可能性越大,即75、24、113、104点可能性更大,121、78、154、235点可能性小。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wUFFdir8-1656167586389)(../../pic/src=http%253A%252F%252Fimg-blog.csdnimg.cn%252Fimg_convert%252F55fcef410beaed0f56cc8f22790d9c0f.png&refer=http%253A%252F%252Fimg-blog.csdnimg.jpeg)]

    import cv2
    
    img = cv2.imread("lenaNoise.png")
    # 高斯滤波
    # 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
    aussian = cv2.GaussianBlur(img, (5, 5), 1)  
    
    cv2.imshow('aussian', aussian)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    中值滤波

    ​ 中值就是中间的值,所以需要从小到大排序,取中间的值。

    ​ 即:24、75、78、104、113、121、154、204、235

    ​ 所以,值为204的像素点经过中值滤波处理后的值就是113了。

    ​ 所以,最后经过中值滤波处理的图像就没有噪点了。

    import cv2
    
    img = cv2.imread("lenaNoise.png")
    # 中值滤波
    # 相当于用中值代替
    median = cv2.medianBlur(img, 5)
    
    cv2.imshow('median', median)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    展示所有结果

    import cv2
    import numpy as np
    
    img = cv2.imread("lenaNoise.png")
    # 均值滤波
    blur = cv2.blur(img, (3, 3))
    # 方框滤波
    box1 = cv2.boxFilter(img, -1, (3, 3), normalize=True)
    box2 = cv2.boxFilter(img, -1, (3, 3), normalize=False)
    # 高斯滤波
    aussian = cv2.GaussianBlur(img, (5, 5), 1)
    # 中值滤波
    median = cv2.medianBlur(img, 5)
    
    # 展示所有的
    # hstack是横向拼接展示;vstack是纵向拼接展示
    res = np.hstack((blur, box1, box2, aussian, median))
    # print(res)
    cv2.imshow('median vs average', res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    8、形态学morphology

    腐蚀操作,cv2.erode()

    ​ 前提条件:图像一般都是2值的数据图像。

    ​ 现在想把下图文字上的毛刺去掉。

    ​ 缺点就是图像有价值的信息越来越少,即下图所示经过腐蚀操作后白色线条变细了。

    ​ 场景:当我们的图像中有一些细线条的时候,我们可以用腐蚀操作去掉,再用膨胀操作复原。

    在这里插入图片描述

    import cv2
    import numpy as np
    
    img = cv2.imread('dige.png')
    
    # 设置3x3的核心
    kernel = np.ones((3, 3), np.uint8)
    # iterations迭代次数,次数越多,白色字条越细,甚至消失。
    erosion = cv2.erode(img, kernel, iterations=1)
    
    res = np.hstack((img, erosion))
    cv2.imshow('res', res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    膨胀操作,cv2.dilate()

    可以用来弥补腐蚀操作后图像中线条变细的情况。

    在这里插入图片描述

    import cv2
    import numpy as np
    
    img = cv2.imread('dige.png')
    # 腐蚀操作
    kernel = np.ones((3, 3), np.uint8)
    erosion = cv2.erode(img, kernel, iterations=1)
    # 膨胀操作
    kernel = np.ones((3, 3), np.uint8)
    dilate = cv2.dilate(erosion, kernel, iterations=1)
    
    res = np.hstack((img, erosion, dilate))
    cv2.imshow('res', res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    开运算与闭运算

    ​ 其实就是把腐蚀和膨胀总结成了一个方法cv2.morphologyEx()

    import cv2
    import numpy as np
    
    # 开:先腐蚀,再膨胀
    img = cv2.imread('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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    import cv2
    import numpy as np
    
    # 闭:先膨胀,再腐蚀
    img = cv2.imread('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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    梯度运算

    梯度=膨胀-腐蚀

    import cv2
    import numpy as np
    
    # 梯度=膨胀-腐蚀
    pie = cv2.imread('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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    import cv2
    import numpy as np
    
    # 梯度运算
    pie = cv2.imread('pie.png')
    kernel = np.ones((7, 7), np.uint8)
    gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)
    
    cv2.imshow('gradient', gradient)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    礼帽与黑帽

    • 礼帽 = 原始输入-开运算结果

    • 黑帽 = 闭运算-原始输入

    import cv2
    import numpy as np
    
    img = cv2.imread('dige.png')
    kernel = np.ones((7, 7), np.uint8)
    # 礼帽
    tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
    cv2.imshow('tophat', tophat)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    img = cv2.imread('dige.png')
    kernel = np.ones((7, 7), np.uint8)
    # 黑帽
    blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
    cv2.imshow('blackhat', blackhat)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    9、图像梯度

    ​ 梯度=膨胀-腐蚀

    ​ 梯度就是边界点。下图红线部分是没有梯度的,因为红线的左右两边都是白色的,红线部分也是白色的。

    ​ 我们要做的就是把那些像素点有梯度给找出来,这样的事。相当于是做边缘检测。

    Sobel算子

    算法:右-左,下-上

    ​ 在像素点层面执行找梯度操作,即像素点左右、上下颜色是不同的。

    在这里插入图片描述

    方法参数:

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

    • src:当前图像

    • ddepth:图像的深度,通常情况指定为-1,代表输出深度和输出深度是相同的。

    • dx、dy:分别表示水平和竖直方向

    • ksize:Sobel算子的大小,一般情况是3x3或5x5即写3或5就行。

    import cv2
    
    img = cv2.imread('pie.png')
    """
        opencv的像素点的值是0~255的。
        但是opencv默认会把两个像素点相减后得到的负值截断成0,但是正常情况咱们左右或者上下相减就是会有负值的情况。
        cv2.CV_64F代表opencv支持负数的结果。
        dx=1,dy=0代表现在算的是水平梯度,不算竖直梯度
    """
    # Gx计算
    sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
    # 白到黑是整数,黑到白就是负数了,所有的负数会被截断成0,我们算的是左右或者上下的差异,不关心值的正负,所以要取绝对值。
    sobel_x = cv2.convertScaleAbs(sobel_x)
    
    # Gy计算
    sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
    sobel_y = cv2.convertScaleAbs(sobel_y)
    
    # 图像融合,把Gx和Gy两张图融合在一起。
    sobel_xy = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)
    
    cv2.imshow("image", sobel_xy)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    # 也可以一起算Gx和Gy,但是不建议,效果不好
    import cv2
    
    img = cv2.imread('pie.png')
    # Gx、Gy同时计算
    sobel_xy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)
    sobel_xy = cv2.convertScaleAbs(sobel_xy)
    
    cv2.imshow("image", sobel_xy)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    换个图片效果更明显:
    在这里插入图片描述

    Scharr算子

    ​ Scharr算子的计算方式和Sobel算子是一样的,只不过核的数值是有差异的。Scharr算子对结果的差异更敏感。

    算法:右-左,下-上

    在这里插入图片描述

    方法示例

    import cv2
    
    img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
    scharr_x = cv2.Scharr(img, cv2.CV_64F, 1, 0)
    scharr_y = cv2.Scharr(img, cv2.CV_64F, 0, 1)
    scharr_x = cv2.convertScaleAbs(scharr_x)
    scharr_y = cv2.convertScaleAbs(scharr_y)
    scharr_xy = cv2.addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0)
    
    cv2.imshow("image", scharr_xy)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    laplacian算子

    ​ laplacian算子是二阶导,二阶导相当于是一阶导的变化率。laplacian算子对变化更敏感;

    ​ 缺点就是对一些噪音点也比较敏感,这不是好事,因为噪音点不是边界。

    ​ 通常情况下,laplacian算子要和其他方法一同使用,我们一般不会单独使用。

    算法

    在这里插入图片描述

    参数示例

    import cv2
    
    img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
    laplacian = cv2.Laplacian(img, cv2.CV_64F)
    laplacian = cv2.convertScaleAbs(laplacian)
    cv2.imshow("image", laplacian)
    
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    10、Canny边缘检测

    Canny进行边缘检测的流程:

    1. 使用高斯滤波器,以平滑图像,滤除噪声。
    2. 计算图像中每个像素点的梯度强度和方向。
    3. 应用 非极大值抑制(Non-Maximum Suppression),以消除边缘检测带来的杂散响应。
      • 非极大值抑制:把极值小的给抑制掉,只保留极值大的,相当于把最明显的体现出来。
    4. 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
    5. 通过抑制孤立的弱边缘最终完成边缘检测。

    1. 高斯滤波器

    在这里插入图片描述
    ###2. 梯度和方向
    在这里插入图片描述

    3. 非极大值抑制

    在这里插入图片描述

    4. 双阈值检测

    在这里插入图片描述

    import cv2
    import numpy as np
    
    img = cv2.imread("lena.jpg", cv2.IMREAD_GRAYSCALE)
    
    # 80是minVal,150是maxVal
    v1 = cv2.Canny(img, 80, 150)
    # minVal和maxVal越小,条件越松,边界越多
    v2 = cv2.Canny(img, 50, 100)
    
    res = np.hstack((v1, v2))
    
    cv2.imshow("image", res)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    v1=cv2.Canny(img,120,250)
    v2=cv2.Canny(img,50,100)
    
    • 1
    • 2

    在这里插入图片描述

    11、图像金字塔

    • 高斯金字塔
    • 拉普拉斯金字塔

    把图像组合成金字塔形状(即每层图像的大小不同);

    用法就是比如在做图像的特征提取时,我们不光要对原始图像做特征提取,还要对金字塔不同层的图片做特征提取。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OY5TM9n-1656316595639)(../../pic/Pyramid_1.png)]

    高斯金字塔

    向下采样方法(缩小)

    即从金字塔底向金字塔尖采样。

    L0是800x800像素点的,那么L1就是4x4像素点的,以此类推。

    在这里插入图片描述

    向上采样方法(放大)

    即从金字塔尖向金字塔底采样。

    L4是2x2像素点,变换后就是4x4的像素点,以此类推。

    在这里插入图片描述

    import cv2
    
    img = cv2.imread("AM.png")
    print(img.shape)  # (442, 340, 3)
    # 向上采样
    up = cv2.pyrUp(img)
    print(up.shape)  # (884, 680, 3)
    # 向下采样
    down = cv2.pyrDown(img)
    print(down.shape)  # (221, 170, 3)
    
    cv2.imshow("image", up)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    注意:比如原图先执行上采样,再执行下采样,虽然最后的图像和原图shape一样,但是色彩和清晰度会降低。因为上采样会增加一些0,下采样会损失一些信息。
    
    • 1

    在这里插入图片描述

    拉普拉斯金字塔

    在这里插入图片描述

    import cv2
    import numpy as np
    
    img = cv2.imread("AM.png")
    # print(img.shape)  # (442, 340, 3)
    down = cv2.pyrDown(img)
    down_up = cv2.pyrUp(down)
    l_1 = img - down_up
    # print(l_1.shape)  # (442, 340, 3)
    res = np.hstack((img, l_1))
    
    cv2.imshow("image", res)
    cv2.waitKey()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

  • 相关阅读:
    用C语言实现ping命令
    ++ Reference: Standard C++ Library reference: C Library: cmath: lround
    Pytorch学习笔记 (参考官方教程)
    Web大学生网页作业成品 bootstrap响应式网站开发 基于HTML+CSS+JS+Bootstrap制作火锅美食网站(4页)
    UML的组成
    精彩分享 | 欢乐游戏 Istio 云原生服务网格三年实践思考
    洛谷 P2812 校园网络(强连通分量, 缩点)
    【LangChain系列 9】Prompt模版——MessagePromptTemplate
    【树形权限】树形列表权限互斥选择、el-tree设置禁用等等
    数据分析常见的英文缩写(一)
  • 原文地址:https://blog.csdn.net/JENREY/article/details/125464987