Gradient mask blending in opencv python

2019-02-16 00:28发布

问题:

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:

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

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:
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:

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.

回答1:

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:

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



回答2:

I think maybe you want something like that.

This is the source image:

The source-blured-pair :

The mask-alphablened-pair:


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



回答3:

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: