13

I have an image. Like this:

enter image description here

I detect a subject(which is a person in this case) & it masks the image like this:

enter image description here

I want the background of the subject to be blurrred. Like this:

enter image description here

Below is the code I have tried. the following code only blurs

import cv2
import numpy as np
from matplotlib import pyplot as plt
import os


path = 'selfies\\'
selfImgs = os.listdir(path)


for image in selfImgs:

    img = cv2.imread(path+image)
    img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    blur = cv2.blur(img,(10,10))
    #canny = cv2.Canny(blur, 10, 30)

    #plt.imshow(canny)
    plt.imshow(blur)

    j=cv2.cvtColor(blur, cv2.COLOR_BGR2RGB)
    print(image)
    cv2.imwrite('blurred\\'+image+".jpg",j)

Is there any way by which I can blur only specific part/parts of the image.

This project is based on https://github.com/matterport/Mask_RCNN

I can provide more information if required.

I have an approach in numpy :-

final_image = original * mask + blurred * (1-mask)
penta
  • 2,536
  • 4
  • 25
  • 50
  • What you would do is blur the entire image and then combine the original and the blurred one according to the mask (e.g. see [this question](https://stackoverflow.com/questions/10469235/opencv-apply-mask-to-a-color-image)). – jdehesa Sep 17 '18 at 10:52

3 Answers3

21

You may use np.where() method to select the pixels where you want blurred values and then replace them as:

import cv2
import numpy as np

img = cv2.imread("/home/user/Downloads/lena.png")
blurred_img = cv2.GaussianBlur(img, (21, 21), 0)

mask = np.zeros((512, 512, 3), dtype=np.uint8)
# For older OpenCV <= 3
# mask = cv2.circle(mask, (258, 258), 100, np.array([255, 255, 255]), -1)
# out = np.where(mask==np.array([255, 255, 255]), img, blurred_img)

# For latest OpenCV
mask = cv2.circle(mask, (258, 258), 100, (255, 255, 255), -1)
out = np.where(mask==(255, 255, 255), img, blurred_img)

cv2.imwrite("./out.png", out)

enter image description here

ZdaR
  • 22,343
  • 7
  • 66
  • 87
  • 1
    Exactly. Using masks is the most common and efficient way. – mr_mo Sep 17 '18 at 13:06
  • 2
    @ZdaR I get TypeError: Scalar value for argument 'color' is not numeric – penta Sep 17 '18 at 18:16
  • At which line of code, maybe you have some different version of OpenCV installed and the code may require minor tweaks. Can you share the full traceback? – ZdaR Sep 18 '18 at 04:42
  • 5
    @ZdaR, in my case I needed to replace `np.array([255, 255, 255])` with `(255, 255, 255)` globally. using opencv-python==4.1.0.25. – Silvan Mühlemann May 07 '19 at 18:47
6

As ZdaR said:

import cv2
import numpy as np

img = cv2.imread("/home/user/Downloads/lena.png")
blurred_img = cv2.GaussianBlur(img, (21, 21), 0)

mask = np.zeros((512, 512, 3), dtype=np.uint8)
mask = cv2.circle(mask, (258, 258), 100, np.array([255, 255, 255]), -1)

out = np.where(mask==np.array([255, 255, 255]), img, blurred_img)

cv2.imwrite("./out.png", out)

This is good idea but I've got same error as penta:

@ZdaR I get TypeError: Scalar value for argument 'color' is not numeric

A simple solution is to modify color value when you create Circle:

mask = cv2.circle(mask, (258, 258), 100, (255, 255,255), -1)

just CHANGE np.array([255,255,255]) to (255,255,255).

Community
  • 1
  • 1
nima farhadi
  • 678
  • 8
  • 9
0

I don't know what tools you use for subject detection, but if you have a way to copy the subject, you can first copy the whole image and then blur it. finally, you can copy the subject on the blurred image. if it gives false and true on pixels in oppose to giving borders, you can just bitwise it.

parsa
  • 370
  • 4
  • 13