• 【OpenCV】Chapter1.图像的基本操作


    最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。
    【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487
    程序仓库:https://github.com/zstar1003/OpenCV-Learning

    图像的读写与显示

    读取图像

    retval = cv.imread(filename[, flags])

    参数说明:

    • filename:读取图像的文件路径和文件名
    • flags:读取图片的方式,可选项
      • cv2.IMREAD_COLOR(1):始终将图像转换为 3 通道BGR彩色图像,默认方式
      • cv2.IMREAD_GRAYSCALE(0):始终将图像转换为单通道灰度图像
      • cv2.IMREAD_UNCHANGED(-1):按原样返回加载的图像(使用Alpha通道)
      • cv2.IMREAD_ANYDEPTH(2):在输入具有相应深度时返回16位/ 32位图像,否则将其转换为8位
      • cv2.IMREAD_ANYCOLOR(4):以任何可能的颜色格式读取图像
    • 返回值 retval:读取的 OpenCV 图像,nparray 多维数组

    保存图像

    retval = cv2.imwrite(filename, img [, paras])

    参数说明:

    • filename:要保存的文件的路径和名称,包括文件扩展名
    • img:要保存的 OpenCV 图像,nparray 多维数组
    • paras:不同编码格式的参数,可选项
      • cv2.CV_IMWRITE_JPEG_QUALITY:设置 .jpeg/.jpg 格式的图片质量,取值为 0-100(默认值 95),数值越大则图片质量越高;
      • cv2.CV_IMWRITE_WEBP_QUALITY:设置 .webp 格式的图片质量,取值为 0-100;
      • cv2.CV_IMWRITE_PNG_COMPRESSION:设置 .png 格式图片的压缩比,取值为 0-9(默认值 3),数值越大则压缩比越大。
    • retval:返回值,保存成功返回 True,否则返回 False。

    显示图像

    None = imshow(winname, img)

    参数说明:

    • winname:字符串,显示窗口的名称。
    • img:所显示的 OpenCV 图像,nparray 多维数组。

    扩展使用:
    可以通过cv2.namedWindowcv2.resizeWindow来指定窗口显示尺寸。

    下面看个示例:

    """
    读取、保存、显示图像
    """
    import cv2
    import numpy as np
    import urllib.request as request
    
    # 读取本地图像
    imgFile = "img/img.jpg"  # 读取文件的路径
    img1 = cv2.imread(imgFile, flags=1)  # flags=1 读取彩色图像(BGR)
    img2 = cv2.imread(imgFile, flags=0)  # flags=0 读取为灰度图像
    # 读取网络图像
    response = request.urlopen("https://img-blog.csdnimg.cn/8992e07a6539448b94b06936c21b4a67.jpeg")
    imgUrl = cv2.imdecode(np.array(bytearray(response.read()), dtype=np.uint8), -1)
    # 显示图像
    cv2.namedWindow("img1", cv2.WINDOW_NORMAL)
    cv2.resizeWindow("img1", 600, 600)
    cv2.imshow("img1", img1)  # img1为窗口标题
    cv2.namedWindow("img2", cv2.WINDOW_NORMAL)
    cv2.resizeWindow("img2", 600, 600)
    cv2.imshow("img2", img2)
    cv2.imshow("img3", imgUrl)
    key = cv2.waitKey(0)  # 等待按键命令, 1000ms 后自动关闭
    # 保存图像
    saveFile = "img/imgSave.png"  # 保存文件的路径
    cv2.imwrite(saveFile, img2)  # 保存图像文件
    # cv2.imwrite(saveFile, img3, [int(cv2.IMWRITE_PNG_COMPRESSION), 8])  # 保存图像文件, 设置压缩比为 8
    
    • 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

    运行效果:
    在这里插入图片描述

    用matplotlib显示图像(BGRtoRGB)

    值得注意的是OpenCV加载的彩色图片使用的是BGR格式,而 matplotlib 使用 RGB 格式,因此使用matplotlib显示图像时,先要对颜色空间进行转换。
    同时需注意matplotlib显示灰度图像时,需要设置cmap参数。

    下面是示例程序:

    """
    用 matplotlib 显示图像
    """
    import cv2
    import matplotlib.pyplot as plt
    
    imgFile = "img/img.jpg"  # 读取文件的路径
    img1 = cv2.imread(imgFile, flags=1)  # flags=1 读取彩色图像(BGR)
    
    imgRGB = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)  # 图片格式转换:BGR(OpenCV) -> RGB(PyQt5)
    img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)  # 图片格式转换:BGR(OpenCV) -> Gray
    
    plt.rcParams['font.sans-serif'] = ['FangSong']  # 支持中文标签
    plt.subplot(221), plt.title("1. RGB 格式(mpl)"), plt.axis('off')
    plt.imshow(imgRGB)  # matplotlib 显示彩色图像(RGB格式)
    plt.subplot(222), plt.title("2. BGR 格式(OpenCV)"), plt.axis('off')
    plt.imshow(img1)  # matplotlib 显示彩色图像(BGR格式)
    plt.subplot(223), plt.title("3. 设置 Gray 参数"), plt.axis('off')
    plt.imshow(img2, cmap='gray')  # matplotlib 显示灰度图像,设置 Gray 参数
    plt.subplot(224), plt.title("4. 未设置 Gray 参数"), plt.axis('off')
    plt.imshow(img2)  # matplotlib 显示灰度图像,未设置 Gray 参数
    plt.savefig("result.png")
    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

    运行效果:
    在这里插入图片描述

    图像的属性

    图像的属性主要有以下四种:

    • img.ndim:查看图像的维数,彩色图像的维数为 3,灰度图像的维数为 2。
    • img.shape:查看图像的形状,即图像栅格的行数(高度)、列数(宽度)、通道数。
    • img.size:查看图像数组元素总数,灰度图像的数组元素总数为像素数量,彩色图像的数组元素总数为像素数量与通道数的乘积。
    • img1.dtype:图像编码类型

    示例程序:

    """
    查看图像属性
    """
    import cv2
    
    imgFile = "img/img.jpg"  # 读取文件的路径
    img1 = cv2.imread(imgFile, flags=1)  # flags=1 读取彩色图像(BGR)
    img2 = cv2.imread(imgFile, flags=0)  # flags=0 读取为灰度图像
    
    # 维数(ndim), 形状(shape), 元素总数(size), 元素类型(dtype)
    print("Ndim of img1(BGR): {}, img2(Gray): {}".format(img1.ndim, img2.ndim))  # number of rows, columns and channels
    print("Shape of img1(BGR): {}, img2(Gray): {}".format(img1.shape, img2.shape))  # number of rows, columns and channels
    print("Size of img1(BGR): {}, img2(Gray): {}".format(img1.size, img2.size))  # size = rows * columns * channels
    print("Dtype of img1(BGR): {}, img2(Gray): {}".format(img1.dtype, img2.dtype))  # uint8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出:

    Ndim of img1(BGR): 3, img2(Gray): 2
    Shape of img1(BGR): (954, 980, 3), img2(Gray): (954, 980)
    Size of img1(BGR): 2804760, img2(Gray): 934920
    Dtype of img1(BGR): uint8, img2(Gray): uint8
    
    • 1
    • 2
    • 3
    • 4

    图像裁剪

    图像的本质是一个array,因此可以直接使用数组切片的方式对图像进行裁剪。
    另外,OpenCV还提供了一个cv2.selectROI函数,可以通过鼠标选择感兴趣的矩形区域(ROI)。

    示例程序:

    """
    裁剪图像
    """
    import cv2
    
    img1 = cv2.imread("img/img.jpg")  # flags=1 读取彩色图像(BGR)
    
    # 方式一:自定义切片位置参数
    # xmin, ymin, w, h = 180, 190, 200, 200  # 矩形裁剪区域 (ymin:ymin+h, xmin:xmin+w)
    # imgROI = img1[ymin:ymin + h, xmin:xmin + w].copy()  # 切片获得裁剪后保留的图像区域
    # 方式二:鼠标框选获得位置参数
    roi = cv2.selectROI(img1, showCrosshair=True, fromCenter=False)
    xmin, ymin, w, h = roi  # 矩形裁剪区域 (ymin:ymin+h, xmin:xmin+w) 的位置参数
    imgROI = img1[ymin:ymin + h, xmin:xmin + w].copy()  # 切片获得裁剪后保留的图像区域
    
    cv2.imshow("DemoRIO", imgROI)
    cv2.waitKey(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    鼠标框选之后,按回车显示裁剪之后的图片。

    在这里插入图片描述

    图像拼接

    因为图像的本质是array,因此可以使用np.hstacknp.vstack来实现图像的水平拼接和垂直拼接。

    示例程序:

    """
    拼接图像
    """
    import cv2
    import numpy as np
    
    img1 = cv2.imread("img/img.jpg")  # 读取彩色图像(BGR)
    img2 = cv2.imread("img/img.jpg")  # 读取彩色图像(BGR)
    img1 = cv2.resize(img1, (400, 400))
    img2 = cv2.resize(img2, (300, 400))
    img3 = cv2.resize(img2, (400, 300))
    imgStackH = np.hstack((img1, img2))  # 高度相同图像可以横向水平拼接
    imgStackV = np.vstack((img1, img3))  # 宽度相同图像可以纵向垂直拼接
    
    
    cv2.imshow("DemoStackH", imgStackH)  # 在窗口显示图像 imgStackH
    cv2.imshow("DemoStackV", imgStackV)  # 在窗口显示图像 imgStackV
    key = cv2.waitKey(0)  # 等待按键命令
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    效果:

    在这里插入图片描述

    图像通道拆分

    图像通道拆分有两种思路:第一种思路是调用cv2.split进行拆分,需要注意的是拆分出来的通道并不能直接显示,必须扩展为三通道才能显示出来。

    """
    图像通道的拆分
    """
    import cv2
    import numpy as np
    
    img1 = cv2.imread("img/img.jpg", flags=1)  # flags=1 读取彩色图像(BGR)
    
    # 思路一:使用cv2.split进行拆分
    bImg, gImg, rImg = cv2.split(img1)  # 拆分为 BGR 独立通道
    cv2.imshow("bImg", bImg)  # 直接显示蓝色分量 bImg 显示为灰度图像
    # 将单通道扩展为三通道
    imgZeros = np.zeros_like(img1)  # 创建与 img1 相同形状的黑色图像
    imgZeros[:, :, 0] = bImg  # 在黑色图像模板添加蓝色分量 bImg
    cv2.imshow("channel B", imgZeros)  # 扩展为 BGR 通道
    
    cv2.waitKey(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    第二种思路还是利用图片的本质,利用切片的方式将各通道剥离开来。

    示例程序:

    """
    图像通道的拆分
    """
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    img1 = cv2.imread("img/img.jpg", flags=1)  # flags=1 读取彩色图像(BGR)
    
    # # 思路一:使用cv2.split进行拆分
    # bImg, gImg, rImg = cv2.split(img1)  # 拆分为 BGR 独立通道
    # cv2.imshow("bImg", bImg)  # 直接显示蓝色分量 bImg 显示为灰度图像
    # # 将单通道扩展为三通道
    # imgZeros = np.zeros_like(img1)  # 创建与 img1 相同形状的黑色图像
    # imgZeros[:, :, 0] = bImg  # 在黑色图像模板添加蓝色分量 bImg
    # cv2.imshow("channel B", imgZeros)  # 扩展为 BGR 通道
    # cv2.waitKey(0)
    
    # 思路二:切片分离
    # 获取 B 通道
    bImg = img1.copy()  # 获取 BGR
    bImg[:, :, 1] = 0  # G=0
    bImg[:, :, 2] = 0  # R=0
    
    # 获取 G 通道
    gImg = img1.copy()  # 获取 BGR
    gImg[:, :, 0] = 0  # B=0
    gImg[:, :, 2] = 0  # R=0
    
    # 获取 R 通道
    rImg = img1.copy()  # 获取 BGR
    rImg[:, :, 0] = 0  # B=0
    rImg[:, :, 1] = 0  # G=0
    
    # 消除 B 通道
    grImg = img1.copy()  # 获取 BGR
    grImg[:, :, 0] = 0  # B=0
    
    plt.subplot(221), plt.title("1. B channel"), plt.axis('off')
    bImg = cv2.cvtColor(bImg, cv2.COLOR_BGR2RGB)  # 图片格式转换:BGR(OpenCV) -> RGB(PyQt5)
    plt.imshow(bImg)  # matplotlib 显示 channel B
    plt.subplot(222), plt.title("2. G channel"), plt.axis('off')
    gImg = cv2.cvtColor(gImg, cv2.COLOR_BGR2RGB)
    plt.imshow(gImg)  # matplotlib 显示 channel G
    plt.subplot(223), plt.title("3. R channel"), plt.axis('off')
    rImg = cv2.cvtColor(rImg, cv2.COLOR_BGR2RGB)
    plt.imshow(rImg)  # matplotlib 显示 channel R
    plt.subplot(224), plt.title("4. GR channel"), plt.axis('off')
    grImg = cv2.cvtColor(grImg, cv2.COLOR_BGR2RGB)
    plt.imshow(grImg)  # matplotlib 显示 channel GR
    plt.savefig("result2.png")
    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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    在这里插入图片描述
    两种方式效果一样,不过使用OpenCV提供的接口似乎更加费时。

    图像通道合并

    图像通道合并同样有两种方式,第一种方式是使用OpenCV的接口cv2.merge,第二种方式是使用np.stack进行拼接。

    示例程序:

    """
    图像通道合并
    """
    import cv2
    import numpy as np
    
    img1 = cv2.imread("img/img.jpg", flags=1)  # flags=1 读取彩色图像(BGR)
    bImg, gImg, rImg = cv2.split(img1)  # 拆分为 BGR 独立通道
    
    # cv2.merge 实现图像通道的合并
    imgMerge = cv2.merge([bImg, gImg, rImg])
    cv2.imshow("cv2Merge", imgMerge)
    
    # Numpy 拼接实现图像通道的合并
    imgStack = np.stack((bImg, gImg, rImg), axis=2)
    cv2.imshow("npStack", imgStack)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()  # 释放所有窗口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这两种方式效果等价,先把原图三个通道拆开来再合并,合并之后和原图一样。

  • 相关阅读:
    Git命令入门
    添加马赛克并虚化边框的技巧分享
    有哪些美股量化接口?
    java 工程管理系统源码+项目说明+功能描述+前后端分离 + 二次开发
    java集合
    信息系统项目管理师-整合管理论文提纲
    【ARFoundation学习笔记】ARFoundation基础(下)
    A. How Much Does Daytona Cost?
    SHOW ME THE CODE - 面向对象程序设计之 - 单一职责原则
    msvc++中的预编译头文件pch.hpp和stdafx.h
  • 原文地址:https://blog.csdn.net/qq1198768105/article/details/126329501