Reputation: 147
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?
Upvotes: 1
Views: 319
Reputation: 22457
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:
Upvotes: 3