在 python 中使用蒙版有效模糊图像

Efficient blurring of image using a mask in python

我需要模糊图像的特定区域。我得到了图像和一个蒙版,描绘了图像中需要模糊的区域。它有效,但比预期的慢一点,因为我需要模糊几十张图像。

这是我使用的代码:

def soft_blur_with_mask(image: np.ndarray, mask: np.ndarray) -> np.ndarray:
    assert len(mask.shape) == 2, mask.shape
    # Create a blurred copy of the original image. This can take up to 1-2 seconds today, because the image is big (~5-10 Megapixels)
    blurred_image = cv2.GaussianBlur(image, (221, 221), sigmaX=20, sigmaY=20)
    image_height, image_width = image.shape[:2]
    mask = cv2.resize(mask.astype(np.uint8), (image_width, image_height), interpolation=cv2.INTER_NEAREST)
    # Blurring the mask itself to get a softer mask with no firm edges
    mask = cv2.GaussianBlur(mask.astype(np.float32), (11, 11), 10, 10)[:, :, None]
    mask = mask/255.0

    # Take the blurred image where the mask it positive, and the original image where the image is original
    return (mask * blurred_image + (1.0 - mask) * image).clip(0, 255.0).astype(np.uint8)

您需要使用不同的模糊算法。让我们定义两个参数:n - 图像中的像素数和 r 高斯模糊滤波器的 window 大小。

您使用了一个非常大的内核 - 221x221 像素,因此 r 等于 221。这需要使用标准卷积方法对每个像素进行 221^2 = 48841 次操作。这将导致 O(r^2*n) 的计算复杂度。但是,您可以使用中心极限定理并分别在两个方向上使用一系列盒式滤波器来近似模糊中使用的内核。如果您利用两个连续像素的邻域只有一个像素不同这一事实,您可以获得更快的处理时间。最后,您可以获得与 window 大小无关的计算复杂度:O(n)。看到这个 link 解释了整个过程。它是用 Javascript 编写的,但数学很简单并且解释得很好,因此您绝对可以使用 Python.

来实现它

这是一个使用 Numpy 切片的方法

  • 将蒙版转换为灰度并在蒙版上找到轮廓
  • 遍历轮廓并提取 ROI
  • 模糊每个 ROI 并替换为原始图像

输入和掩码图像

使用 Numpy 切片获取 ROI(左),模糊 ROI(右)

将模糊的 ROI 替换回原始图像

此方法应该更快,因为您正在利用 Numpy 切片

import cv2
import numpy as np

def soft_blur_with_mask(image: np.ndarray, mask: np.ndarray) -> np.ndarray:
    mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
    cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    for c in cnts:
        x,y,w,h = cv2.boundingRect(c)
        ROI = image[y:y+h, x:x+w]
        image[y:y+h, x:x+w] = cv2.GaussianBlur(ROI, (41,41), 0)
    return image

if __name__ == '__main__':
    image = cv2.imread('1.png')
    mask = cv2.imread('mask.png')

    result = soft_blur_with_mask(image, mask)

    cv2.imshow('result', result)
    cv2.waitKey()