Remove Image background and create a transparent Image using Python's PIL - python

I'm working on a project in which I need to remove the background of an Image, the only Information we have is that it's an Image which has some (one or more) objects in it, and I need to remove the background and make it a transparent image.
Here's a sample Image:
And, here's what I have tried using PIL:
img = Image.open(url)
img = img.convert("RGBA")
datas = img.getdata()
print('Old Length is: {}'.format(len(datas)))
# print('Exisitng Data is as: {}'.format(datas))
newData = []
for item in datas:
# print(item)
if item[0] == 255 and item[1] == 255 and item[2] == 255:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
img.putdata(newData)
print('New Length is: {}'.format(len(datas)))
img.show()
img.save("/Users/abdul/PycharmProjects/ImgSeg/img/new.png", "PNG")
print('Done')
It saves the same image as input with the name as new.png, nothing has been removed from the image.
When I printed the datas and newData it prints the same values:
Old Length is: 944812
New Length is: 944812
Thanks in advance!

You are filtering out all white pixels:
item[0] == 255 and item[1] == 255 and item[2] == 255
but that does not mean that:
all white pixels (255, 255, 255) belong to the background and
all background contains only white pixels.
A heuristic method (partially applicable to your sample image) would be to increase the threshold of your background pixel definition:
if 50 <= item[0] <= 80 and 60 <= item[1] <= 100 and 80 <= item[2] < 140:
filters out much more pixels.
Do you really want your background pixels being white is also a question to be answered.
Also, your test for checking the output of your filtering won't work since both images will contain the same number of pixels anyway, regardless of their transparency.

Related

Python OpenCV image editing: Faster way to edit pixels

Using python (openCV2, tkinter etc) I've created an app (a very amateur one) to change blue pixels to white. The images are high quality jpgs or PNGS.
The process: Search every pixel of an image and if the 'b' value of BGR is higher than x, set pixel to white (255, 255, 255).
The problem: There are about 150 pictures to process at a time, so the above process takes quite long. It's around 9 - 15 seconds per iteration depending on the images size (resizing the image speeds up the process, but not ideal).
Here is the code (with GUI and exception handling elements removed for simplicity):
for filename in listdir(sourcefolder):
# Read image and set variables
frame = imread(sourcefolder+"/"+filename)
rows = frame.shape[0]
cols = frame.shape[1]
# Search pixels. If blue, set to white.
for i in range(0,rows):
for j in range(0,cols):
if frame.item(i,j,0) > 155:
frame.itemset((i,j,0),255)
frame.itemset((i,j,1),255)
frame.itemset((i,j,2),255)
imwrite(sourcecopy+"/"+filename, frame)
#release image from memory
del frame
Any help on increasing efficiency / speed would be greatly appreciated!
Start with this image:
Then use this:
import cv2
im = cv2.imread('a.png')
# Make all pixels where Blue > 150 into white
im[im[...,0]>150] = [255,255,255]
# Save result
cv2.imwrite('result.png', im)
Use cv2.threshold to create a mask using x threshold value.
Set the color like this : img_bgr[mask == 255] = [255, 0, 0]

python black and white image detection

I am trying to identify if an image is black and white or a color image using Open CV in python language. i have created a black and white image using MS paint to check the same. even though the image is black white it still has RGB values other than 0 and 255.
below is the code i have used and images i have used. the output i am getting is color image.
I checked the RGB values they have values other than 0 and 255, i am not able to debug why, can some one help me with this?
img = cv2.imread('C:/Comp_vision/image_data/black_and_white.jpg')
image_pixel =img.flatten()
bnw_cnt = sum(np.where((image_pixel == 0) | (image_pixel == 255), 1, 0))
if np.vectorize(bnw_cnt) == np.vectorize(image_pixel):
print("Black and white image")
else:
print ("Color image")
Image will have black-white colors if and only if for given pixel (x,y) values on each channel will be equal.
For example:
def check_gray(img):
for x in range(img.shape[0])
for y in range(img.shape[1])
b, g, r == img[x,y]
if not(b == g == r):
return False
return True

How to compose an image with an alpha channel over another image(RGB, without alpha channel)? Using python PIL

My question is similar as this one:
With the Python Imaging Library (PIL), how does one compose an image with an alpha channel over another image?
I have two images, the top image with alpha channels and the bottom one without. I want to put top image over the bottom one, resulting in a new image , just as would occur if they were rendered in layers. I would like to do this with the Python PIL. Any suggestion would be appreciable, thanks!
Simply extend your RGB image to RGBA with A set to "1":
rgba = np.dstack((rgb, np.ones(rgb.shape[:-1])))
and then use the compose method you mentioned.
If you use Pillow instead you can simply use:
imRGB.putalpha(alpha)
composite = PIL.Image.alpha_composite(imRGB, im2RGBA)
I have solved my issue by myself, the problem is that the value of alpha channel in RGBA image is either 0 or 255, I just change the 255 to 220, so that the top image won't cover the bottom image. My code is as follow:
def transPNG(srcImageName, dstImageName):
img = Image.open(srcImageName)
img = img.convert("RGBA")
datas = img.getdata()
newData = list()
for item in datas:
if item[0] > 200 and item[1] > 200 and item[2] > 200:
newData.append(( 255, 255, 255, 0))
else:
newData.append((item[0], item[1], item[2], randint(220, 220)))
img.putdata(newData)
img.save(dstImageName,"PNG")

PIL transparency doesn't seem to work

I'm trying to make the black pixels in the background of an image transparent. For this, I wrote the function below. I then overlay the image (a car in the center surrounded by black pixels) on a copy of itself and move the first one slowly. I was expecting the first image to reveal the copy underneath without showing the rectangular boundary since that part should have been transparent. However, I don't get the desired effect. Does anyone know what I'm missing?
def makeImageTransparent(img):
img = img.convert("RGBA")
datas = img.getdata()
newData = []
ii = 0
for item in datas:
if item[0] == 0 and item[1] == 0 and item[2] == 0:
newData.append((0, 0, 0, 0))
ii = ii + 1
else:
newData.append(item)
print str(ii)
img.putdata(newData)
return img
I couldn't figure out how to make the transparency work, so I just created my own paste method that updated the bytes of the image I was pasting to directly and this got me the desired effect.
def pasteImage(img, bigim, posn):
pixdata = img.load()
width, height = img.size
mainpixdata = bigim.load()
for y in xrange(height):
for x in xrange(width):
if pixdata[x, y] != (0, 0, 0, 0):
mainpixdata[x+posn[0], y+posn[1]] = pixdata[x,y]

Residual white pixels in transparent background from PIL

I used this following code from another stackoverflow post
from PIL import Image as image
img = image.open('output.png')
img = img.convert("RGBA")
datas = img.getdata()
newData = []
for item in datas:
if item[0] == 255 and item[1] == 255 and item[2] == 255:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
img.putdata(newData)
img.save("img2.png", "PNG")
to transform my png's background to transparent. However, when I tried to add some shapes in powerpoint underneath the transparent image, it still has some residual white pixels left. Anyone know how to solve this?
Those pixels are not exactly "white". The color you are testing against and removing from the image is, with its value of #FFFFFF. But those slanted lines are heavily antialiased, "fading" from the pure white of the background to the pure color of the center of the lines.
This can be seen when zooming in just a teeny bit:
You can lower the threshold of when to make a pixel entirely transparent:
if item[0] > 240 and item[1] > 240 and item[2] > 240:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
but no matter how much you do this, you will always end up with either visibly lighter pixels around the lines, or – when only matching the center "line" color exactly – with disconnected pixels, not resembling the original lines anymore.
But there is no reason to use a Yes/No mask with PNG images! PNG supports full 8-bit transparency, and so you can make the 'solid' center lines fully opaque, the solid white fully transparent, and have the gradually darkening pixels fade between these values.
This works best if you know the exact original color that was used to draw the lines with. Measuring it with Adobe PhotoShop, I get something like #818695. Plugging in these values into your program and adjusting the 'tint' (towards white) to transparency, flattened towards the full possible range, I suggest this code:
from PIL import Image as image
img = image.open('input.png')
img = img.convert("RGBA")
datas = img.getdata()
retain = (0x81,0x86,0x95)
retain_gray = (39*retain[0] + 50*retain[1] + 11*retain[2])
newData = []
for item in datas:
if item[0] > retain[0] and item[1] > retain[1] and item[2] > retain[2]:
# convert to grayscale
val = 39*item[0] + 50*item[1] + 11*item[2]
# invert
val = 25500 - val;
# difference with 'retain'
val = retain_gray - val
# scale down
val = 255*val/retain_gray
# invert to act as transparency
transp = 255-val
# apply transparency to original 'full' color value
newData.append((retain[0], retain[1], retain[2], transp ))
else:
newData.append(item)
img.putdata(newData)
img.save("output.png", "PNG")
print "done"
What it essentially does is converting the input image to grayscale, scaling it (because the scale from darkest to lightest should be in the full transparency range of 0..255), then using this as the 'transparent' byte. The result is way better than your on/off approach:

Categories