【问题标题】:Compositing images by blurred mask in Numpy在 Numpy 中通过模糊蒙版合成图像
【发布时间】:2020-06-09 00:04:56
【问题描述】:

我有两个图像和一个掩码,所有尺寸都与 Numpy 数组相同:



期望的输出

我想以这样的方式合并它们,输出将是这样的:

当前代码

def merge(lena, rocket, mask):
    '''Mask init and cropping'''
    mask = np.zeros(lena.shape[:2], dtype='uint8')
    cv2.fillConvexPoly(mask, circle, 255) # might be polygon
    '''Bitwise operations'''
    lena = cv2.bitwise_or(lena, lena, mask=mask)
    mask_inv = cv2.bitwise_not(mask) # mask inverting
    rocket = cv2.bitwise_or(rocket, rocket, mask=mask_inv)
    output = cv2.bitwise_or(rocket, lena)

    return output

当前结果

这段代码给了我这个结果:

应用cv2.GaussianBlur(mask, (51,51), 0) 会以不同方式扭曲叠加图像的颜色。
其他 SO 问题与类似问题有关,但不能完全解决这种类型的模糊合成。

更新:这给出了与当前结果相同的结果

mask = np.zeros(lena.shape[:2], dtype='uint8')
mask = cv2.GaussianBlur(mask, (51,51), 0)
mask = mask[..., np.newaxis]
cv2.fillConvexPoly(mask, circle, 1)
output = mask * lena + (1 - mask) * rocket

临时解

由于转化次数较多,这可能不是最优的,请告知

mask = np.zeros(generated.shape[:2])
polygon = np.array(polygon, np.int32) # 2d array of x,y coords
cv2.fillConvexPoly(mask, polygon, 1)
mask = cv2.GaussianBlur(mask, (51, 51), 0)
mask = mask.astype('float32')
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
foreground = cv2.multiply(lena, mask, dtype=cv2.CV_8U)
background = cv2.multiply(rocket, (1 - mask), dtype=cv2.CV_8U)
output = cv2.add(foreground, background)

请告知如何模糊蒙版,将其与前景正确合并,然后叠加在背景图像上?

【问题讨论】:

  • 您在寻找什么样的混合物?乘法?添加剂? alpha * x + (1-alpha) * y?在您选择的图像编辑软件中尝试几个(Photoshop/GIMP/Paint.NET/...)。
  • @MateenUlhaq 只需在背景顶部应用模糊前景就足够了,所有这些都是为了达到帖子中所示的所需输出,感谢您的澄清
  • @MateenUlhaq 我想这与 RGB 通道有关,但不确定;我在某处读到它可以使用PIL.Image.composite(lena, rocket, mask) 来实现,但还不知道如何正确地来回转换数组
  • 你试过我提到的简单的alpha混合方法吗? output = mask * lena + (255 - mask) * rocket,其中mask 已根据需要进行模糊处理。
  • @MateenUlhaq 是的,它给了ValueError: operands could not be broadcast together with shape (225, 400) with (225, 400, 3),可能是因为掩码

标签: python numpy opencv python-imaging-library bitwise-or


【解决方案1】:

您需要在混合之前重新规范化蒙版:

def blend_merge(lena, rocket, mask):
    mask = cv2.GaussianBlur(mask, (51, 51), 0)
    mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
    mask = mask.astype('float32') / 255
    foreground = cv2.multiply(lena, mask, dtype=cv2.CV_8U)
    background = cv2.multiply(rocket, (1 - mask), dtype=cv2.CV_8U)
    output = cv2.add(foreground, background)
    return output

一个完整的工作示例是here

【讨论】:

    【解决方案2】:

    这里是如何在 Python/OpenCV 中做到这一点。你的第二种方法很接近。

    • 读取 3 个输入图像
    • 对圆形应用线性(或高斯)模糊
    • 将圆拉伸到全动态范围(0 到 255)作为蒙版
    • 将掩码转换为在 0 到 1 范围内浮动为 3 个通道
    • 通过乘法将蒙版应用于 image1 并将反转蒙版应用于 image2 并将乘积相加
    • 将结果转换为 8 位范围(0 到 255)的剪辑,以确保没有溢出或回绕
    • 保存结果


    输入图片:

    import cv2
    import numpy as np
    
    # Read images
    image1 = cv2.imread('lena_wide.jpg')
    image2 = cv2.imread('rocket.jpg')
    circle = cv2.imread('white_circle.jpg', cv2.IMREAD_GRAYSCALE)
    
    # linear blur mask
    mask = cv2.blur(circle, (30,30))
    # alternate using Gaussian blur
    #mask = cv2.GaussianBlur(circle, (0,0), sigmaX=10, sigmaY=10)
    
    # stretch mask to full dynamic range
    mask = cv2.normalize(mask, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
    
    # convert mask to float in range 0 to 1
    maskf = (mask/255).astype(np.float64)
    maskf = cv2.merge([maskf,maskf,maskf])
    
    # apply mask to image1 and inverted  mask to image2
    result = maskf*image1 + (1-maskf)*image2
    result = result.clip(0,255).astype(np.uint8)
    
    
    # save results
    cv2.imwrite('white_circle_ramped.jpg', mask)
    cv2.imwrite('lena_wide_rocked_composited.png', result)
    
    # show results
    cv2.imshow("mask", mask)
    cv2.imshow("result", result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    


    渐变蒙版图片:

    结果:


    补充:

    这是一种主要使用 Numpy 的替代方法。

    import cv2
    import numpy as np
    
    # Read images
    image1 = cv2.imread('lena_wide.jpg')
    image2 = cv2.imread('rocket.jpg')
    circle = cv2.imread('white_circle2.jpg', cv2.IMREAD_GRAYSCALE)
    
    # linear blur mask
    mask = cv2.blur(circle, (30,30))
    # alternate using Gaussian blur
    #mask = cv2.GaussianBlur(circle, (0,0), sigmaX=10, sigmaY=10)
    
    # stretch mask to full dynamic range
    mask = cv2.normalize(mask, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
    
    # convert mask to 3 channels
    mask = cv2.merge([mask,mask,mask])
    
    # apply mask to image1 and inverted  mask to image2
    image1_masked = np.multiply(image1, mask/255).clip(0,255).astype(np.uint8)
    image2_masked = np.multiply(image2, 1-mask/255).clip(0,255).astype(np.uint8)
    
    # add the two masked images together
    result = np.add(image1_masked, image2_masked)
    
    # save results
    cv2.imwrite('white_circle_ramped2.jpg', mask)
    cv2.imwrite('lena_wide_rocked_composited2.png', result)
    
    # show results
    cv2.imshow("mask", mask)
    cv2.imshow("image1_masked", image1_masked)
    cv2.imshow("image2_masked", image2_masked)
    cv2.imshow("result", result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    


    【讨论】:

    • 太棒了!非常感谢你做的这些!如果有的话,您能否告诉直接 Numpy 数组操作和 OpenCV multiply/add 方法之间的时间复杂度等是否有显着差异?我记得几年前你对 ffmpeg 的帮助,再次感谢!
    • 我不能肯定地说,但我希望这样做会慢一些,但我不知道会慢多少。您必须进行时间测试才能正确确定。请确定您的提议的确切处理方式。
    • 请参阅下面的@shortcipher3 解决方案。它就像我的 Numpy 解决方案,但使用 OpenCV 乘法和加法。应该是最简单的方法了。所有 OpenCV 都可能经过优化,如果需要可以通过 GPU 运行。
    猜你喜欢
    • 2020-05-05
    • 2023-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-24
    • 2016-04-11
    • 1970-01-01
    相关资源
    最近更新 更多