Home > Software design >  How to properly insert mask into original image?
How to properly insert mask into original image?

Time:01-13

My goal is to cover a face with circular noise (salt and pepper/black and white dots), however what I have managed to achieve is just rectangular noise.

Using this image: like_this.jpg

I found the face coordinates (x,y,w,h) = [389, 127, 209, 209] And using this new.jpg

From x,y,w,h I found that I want my circle to be at (493, 231) with radius 105

I researched I found something about masking and bitwise operations, so I tried:

mask = np.zeros(new.shape[:2], dtype='uint8')
cv2.circle(mask, (493, 231), 105, 255, -1)

new_gray = cv2.cvtColor(new, cv2.COLOR_BGR2GRAY)
masked = cv2.bitwise_and(new_gray, new_gray , mask=mask)

cv2.imshow('masked', masked)

masked.jpg

Here, the problem arises:

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
OR = cv2.bitwise_or(masked, img_gray) # removes the black dots, idk why 

cv2.imshow('bitwise - OR', OR)

enter image description here

The black dots get removed from the noise and besides that, I can't seem to convert OR back to BGR.

Maybe there is a better way to do that.

Please, help/guidance needed!

CodePudding user response:

Bitwise opperations function on binary conditions turning "off" a pixel if it has a value of zero, and turning it "on" if the pixel has a value greater than zero.

In your case the bitwise_or "removes" both the black background of the mask and the black points of the noise.

I propose to do it like this:

# Read image
img = cv2.imread('like_this.jpg')
x,y,w,h = [389, 127, 209, 209]

# Add squared noise
noised = add_noise(img[y:y h,x:x w])
new = img.copy()
new[y:y h,x:x w] = noised

# Transform the image to graylevel
new_gray = cv2.cvtColor(new, cv2.COLOR_BGR2GRAY)

# Create a mask in which black noise is equal to 0 and white to 2
mask = np.zeros(new.shape[:2], dtype='uint8')
mask[new_gray==0] = 1
mask[new_gray==255] = 2

# Mask the previous mask with a circle
circle = np.zeros(new.shape[:2], dtype='uint8')
circle = cv2.circle(circle, (493, 231), 105, 1, -1)
mask = mask * circle

plt.imshow(mask)
plt.show()

# Join the mask adding the noise to the image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray[mask==1] = 0
gray[mask==2] = 255

cv2.imshow('Result', gray)
cv2.waitKey(0)

circle mask

final image

Hope it works. If you need more help let me know.

CodePudding user response:

So the issue is how to use masking. There are two options, numpy and OpenCV.

numpy

Since you copied the noisy area into the result and now want to restore everything outside of the circle, I'll use mask == 0 to get a boolean array that is true everywhere outside of the circle.

mask ~mask

With numpy, boolean arrays can be used as indices. The result is a "view", it behaves like a slice. Operations through it affect the original data.

noised = add_noise(img[y:y h,x:x w])
new = img.copy()
new[y:y h,x:x w] = noised # whole rectangle affected

new[mask == 0] = img[mask == 0] # restore everything outside of the circle

All three arrays (mask, new, img) need to have the same shape.

OpenCV, masks

Not much point to it with Python and numpy available, but many of its C APIs take an optional mask argument that modifies that function's operation. Mat::copyTo() is one such method.

OpenCV, bitwise operations

With bitwise operations, the mask would no longer just label each pixel as true or false, but it would have to be 3-channels and all eight bits of every value count, so it must contain only 0 and 255 (0xFF).

I'll erase everything outside of the circle first, then add back the part of the source that is outside of the circle. Both operations use bitwise_and. Two operations are required because bitwise operations can't just "overwrite". They react to both operands. I'll also use numpy's ~ operator to bitwise-negate the mask.

bitwise_mask = cv.cvtColor(mask, cv.COLOR_GRAY2BGR) # blow it up
new = cv.bitwise_and(new, bitwise_mask)
new  = cv.bitwise_and(img, ~bitwise_mask)

enter image description here enter image description here

  •  Tags:  
  • Related