5

I have an image and circle zone. I need to blur all, except for circle zone. Also i need to make border of circle smooth.
The input: Input

The output(made it in image redactor with mask, but i think opencv is using only bitmap masks):
Output

For now i have code in python, which isn't blurring border of circle.

def blur_image(cv_image, radius, center, gaussian_core, sigma_x):
    blurred = cv.GaussianBlur(cv_image, gaussian_core, sigma_x)
    h, w, d = cv_image.shape
# masks
    circle_mask = np.ones((h, w), cv_image.dtype)
    cv.circle(circle_mask, center, radius, (0, 0, 0), -1)
    circle_not_mask = np.zeros((h, w), cv_image.dtype)
    cv.circle(circle_not_mask, center, radius, (2, 2, 2), -1)
# Computing
    blur_around = cv.bitwise_and(blurred, blurred, mask=circle_mask)
    image_in_circle = cv.bitwise_and(cv_image, cv_image, mask=circle_not_mask)
    res = cv.bitwise_or(blur_around, image_in_circle)
    return res

Current version:
enter image description here How can i blur the border of circle? In example of output i've used gradient mask in program. Is there something similar in opencv?
UPDATE 04.03
So, i've tried formula from this answered topic and what i have:
Another try
Code:

def blend_with_mask_matrix(src1, src2, mask):
    res = src2 * (1 - cv.divide(mask, 255.0)) + src1 * cv.divide(mask, 255.0)
return res

This code should work similar as recent one, but it doesn't. The image in circle is slightly different. It has some problems with color. The question is still open.

Community
  • 1
  • 1
01ghost13
  • 133
  • 1
  • 2
  • 12
  • have a look at [THIS PAGE](http://stackoverflow.com/questions/30101044/how-to-blur-some-portion-of-image-in-android) – Jeru Luke Mar 04 '17 at 13:43
  • @Jeru I've already find a proper formula but have some issues with integreating it into python. `(mask/255) * blur + (1-mask/255)*another img`. I'm trying to work without loops, only with built in numpy matrix operations – 01ghost13 Mar 04 '17 at 14:23
  • how well is your formula working? – Jeru Luke Mar 04 '17 at 14:31
  • @Jeru I got it from [link](http://stackoverflow.com/questions/36216702/combining-2-images-with-transparent-mask-in-opencv). The main problem is that i cant divide matrix with mask on 255, because i'm getting assertation error `OpenCV Error: Assertion failed (depth == CV_8U || depth == CV_16U || depth == CV_32F)` – 01ghost13 Mar 04 '17 at 14:36

4 Answers4

11

I think maybe you want something like that.

This is the source image:

enter image description here

The source-blured-pair : enter image description here

The mask-alphablened-pair:

enter image description here


The code with description in the code comment.

#!/usr/bin/python3
# 2018.01.16 13:07:05 CST
# 2018.01.16 13:54:39 CST
import cv2
import numpy as np

def alphaBlend(img1, img2, mask):
    """ alphaBlend img1 and img 2 (of CV_8UC3) with mask (CV_8UC1 or CV_8UC3)
    """
    if mask.ndim==3 and mask.shape[-1] == 3:
        alpha = mask/255.0
    else:
        alpha = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)/255.0
    blended = cv2.convertScaleAbs(img1*(1-alpha) + img2*alpha)
    return blended

img = cv2.imread("test.png")

H,W = img.shape[:2]
mask = np.zeros((H,W), np.uint8)
cv2.circle(mask, (325, 350), 40, (255,255,255), -1, cv2.LINE_AA)
mask = cv2.GaussianBlur(mask, (21,21),11 )

blured = cv2.GaussianBlur(img, (21,21), 11)

blended1 = alphaBlend(img, blured, mask)
blended2 = alphaBlend(img, blured, 255- mask)

cv2.imshow("blened1", blended1);
cv2.imshow("blened2", blended2);
cv2.waitKey();cv2.destroyAllWindows()

Some useful links:

  1. Alpha Blending in OpenCV C++ : Combining 2 images with transparent mask in opencv

  2. Alpha Blending in OpenCV Python: Gradient mask blending in opencv python

Kinght 金
  • 17,681
  • 4
  • 60
  • 74
3

So the main problem with (mask/255) * blur + (1-mask/255)*another img was operators. They were working only with one channel. Next problem is working with float numbers for "smoothing".
I've changed code of blending with alpha channel to this:
1) i'm taking every channel for source images and mask
2) Performing formula
3) Merging channels

def blend_with_mask_matrix(src1, src2, mask):
    res_channels = []
    for c in range(0, src1.shape[2]):
        a = src1[:, :, c]
        b = src2[:, :, c]
        m = mask[:, :, c]
        res = cv.add(
            cv.multiply(b, cv.divide(np.full_like(m, 255) - m, 255.0, dtype=cv.CV_32F), dtype=cv.CV_32F),
            cv.multiply(a, cv.divide(m, 255.0, dtype=cv.CV_32F), dtype=cv.CV_32F),
           dtype=cv.CV_8U)
        res_channels += [res]
    res = cv.merge(res_channels)
    return res

And as a gradient mask i'm just using blurred circle.

def blur_image(cv_image, radius, center, gaussian_core, sigma_x):
    blurred = cv.GaussianBlur(cv_image, gaussian_core, sigma_x)

    circle_not_mask = np.zeros_like(cv_image)
    cv.circle(circle_not_mask, center, radius, (255, 255, 255), -1)
#Smoothing borders
    cv.GaussianBlur(circle_not_mask, (101, 101), 111, dst=circle_not_mask)
# Computing
    res = blend_with_mask_matrix(cv_image, blurred, circle_not_mask)
    return res

Result:

Result It is working a bit slower than very first version without smoother borders, but it's ok.
Closing question.

01ghost13
  • 133
  • 1
  • 2
  • 12
0

You can easily mask upon an image using the following funciton:

def transparentOverlay(src, overlay, pos=(0, 0), scale=1):
    overlay = cv2.resize(overlay, (0, 0), fx=scale, fy=scale)
    h, w, _ = overlay.shape  # Size of foreground
    rows, cols, _ = src.shape  # Size of background Image
    y, x = pos[0], pos[1]  # Position of foreground/overlay image

    # loop over all pixels and apply the blending equation
    for i in range(h):
        for j in range(w):
            if x + i >= rows or y + j >= cols:
                continue
            alpha = float(overlay[i][j][3] / 255.0)  # read the alpha channel
            src[x + i][y + j] = alpha * overlay[i][j][:3] + (1 - alpha) * src[x + i][y + j]
    return src

You need to pass the source image, then the overlay mask and position where you want to set the mask. You can even set the masking scale. by calling it like this way.

transparentOverlay(face_cigar_roi_color,cigar,(int(w/2),int(sh_cigar/2)))

For details you can look at this link: Face masking and Overlay using OpenCV python

Output:

Face Masking Demo

0

You can try using a function from PIL library. example -

from PIL import Image, ImageFilter
blur_factor = 3 # for smooth borders as you have mentioned

blurred_mask = mask.filter(ImageFilter.GaussianBlur(blur_factor)) # your circle = 255, background = 0
final_img = Image.composite(blurred_img, original_img, blurred_mask) # here blurred image is the one which you have already blurred, original image is your sharp non blurred image