Reputation: 3856
I need to blur specific areas of an image. I get the image and a mask depicting areas in the image that needs to be blurred. It works, but a bit slower than expected, given that I need to blur dozens of images.
Here is the code I use:
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)
Upvotes: 5
Views: 5560
Reputation: 46600
Here's a method using Numpy slicing
Input and mask image
Grab ROI using Numpy slicing (left), blur ROI (right)
Replace blurred ROI back into original image
This method should be faster since you're taking advantage of Numpy slicing
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()
Upvotes: 0
Reputation: 1715
You need to use different algorithm for blurring. Lets define two parameters: n
- the number of pixels in the image and r
the window size of the Gaussian blur filter.
You use a very big kernel - 221x221 pixels so r
is equal to 221. That requires 221^2 = 48841 operations per pixel using standard convolution approach. That will result in computational complexity of O(r^2*n
). However you can use Central Limit Theorem and approximate the kernel used in blurring with a series of box filters separately in two directions. You can achieve even faster processing time if you use the fact that neighborhood of two consecutive pixels differs in only one pixel. Finally you can get computational complexity that is independent of the window size: O(n
). See this link that explain the whole process. It is written in Javascript but the math is simple and well explained so you can definitely implement it using Python.
Upvotes: 2