Home > Software engineering >  Can anyone tell me how can I change mask the foreground if I know the color range of background in R
Can anyone tell me how can I change mask the foreground if I know the color range of background in R

Time:10-25

all of you,

Here is the image; enter image description here

The exact background color is in RGB; (246, 46, 100)

I have tried several methods but those are too slow, one of the methods is below;

new_image = Image.open("image-from-rawpixel-id-6649116-original.png")
img_up = np.asarray(new_image)

for ind1, i in enumerate(tqdm(img_up)):
    for ind2, i2 in enumerate(i):
        if list(i2[:3]) != a:
            img_up2 = img_up.copy()
            img_up2.setflags(write=1)
            img_up2[ind1][ind2][:3] = [0,0,0]

cv2.imshow('', img_up2)

cv2.waitKey()

I want to make the background white and the foreground person black (masked), but unable to find a quick method.

CodePudding user response:

First you could read image using cv2.imread() and you get directly numpy.array.

You can use numpy image[ mask ] = [0,0,0] to assign value to many pixels in milliseconds.

For exact color you can create mask using img == (100, 46, 247).

cv2 keeps image as BGR instead of RGB so it needs (100,46,247) instead of (247,46,100).

It needs also .all(axis=-1) because it compares every value B,G,R separatelly and gets tuples (True, True, False) but it needs to reduce it to single True when all values are True.

import cv2

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

#print('color:', img[0, 0])  # [100  46 247]

mask = (img == (100, 46, 247)).all(axis=-1)

img1 = img.copy()
img2 = img.copy()

img1[  mask ] = [0,0,0]
img2[ ~mask ] = [0,0,0]

cv2.imshow('image1', img1)
cv2.imshow('image2', img2)
cv2.waitKey()
cv2.destroyAllWindows()

cv2.imwrite('image2-1.png', img1)
cv2.imwrite('image2-2.png', img2)


Result:

image1:

enter image description here

image2:

enter image description here


BTW:

cv2 has function inRange() to select colors in some ranges and it may give better result.

Example code but I didn't find good range for this image.

import cv2
import numpy as np

img = cv2.imread("image.jpg")

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#print('hsv:', hsv[0, 0])

lower = np.array([92, 120, 147])
upper = np.array([172, 208, 247])  # for BGR (100, 46, 247) - value from hsv[0,0]
upper = np.array([202, 218, 247])

mask = cv2.inRange(hsv, lower, upper)

print('masking')
# `mask==255` `mask==0`
#img = cv2.bitwise_and(img, img, mask=~mask)  # OK
#img[np.where(mask==255)] = [0,0,0]           # OK 
img[ mask==255 ] = [0,0,0]                    # OK
#img[ mask.astype(bool) ] = [0,0,0]           # OK
#img[ mask ] = [0,0,0]                        # WRONG (hangs)

print('display')
#h, w = img.shape[:2]
#img = cv2.resize(img, (h//5, w//5))
cv2.imshow('image', img)
cv2.waitKey()
cv2.destroyAllWindows()

cv2.imwrite('image2b.jpg', img)

enter image description here

See also: enter image description here


I actually did that with ImageMagick equivalent operator in Terminal as I don't currently have Python to hand:

magick IXVJl.jpg -fuzz 30% -fill white -draw "color 0,0 floodfill" result.jpg
  • Related