[python] OpenCV - Apply mask to a color image

How can I apply mask to a color image in latest python binding (cv2)? In previous python binding the simplest way was to use cv.Copy e.g.

cv.Copy(dst, src, mask)

But this function is not available in cv2 binding. Is there any workaround without using boilerplate code?

This question is related to python opencv image-processing mask

The answer is


Answer given by Abid Rahman K is not completely correct. I also tried it and found very helpful but got stuck.

This is how I copy image with a given mask.

x, y = np.where(mask!=0)
pts = zip(x, y)
# Assuming dst and src are of same sizes
for pt in pts:
   dst[pt] = src[pt]

This is a bit slow but gives correct results.

EDIT:

Pythonic way.

idx = (mask!=0)
dst[idx] = src[idx]

Well, here is a solution if you want the background to be other than a solid black color. We only need to invert the mask and apply it in a background image of the same size and then combine both background and foreground. A pro of this solution is that the background could be anything (even other image).

This example is modified from Hough Circle Transform. First image is the OpenCV logo, second the original mask, third the background + foreground combined.

apply mask and get a customized background

# http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghcircles/py_houghcircles.html
import cv2
import numpy as np

# load the image
img = cv2.imread('E:\\FOTOS\\opencv\\opencv_logo.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# detect circles
gray = cv2.medianBlur(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY), 5)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=50, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))

# draw mask
mask = np.full((img.shape[0], img.shape[1]), 0, dtype=np.uint8)  # mask is only 
for i in circles[0, :]:
    cv2.circle(mask, (i[0], i[1]), i[2], (255, 255, 255), -1)

# get first masked value (foreground)
fg = cv2.bitwise_or(img, img, mask=mask)

# get second masked value (background) mask must be inverted
mask = cv2.bitwise_not(mask)
background = np.full(img.shape, 255, dtype=np.uint8)
bk = cv2.bitwise_or(background, background, mask=mask)

# combine foreground+background
final = cv2.bitwise_or(fg, bk)

Note: It is better to use the opencv methods because they are optimized.


The other methods described assume a binary mask. If you want to use a real-valued single-channel grayscale image as a mask (e.g. from an alpha channel), you can expand it to three channels and then use it for interpolation:

assert len(mask.shape) == 2 and issubclass(mask.dtype.type, np.floating)
assert len(foreground_rgb.shape) == 3
assert len(background_rgb.shape) == 3

alpha3 = np.stack([mask]*3, axis=2)
blended = alpha3 * foreground_rgb + (1. - alpha3) * background_rgb

Note that mask needs to be in range 0..1 for the operation to succeed. It is also assumed that 1.0 encodes keeping the foreground only, while 0.0 means keeping only the background.

If the mask may have the shape (h, w, 1), this helps:

alpha3 = np.squeeze(np.stack([np.atleast_3d(mask)]*3, axis=2))

Here np.atleast_3d(mask) makes the mask (h, w, 1) if it is (h, w) and np.squeeze(...) reshapes the result from (h, w, 3, 1) to (h, w, 3).


import cv2 as cv

im_color = cv.imread("lena.png", cv.IMREAD_COLOR)
im_gray = cv.cvtColor(im_color, cv.COLOR_BGR2GRAY)

At this point you have a color and a gray image. We are dealing with 8-bit, uint8 images here. That means the images can have pixel values in the range of [0, 255] and the values have to be integers.

left-color,right-gray

Let's do a binary thresholding operation. It creates a black and white masked image. The black regions have value 0 and the white regions 255

_, mask = cv.threshold(im_gray, thresh=180, maxval=255, type=cv.THRESH_BINARY)
im_thresh_gray = cv.bitwise_and(im_gray, mask)

The mask can be seen below on the left. The image on it's right is the result of applying bitwise_and operation between the gray image and the mask. What happened is, the spatial locations where the mask had a pixel value zero (black), became pixel value zero in the result image. The locations where the mask had pixel value 255 (white), the resulting image retained it's original gray value.

left-mask,right-bitwise_and_with_mask

To apply this mask to our original color image, we need to convert the mask into a 3 channel image as the original color image is a 3 channel image.

mask3 = cv.cvtColor(mask, cv.COLOR_GRAY2BGR)  # 3 channel mask

Then, we can apply this 3 channel mask to our color image using the same bitwise_and function.

im_thresh_color = cv.bitwise_and(im_color, mask3)

mask3 from the code is the image below on the left, and im_thresh_color is on its right.

left-mask-3channel,right-bitwise_and_with_3channel-mask

You can plot the results and see for yourself.

cv.imshow("original image", im_color)
cv.imshow("binary mask", mask)
cv.imshow("3 channel mask", mask3)
cv.imshow("im_thresh_gray", im_thresh_gray)
cv.imshow("im_thresh_color", im_thresh_color)
cv.waitKey(0)

The original image is lenacolor.png that I found here.


Examples related to python

programming a servo thru a barometer Is there a way to view two blocks of code from the same file simultaneously in Sublime Text? python variable NameError Why my regexp for hyphenated words doesn't work? Comparing a variable with a string python not working when redirecting from bash script is it possible to add colors to python output? Get Public URL for File - Google Cloud Storage - App Engine (Python) Real time face detection OpenCV, Python xlrd.biffh.XLRDError: Excel xlsx file; not supported Could not load dynamic library 'cudart64_101.dll' on tensorflow CPU-only installation

Examples related to opencv

Real time face detection OpenCV, Python OpenCV TypeError: Expected cv::UMat for argument 'src' - What is this? OpenCV !_src.empty() in function 'cvtColor' error ConvergenceWarning: Liblinear failed to converge, increase the number of iterations How do I install opencv using pip? Access IP Camera in Python OpenCV ImportError: libSM.so.6: cannot open shared object file: No such file or directory Convert np.array of type float64 to type uint8 scaling values How to import cv2 in python3? cmake error 'the source does not appear to contain CMakeLists.txt'

Examples related to image-processing

Convert np.array of type float64 to type uint8 scaling values dlib installation on Windows 10 OpenCV - Saving images to a particular folder of choice How do I increase the contrast of an image in Python OpenCV OpenCV & Python - Image too big to display TypeError: Image data can not convert to float Extracting text OpenCV c++ and opencv get and set pixel color to Mat cv2.imshow command doesn't work properly in opencv-python How does one convert a grayscale image to RGB in OpenCV (Python)?

Examples related to mask

Update row values where certain condition is met in pandas Implement an input with a mask Phone mask with jQuery and Masked Input Plugin OpenCV - Apply mask to a color image How to Apply Mask to Image in OpenCV?