Related
all of you,
Here is the image;
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?
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?
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:
image2:
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)
See also: Finding red color in image using Python & OpenCV - Stack Overflow
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:
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
The first image is the original, the second is the hsv, and the third is the mask.
The yellowest color in the hsv image is between the boundaries set. Why is the whole image turning black?
import numpy as np
import cv2
import imutils
directory = r"C:\\Users\\colin\\Documents\\projects\\dataset\\"
i = 0
for entry in os.scandir(directory):
if (entry.path.endswith(".png")) and (i == 0):
img = cv2.imread(directory + str(entry.name))
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
i += 1
lower_range = np.array([27,132,156])
upper_range = np.array([33,138,162])
mask = cv2.inRange(hsv, lower_range, upper_range)
cv2.imwrite('C:\\Users\\colin\\Documents\\projects\\mask.png',mask)
It's all about your color ranges, you can either change the values manually and randomly or maybe take a look at this color seperator script it can be very helpful hsv color seperator
I have image with kind of light purple image in background and character in dark blue. My goal is to identify text from the image. So I'm trying to remove light purple color from background so that my image will be free of noise, but I can't find the exact color code for that image as it is somewhat different everywhere, so I'm not able to mask image. Here's my code
import numpy as np
from PIL import Image
im = Image.open('capture.png')
im = im.convert('RGBA')
data = np.array(im)
rgb = data[:,:,:3]
color = [27, 49, 89] # Original value to be mask
black = [0,0,0, 255]
white = [255,255,255,255]
mask = np.all(rgb == color, axis = -1)
data[mask] = black
data[np.logical_not(mask)] = white
new_im = Image.fromarray(data)
new_im.save('new_file.png')
So I thought if I can remove color in all particular color range like [R:0-20, G:0-20, B:80-100] maybe that'll will work. Can someone tell me how can i do that.
Any other suggestion to solve this problem will also be appreciated.
Since there seems to be a distinguishable shade from the text and the background, color thresholding should work here. The idea is to convert the image to HSV format then use a lower and upper threshold to generate a binary segmented mask then bitwise-and to extract the text. Here's an implementation using Python OpenCV
Using this lower and upper threshold, we obtain this mask
lower = np.array([0, 120, 0])
upper = np.array([179, 255, 255])
Then we bitwise-and with the original image
Finally we threshold to get a binary image with the foreground text in black and the background in white
import numpy as np
import cv2
# Color threshold
image = cv2.imread('1.png')
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 120, 0])
upper = np.array([179, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
result = cv2.bitwise_and(original,original,mask=mask)
result[mask==0] = (255,255,255)
# Make text black and foreground white
result = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
result = cv2.threshold(result, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)[1]
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey()
You can use this HSV color threshold script to determine the lower and upper thresholds
import cv2
import sys
import numpy as np
def nothing(x):
pass
# Load in image
image = cv2.imread('1.png')
# Create a window
cv2.namedWindow('image')
# create trackbars for color change
cv2.createTrackbar('HMin','image',0,179,nothing) # Hue is from 0-179 for Opencv
cv2.createTrackbar('SMin','image',0,255,nothing)
cv2.createTrackbar('VMin','image',0,255,nothing)
cv2.createTrackbar('HMax','image',0,179,nothing)
cv2.createTrackbar('SMax','image',0,255,nothing)
cv2.createTrackbar('VMax','image',0,255,nothing)
# Set default value for MAX HSV trackbars.
cv2.setTrackbarPos('HMax', 'image', 179)
cv2.setTrackbarPos('SMax', 'image', 255)
cv2.setTrackbarPos('VMax', 'image', 255)
# Initialize to check if HSV min/max value changes
hMin = sMin = vMin = hMax = sMax = vMax = 0
phMin = psMin = pvMin = phMax = psMax = pvMax = 0
output = image
wait_time = 33
while(1):
# get current positions of all trackbars
hMin = cv2.getTrackbarPos('HMin','image')
sMin = cv2.getTrackbarPos('SMin','image')
vMin = cv2.getTrackbarPos('VMin','image')
hMax = cv2.getTrackbarPos('HMax','image')
sMax = cv2.getTrackbarPos('SMax','image')
vMax = cv2.getTrackbarPos('VMax','image')
# Set minimum and max HSV values to display
lower = np.array([hMin, sMin, vMin])
upper = np.array([hMax, sMax, vMax])
# Create HSV Image and threshold into a range.
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
output = cv2.bitwise_and(image,image, mask= mask)
# Print if there is a change in HSV value
if( (phMin != hMin) | (psMin != sMin) | (pvMin != vMin) | (phMax != hMax) | (psMax != sMax) | (pvMax != vMax) ):
print("(hMin = %d , sMin = %d, vMin = %d), (hMax = %d , sMax = %d, vMax = %d)" % (hMin , sMin , vMin, hMax, sMax , vMax))
phMin = hMin
psMin = sMin
pvMin = vMin
phMax = hMax
psMax = sMax
pvMax = vMax
# Display output image
cv2.imshow('image',output)
# Wait longer to prevent freeze for videos.
if cv2.waitKey(wait_time) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
Here is an approach using a pixel array. Pixel arrays are slow, but if speed isn't an issue, they could serve your needs without having to download any outside libraries. Also, pixel arrays are easy to understand.
import pygame
# -- You would load your image as a sprite here. --
# -- But let's create a demonstration sprite instead.--
#
usecolor = (46,12,187,255) # Declare an example color.
sprite = pygame.Surface((10,10)) # Greate a surface. Let us call it a 'sprite'.
sprite.fill(usecolor) # Fill the 'sprite' with our chosen color.
#
# -- Now process the image. --
array = pygame.PixelArray(sprite) # Create a pixel array of the sprite, locking the sprite.
sample = array[5,5] # Sample the integer holding the color values of pixel [5,5]
# We will feed this integer to pygame.Color()
sample_1 = sprite.get_at((5,5)) # Alternately, we can use the .get_at() method.
# Do the same for every pixel, creating a list (an array) of color values.
del array # Then delete the pixel array, unlocking the sprite.
m,r,g,b = pygame.Color(sample) # Note: m is for the alpha value (not used by .Color())
print("\n sample =",sample,"decoded by python.Color() to:")
print(" r >>",r)
print(" g >>",g)
print(" b >>",b)
print("\n or we could use .get_at()")
print(" sample_1 =",sample_1)
print()
exit()
Just test each r,g,b value to see if they fall within some desired range for each color component. Then copy each pixel over to a new surface, replacing all colors that fall within your range with your desired replacement color.
Or you could add, say 75 to each R,G,B color component (if color > 255: color = 255) before placing the pixel in the new image. This would have the effect of fading all colors towards white until the light color is gone. Then you could repeat the process subtracting 75 from each remaining pixel (with component values less than 255) to bring the colors forward again. I doubt any decent captcha is so easily defeated, but there you go.
Fun fun!
I'm trying to select the green color in an image using OpenCV (the method to do it comes from this website. The image I'm treating is :
Here is the code I tried to write.
import cv2
import matplotlib.pyplot as plt
import numpy as np
greenhsv = (60, 255, 255)
green2hsv=(70,100,170)
g_square = np.full((10, 10, 3), greenhsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g_square))
plt.show()
g1_square = np.full((10, 10, 3), green2hsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g1_square))
plt.show()
nucl = cv2.imread('./Pictures/image_nucleation_essai0.png')
nucl = cv2.cvtColor(nucl, cv2.COLOR_BGR2RGB)
plt.imshow(nucl)
plt.show()
hsv_nucl = cv2.cvtColor(nucl, cv2.COLOR_RGB2HSV)
mask = cv2.inRange(hsv_nucl, greenhsv,green2hsv)
result = cv2.bitwise_and(nucl, nucl, mask=mask)
plt.imshow(mask, cmap="gray")
plt.show()
plt.imshow(result)
plt.show()
The result is :
So the mask did not work.
Your color ranges are not quite right yet. Also the variables in the inRange() function are in the wrong order. It's from-to, so the darker color must be first. Change your code to cv2.inRange(hsv_nucl, green2hsv,greenhsv) You can use/tweak the values in the code below, that works.
Result:
With white background:
import numpy as np
import cv2
# load image
img = cv2.imread("Eding.png")
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# set lower and upper color limits
lower_val = np.array([50,100,170])
upper_val = np.array([70,255,255])
# Threshold the HSV image to get only green colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# apply mask to original image - this shows the green with black blackground
only_green = cv2.bitwise_and(img,img, mask= mask)
# create a black image with the dimensions of the input image
background = np.zeros(img.shape, img.dtype)
# invert to create a white image
background = cv2.bitwise_not(background)
# invert the mask that blocks everything except green -
# so now it only blocks the green area's
mask_inv = cv2.bitwise_not(mask)
# apply the inverted mask to the white image,
# so it now has black where the original image had green
masked_bg = cv2.bitwise_and(background,background, mask= mask_inv)
# add the 2 images together. It adds all the pixel values,
# so the result is white background and the the green from the first image
final = cv2.add(only_green, masked_bg)
#show image
cv2.imshow("img", final)
cv2.waitKey(0)
cv2.destroyAllWindows()
How would I take an RGB image in Python and convert it to black and white? Not grayscale, I want each pixel to be either fully black (0, 0, 0) or fully white (255, 255, 255).
Is there any built-in functionality for getting it done in the popular Python image processing libraries? If not, would the best way be just to loop through each pixel, if it's closer to white set it to white, if it's closer to black set it to black?
Scaling to Black and White
Convert to grayscale and then scale to white or black (whichever is closest).
Original:
Result:
Pure Pillow implementation
Install pillow if you haven't already:
$ pip install pillow
Pillow (or PIL) can help you work with images effectively.
from PIL import Image
col = Image.open("cat-tied-icon.png")
gray = col.convert('L')
bw = gray.point(lambda x: 0 if x<128 else 255, '1')
bw.save("result_bw.png")
Alternatively, you can use Pillow with numpy.
Pillow + Numpy Bitmasks Approach
You'll need to install numpy:
$ pip install numpy
Numpy needs a copy of the array to operate on, but the result is the same.
from PIL import Image
import numpy as np
col = Image.open("cat-tied-icon.png")
gray = col.convert('L')
# Let numpy do the heavy lifting for converting pixels to pure black or white
bw = np.asarray(gray).copy()
# Pixel range is 0...255, 256/2 = 128
bw[bw < 128] = 0 # Black
bw[bw >= 128] = 255 # White
# Now we put it back in Pillow/PIL land
imfile = Image.fromarray(bw)
imfile.save("result_bw.png")
Black and White using Pillow, with dithering
Using pillow you can convert it directly to black and white. It will look like it has shades of grey but your brain is tricking you! (Black and white near each other look like grey)
from PIL import Image
image_file = Image.open("cat-tied-icon.png") # open colour image
image_file = image_file.convert('1') # convert image to black and white
image_file.save('/tmp/result.png')
Original:
Converted:
Black and White using Pillow, without dithering
from PIL import Image
image_file = Image.open("cat-tied-icon.png") # open color image
image_file = image_file.convert('1', dither=Image.NONE) # convert image to black and white
image_file.save('/tmp/result.png')
I would suggest converting to grayscale, then simply applying a threshold (halfway, or mean or meadian, if you so choose) to it.
from PIL import Image
col = Image.open('myimage.jpg')
gry = col.convert('L')
grarray = np.asarray(gry)
bw = (grarray > grarray.mean())*255
imshow(bw)
img_rgb = cv2.imread('image.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
(threshi, img_bw) = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
Pillow, with dithering
Using pillow you can convert it directly to black and white. It will look like it has shades of grey but your brain is tricking you! (Black and white near each other look like grey)
from PIL import Image
image_file = Image.open("cat-tied-icon.png") # open colour image
image_file = image_file.convert('1') # convert image to black and white
image_file.save('/tmp/result.png')
Original:
Converted:
And you can use colorsys (in the standard library) to convert rgb to hls and use the lightness value to determine black/white:
import colorsys
# convert rgb values from 0-255 to %
r = 120/255.0
g = 29/255.0
b = 200/255.0
h, l, s = colorsys.rgb_to_hls(r, g, b)
if l >= .5:
# color is lighter
result_rgb = (255, 255, 255)
elif l < .5:
# color is darker
result_rgb = (0,0,0)
Using opencv You can easily convert rgb to binary image
import cv2
%matplotlib inline
import matplotlib.pyplot as plt
from skimage import io
from PIL import Image
import numpy as np
img = io.imread('http://www.bogotobogo.com/Matlab/images/MATLAB_DEMO_IMAGES/football.jpg')
img = cv2.cvtColor(img, cv2.IMREAD_COLOR)
imR=img[:,:,0] #only taking gray channel
print(img.shape)
plt.imshow(imR, cmap=plt.get_cmap('gray'))
#Gray Image
plt.imshow(imR)
plt.title('my picture')
plt.show()
#Histogram Analyze
imgg=imR
hist = cv2.calcHist([imgg],[0],None,[256],[0,256])
plt.hist(imgg.ravel(),256,[0,256])
# show the plotting graph of an image
plt.show()
#Black And White
height,width=imgg.shape
for i in range(0,height):
for j in range(0,width):
if(imgg[i][j]>60):
imgg[i][j]=255
else:
imgg[i][j]=0
plt.imshow(imgg)
Here is the code for creating binary image using opencv-python :
img = cv2.imread('in.jpg',2)
ret, bw_img = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
cv2.imshow("Output - Binary Image",bw_img)
If you don't want to use cv methods for the segmentation and understand what you are doing, treat the RGB image as matrix.
image = mpimg.imread('image_example.png') # your image
R,G,B = image[:,:,0], image[:,:,1], image[:,:,2] # the 3 RGB channels
thresh = [100, 200, 50] # example of triple threshold
# First, create an array of 0's as default value
binary_output = np.zeros_like(R)
# then screen all pixels and change the array based on RGB threshold.
binary_output[(R < thresh[0]) & (G > thresh[1]) & (B < thresh[2])] = 255
The result is an array of 0's and 255's based on a triple condition.