美文网首页
OpenCV_007-OpenCV 中的图像基本操作

OpenCV_007-OpenCV 中的图像基本操作

作者: hanpfei | 来源:发表于2022-03-22 08:51 被阅读0次

    本文主要内容来自于 OpenCV-Python 教程核心操作 部分,这个部分的主要内容如下:

    目标

    学习:

    • 访问像素值并修改它们
    • 访问图像属性
    • 设置感兴趣区域 (ROI)
    • 分割和合并图像

    本节中几乎所有的操作都主要与 Numpy 有关,而不是 OpenCV。使用 OpenCV 编写更好的优化的代码需要良好的 Numpy 知识。

    访问及修改像素值

    让我们先加载一幅彩色图像:

    #!/use/bin/env python
    
    import numpy as np
    import cv2 as cv
    
    if __name__ == "__main__":
        cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
        filepath = cv.samples.findFile("messi5.jpg")
        img = cv.imread(filepath)
    

    我们可以根据像素点的行和列坐标访问像素的值。对于 BGR 图像,它返回一个蓝色、绿色和红色值的数组。对于灰度图像,只返回相应的亮度。

        px = img[100, 100]
        print(px)
        print(px.__class__.__name__)
    
        blue = img[100, 100, 0]
        print(blue)
    

    这几行代码对应的输出如下:

    [157 166 200]
    ndarray
    157
    

    彩色图像像素点的值是由一个 Numpy 的 ndarray 表示的。我们还可以以相同的方式修改像素值。

        img[100, 100] = [255, 255, 255]
        print(img[100, 100])
    

    这几行代码对应的输出如下:

    [255 255 255]
    

    警告
    Numpy 是一个为快速数组计算高度优化的库。因此,简单地访问每个像素值并对其进行修改将非常缓慢,并且不鼓励这样做。

    注意
    上面的方法通常用于选择数组的一个区域,比如开始的 5 行和最后的 3 列。对于单独的像素访问,Numpy 数组方法,array.item()array.itemset() 被认为是更好的选择。它们总是返回一个标量,然而,如果想要访问所有的 B,G,R 值,则将需要为每个值分别调用 array.item()

    更好的像素访问和编辑方法:

        # accessing RED value
        red_value = img.item(10, 10, 2)
        print(red_value)
    
        # modifying RED value
        img.itemset((10, 10, 2), 100)
    
        red_value = img.item(10, 10, 2)
        print(red_value)
    

    这几行代码对应的输出如下:

    59
    100
    

    访问图像的属性

    图像属性包括行数、列数和通道数;图像数据的类型;像素的个数等等。

    一幅图像的形状可以通过 img.shape 访问。它返回行数、列数和通道数的元组(如果图像是彩色的):

        print(img.shape)
    

    这行代码对应的输出如下:

    (342, 548, 3)
    

    注意
    如果图像是灰度图,则返回的元组只包含行数和列数,因而这是一种检查加载的图像是灰度图还是彩色图的好方法。

    像素值的总个数通过 img.size 访问,它是行数、列数和通道数三者的乘积,而不是行数和列数两者的乘积:

        print(img.size)
        totoal_pixels = img.shape[0] * img.shape[1] * img.shape[2]
        print(totoal_pixels)
    

    这几行代码对应的输出如下:

    562248
    562248
    

    图像的数据类型通过 img.dtype 获取:

        print(img.dtype)
    

    这行代码对应的输出如下:

    uint8
    

    注意
    img.dtype 在调试时非常重要,因为 OpenCV-Python 代码中的大量错误都是由无效的数据类型引起的。

    图像 ROI

    有时,我们将不得不使用某些图像区域。对于图像中的眼睛检测,首先对整个图像进行人脸检测。当获得人脸时,我们只选择人脸区域并在其中搜索眼睛,而不是搜索整个图像。它提高了精度(因为眼睛总是在脸上:D)和性能(因为我们在一个更小的区域内搜索)。

    使用 Numpy 索引再次获得 ROI。这里我们选择足球,并把它拷贝到图像的另一个区域:

        ball = img[280:340, 330:390]
        img[273:333, 100:160] = ball
    

    上面方括号中逗号前面的数字表示选取的区域的行的范围,即区域的垂直方向的范围,后面的数字表示选取的区域的列的范围,即区域的水平方向的范围。检查结果如下:

    分割和合并图像通道

    有时我们需要分别处理一幅图像的 B,G,R 通道。在这种情况下,我们需要把 BGR 图像分割为单独的通道。在其它情况下,我们可能需要合并这些单独的通道,并创建 BGR 图像。我们可以通过以下方式简单地做到这一点:

        b, g, r = cv.split(img)
        img = cv.merge((b, r, g))
    

    这里有意没有按照原来的数据格式合并数据,而是把所有像素点的绿色通道和红色通道的值做了交换。此外,分割获得的单个色彩通道的值可以作为一幅灰度图来绘制。

    或者:

        b = img[:, :, 0]
    

    假设我们想要把所有像素的红色通道值都设置为 0 —— 我们不需要先分割通道。Numpy 的索引更快:

        img[:, :, 2] = 0
    

    警告
    cv.split() 是一项代价高昂的操作(就时间而言)。因此只在需要的时候使用它。否则使用 Numpy 的索引。

    为图像制作边框(填充)

    如果要在图像周围创建边框,例如相框,可以使用 cv.copyMakeBorder()。 但它在卷积运算、零填充等方面有更多应用。此函数接收以下参数:

    • src - 输入图像

    • topbottomleftright - 相应方向上边框的以像素为单位的宽度。

    • borderType - 定义了添加何种边框的标记。它可以是以下类型:

    • value - 如果边框类型是 cv.BORDER_CONSTANT 这个是边框的颜色

    下面这段代码演示了所有这些边框类型,以使我们获得更好的理解。

    def border_type():
        BLUE = [255, 0, 0]
    
        cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
        filepath = cv.samples.findFile("opencv-logo.png")
        img1 = cv.imread(filepath)
    
        replicate = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_REPLICATE)
        reflect = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_REFLECT)
        reflect101 = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_REFLECT_101)
        wrap = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_WRAP)
        constant = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_CONSTANT, value=BLUE)
    
        isolated = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_ISOLATED)
    
        plt.subplot(231), plt.imshow(img1, 'gray'), plt.title('ORIGINAL')
        plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
        plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
    
        plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
        plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
        plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')
    
        plt.subplots_adjust(wspace=0.4, hspace=0.4)
    
        plt.show()
    

    这里用到的示例图像文件同样在 OpenCV 的示例数据中,因而先查找这个文件的完整路径并加载。图像由 matplotlib 显示。因而 RED 和 BLUE 通道将会被交换 。用 matplotlib 画图时,为了防止不同图之间相互遮盖,这里通过 plt.subplots_adjust(wspace=0.4, hspace=0.4) 对子图做了一些调整。

    来看下最终的结果:

    Image

    参考文档

    Basic Operations on Images

    Done.

    相关文章

      网友评论

          本文标题:OpenCV_007-OpenCV 中的图像基本操作

          本文链接:https://www.haomeiwen.com/subject/aljadrtx.html