【问题标题】:Equivalent of copyTo in Python OpenCV bindings?Python OpenCV绑定中的copyTo等价物?
【发布时间】:2017-05-25 04:22:12
【问题描述】:

OpenCV 具有 copyTo 功能,可以将蒙版区域从一个 Mat 复制到另一个。

http://docs.opencv.org/3.1.0/d3/d63/classcv_1_1Mat.html#a4331fa88593a9a9c14c0998574695ebb

在 Python 绑定中 this 的等价物是什么?我想使用二进制掩码将图像的一个区域复制到另一个图像。

【问题讨论】:

    标签: python image opencv image-processing


    【解决方案1】:

    cv::Mat::copyTo 根据输出矩阵是否已初始化,执行以下两种操作之一。如果您的输出矩阵未初始化,则使用带有掩码的copyTo 会创建一个与输入类型相同的新输出矩阵,并且所有通道的所有值都设置为 0。一旦发生这种情况,将复制掩码定义的图像数据,并将矩阵的其余部分设置为 0。如果您的输出矩阵 已初始化并且已包含内容,copyTo 将复制覆盖源中掩码中定义的像素,并将不属于掩码的像素留在目标中。因此,源图像中由掩码定义的像素替换被复制到输出中。

    因为 OpenCV 现在使用numpy 与库进行交互,所以使用这两种方法都非常容易。为了与本文中看到的其他答案区分开来,第一种方法可以通过简单地将蒙版与图像以元素方式相乘来完成。假设您的输入称为 img 并且您的二进制掩码称为 mask 我假设掩码是 2D,只需执行以下操作:

    import numpy as np
    import cv2
    
    mask = ... # define mask here
    img = cv2.imread(...) # Define input image here
    
    # Create new image
    new_image = img * (mask.astype(img.dtype))
    

    虽然上面的代码假设imgmask 共享相同数量的通道。如果您使用彩色图像作为源和蒙版 2D,就像我已经假设的那样,它会变得很棘手。因此,通道的总数是 2 而不是 3,所以上面的语法会给你一个错误,因为两者之间的维度不再兼容。当您使用彩色图像时,您需要适应这一点。您可以通过向掩码添加单例第三维来做到这一点,以便可以利用广播。

    import numpy as np
    import cv2
    
    mask = ... # define mask here
    img = cv2.imread(...) # Define input image here
    
    # Create new image
    # Case #1 - Other image is grayscale and source image is colour
    if len(img.shape) == 3 and len(mask.shape) != 3:
        new_image = img * (mask[:,:,None].astype(img.dtype))
    # Case #2 - Both images are colour or grayscale
    elif (len(img.shape) == 3 and len(mask.shape) == 3) or \
       (len(img.shape) == 1 and len(mask.shape) == 1):
        new_image = img * (mask.astype(img.dtype))
    # Otherwise, we can't do this
    else:
        raise Exception("Incompatible input and mask dimensions")
    

    对于第二种方法,假设我们有另一个名为other_image 的图像,您希望将这个图像中由掩码定义的内容复制回目标图像img。在这种情况下,您首先要做的是使用numpy.where 确定掩码中非零的所有位置,然后使用这些位置索引或切片到您的图像以及您想要复制的位置。就像第一种方法一样,我们还必须注意两个图像之间的通道数:

    import numpy as np
    import cv2
    
    mask = ... # define mask here
    img = cv2.imread(...) # Define input image here
    other_image = cv2.imread(...) # Define other image here
    
    locs = np.where(mask != 0) # Get the non-zero mask locations
    
    # Case #1 - Other image is grayscale and source image is colour
    if len(img.shape) == 3 and len(other_image.shape) != 3:
        img[locs[0], locs[1]] = other_image[locs[0], locs[1], None]
    # Case #2 - Both images are colour or grayscale
    elif (len(img.shape) == 3 and len(other_image.shape) == 3) or \
       (len(img.shape) == 1 and len(other_image.shape) == 1):
        img[locs[0], locs[1]] = other_image[locs[0], locs[1]]
    # Otherwise, we can't do this
    else:
        raise Exception("Incompatible input and output dimensions")
    

    以下是两种方法的运行示例。我将使用 Cameraman 图像,它是大多数图像处理算法中看到的标准测试图像。

    我还人为地使图像颜色化,即使它被可视化为灰度,但强度将被复制到所有通道。我还将定义一个蒙版,它只是左上角的 100 x 100 子区域,因此我们将创建一个仅复制该子区域的输出图像:

    import numpy as np
    import cv2
    
    # Define image
    img = cv2.imread("cameraman.png")
    
    # Define mask
    mask = np.zeros(img.shape, dtype=np.bool)
    mask[:100, :100] = True
    

    当您使用第一种方法并显示结果时,我们得到:

    我们可以看到我们创建了一个输出图像,其中左上角的 100 x 100 子区域包含我们的图像数据,其余像素设置为 0。这取决于设置为 True 的掩码位置。对于第二种方法,我们将创建另一张随机图像,该图像与输入图像的大小相同,从[0, 255] 跨越所有通道。

    # Define other image
    other_image = (255*np.random.rand(*img.shape)).astype(np.uint8)
    

    当我们用第二种方法运行代码后,我现在得到了这张图片:

    如您所见,图像的左上角已根据设置为True 的蒙版位置进行了更新。

    【讨论】:

    • 我不是 python 专家。是否存在新的答案?我可以在 python 中制作关于 copyTo 的 PR(opencv),但这有用吗? (链接github.com/opencv/opencv/issues/10225#issuecomment-419886699
    • 同时有一个cv.CopyTo,但它似乎只实现了第一种情况(目标用零初始化)。 @LBerger:不应该在你的 PR 中考虑第二种方法(dst 已经初始化)吗?
    • 是的。我的回答解决了copyTo 所做的这两种情况。 PR 没有实现完整的解决方案。
    【解决方案2】:

    请注意这是否正是您想要的,但对于在 Python 中使用掩码进行复制,我会选择 cv2.bitwise_

    new_image = cv2.bitwise_and(old_image,binary_mask)
    

    【讨论】:

    • 哦,抱歉,我不确定问了什么
    • 实际上,在查看文档时,cv::Mat::copyTo 会将不属于遮罩的像素归零,因此这是正确的。我会在上面留下我的答案,以防 OP 想要复制不受蒙版影响的像素。
    猜你喜欢
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 2013-09-27
    • 2018-09-06
    • 2013-02-06
    • 2012-03-22
    • 2016-12-27
    • 2013-12-13
    相关资源
    最近更新 更多