0

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.

Modified

I have tied another method to mask the foreground but I think, I am doing some mistakes while converting between RGBs. Below is the code;

path = 'image-from-rawpixel-id-2923073-png.png'

im = Image.open(path).convert('RGBA')

img = cv2.imread(path, cv2.IMREAD_UNCHANGED)


#--------------------------- To change the background color that is not present in the foreground ---------------------------------------------
lst_ch_a = []
for pixel in tqdm(im.getdata()):
    lst_ch_a.append(pixel[:3])

break_out = True
while break_out:
    a = random.sample(range(0, 255), 3)
    if a not in lst_ch_a:
        new_image = Image.new("RGBA", im.size, tuple(a))
        print(tuple(a))
        break_out = False

new_image.paste(im, mask=im)
new_image.convert("RGB").save("check6.jpg")

#--------------------------- Numpy ----------------------------------------------------------------
img_up = np.asarray(new_image)

img = img_up.copy()
img.setflags(write=1)
img[:,:,:3][img[:,:,:3] != tuple(a)] = 0
img[img!=0]=255

img = cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA)
img = cv2.resize(img,(500,500), cv2.INTER_LINEAR)

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

cv2.imshow('', img)
cv2.waitKey(0)

Below is the result, but I am getting pixels of Blue, green, red, and some other colors in the image. do you know why I am getting this?

enter image description here

You can see in the above first image, where I have changed the background. The image was transparent image, but then I changed the background color. There was no green, blue, or red colors but while masking the foreground the red, blue, and green color emerges.

Do you know why it is happening?

  • maybe you should numpy's function for this. Something like `array[ array == (246, 46, 100) ] = [0, 0, 0]` - without `for`-loops. – furas Oct 24 '22 at 20:33
  • Why do you copy image in every loop? Do it only once. And sometimes the bigest problem makes displaying information. Code may work faster without `tqdm` – furas Oct 24 '22 at 20:36
  • if you use `cv` then you could use `cv2.imread()` and you get directly `numpy.array`. But it may need to convert colors because `cv2` keeps image as `BGR` instead of `RGB` – furas Oct 24 '22 at 20:40
  • [Finding red color in image using Python & OpenCV - Stack Overflow](https://stackoverflow.com/questions/30331944/finding-red-color-in-image-using-python-opencv) – furas Oct 24 '22 at 21:10
  • 1
    @furas it's simpler and faster to reverse the RGB order of the color you're searching for rather than the entire image. – Mark Ransom Oct 24 '22 at 23:35
  • I have modified my image, please check, and response to me accordingly. – Tariq Hussain Oct 25 '22 at 11:49
  • Maybe first use `print()` (and `print(type(...))`, `print(len(...))`, etc.) to see which part of code is executed and what you really have in variables. It is called `"print debuging"` and it helps to see what code is really doing. – furas Oct 25 '22 at 11:51

2 Answers2

3

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.

Besides it starts removing similar pixels in lips.

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: Finding red color in image using Python & OpenCV - Stack Overflow

furas
  • 134,197
  • 12
  • 106
  • 148
  • If we know the exact background color then why it is selecting the outer part pink? – Tariq Hussain Oct 24 '22 at 21:05
  • some pixels don't have exact color (especially if image was saved as JPG). And it would need `inRang()` to select color in some range. See also [Finding red color in image using Python & OpenCV - Stack Overflow](https://stackoverflow.com/questions/30331944/finding-red-color-in-image-using-python-opencv) – furas Oct 24 '22 at 21:11
  • I know this method but that is not the more accurate, but thanks to you for helping me with this. I think I need to work on another solution. – Tariq Hussain Oct 25 '22 at 11:37
  • I have modified my image, please check, and response to me accordingly. – Tariq Hussain Oct 25 '22 at 11:49
2

You should be able to do this more effectively with PIL.ImageDraw.floodfill() which is described here.

If you use a global replacement, you will catch reddish tones (such as the model's lips) anywhere and everywhere they occur in the image. If you use a floodfill from the top-left corner with a suitable tolerance, the flooding should get stopped by her dark hair before it can contaminate her lips.

The result should be like this:

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
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432