Modifying the alpha value of multiple colours in an image? - python

I was wondering how to change the alpha value of an image (.png or .jpg), only for multiple colours? I have done a some looking around and found some solutions with the PIL module, and I found a partial solution here:
Python: PIL replace a single RGBA color
(Code used from link provided)
import Image
import numpy
im = Image.open('test.png').convert('RGBA')
data = numpy.array(im)
r, g, b, a = data.T
colour_to_keep = (r == 255) & (b == 255) & (g == 255)
data[..., :-1][colour_to_keep.T] = (255, 0, 0)
im2 = Image.fromarray(data)
im2.show()
But after playing around with it, I don't think that it will do what I am attempting to do. This code will replace all except one colour.
I am attempting to set only one "background colour" (in this case, green) of a very simple image to transparent (with alpha = 0).
Can anyone please point me in a direction of how to do this? Thank you!

Related

how to add alpha channel (transparency) to background (not the front image) using numpy

I have an RGB image and after doing processing identify an object in image (which is zeros and non-zero values for the object). The image shows a black background which I want to remove.
I have tried following two methods:
Method 1:
rgba_image = np.insert(
rgb_image,
3, #position in the pixel value [ r, g, b, a <-index [3] ]
255, #change as per requirement
axis=2 #this is the depth where you are inserting this alpha channel into
)
Method 2:
b, g, r = cv2.split(img_numpy)
alpha = np.ones(b.shape, dtype=b.dtype) * 100 #creating a dummy alpha channel image.
rgba_image = cv2.merge((b, g, r, alpha))
Both of these methods do actually change the transparency (but of the whole image). What I am looking for is to have transparent background with only showing the object
Could you please advise?
#MarkSetchell helped me to resolve this. There was an additional bit in my case that i had the data in tensors so following code resolved this:
rgb_numpy = (image * 255).byte().cpu().numpy()
after this point the background is black
mask_alpha = (mask * 255).byte().cpu().numpy()
rgba_numpy = np.dstack((rgb_numpy,mask_alpha))
after this the mask layer is added which automatically highlight the area of the detected object and everything else goes transparent

Replace Color Values in Image with Random Noise

I found something reasonably close to what I want to do here:
Python: PIL replace a single RGBA color
However, in my scenario I have images that were originally grayscale with color annotations added to the image (an x-ray with notes in color). I would like to replace any pixel that is not grayscale with random noise. My main problem is replacing values with noise and not a single color.
Edit: I figured out the random noise part, now just trying to figure out how to separate the color pixels from the pixels that were originally in grayscale.
from PIL import Image
import numpy as np
im = Image.open('test.jpg')
data = np.array(im) # "data" is a height x width x 3 numpy array
red, green, blue = data.T # Temporarily unpack the bands for readability
# Replace white with random noise...
white_areas = (red == 255) & (blue == 255) & (green == 255)
Z = random.random(data[...][white_areas.T].shape)
data[...][white_areas.T] = Z
im2 = Image.fromarray(data)
im2.show()
You could try
col_areas = np.logical_or(np.not_equal(red, blue), np.not_equal(red, green))
You could use this Pixel Editing python module
from PixelMenu import ChangePixels as cp
im = Image.open('test.jpg')
grayscalergb=(128, 128, 128) #RGB value of gray in your image
noise=(100,30,5) #You can adjust the noise based upon your requirements
outputimg=cp(im, col=grayscalergb, col2=noise, save=False,tolerance=100) #Adjust the tolerance until you get the right amount of noise in your image
Also:
I'd suggest you to use png images instead of jpg images because JPEG is designed with compression, everytime you load the image the RGB values change making it hard for your code to function perfectly everytime

Using opencv, how to remove non-object in transparent image?

I'm a newbie in image processing.
I'm trying to resize the rectangle/frame bound my object in transparent image.
But i don't know how to make it.
Please help me.
Thank a lot.
P/s: It doesn't duplicate with crop. In crop you have fix a tuple (Crop from x, y, w, h). But in my picture, I don't know where to crop. We need to detect minimize rectangle that contain my object(sunglass) first and crop then.
First you have to load the image with alpha support in OpenCV
import cv2
import numpy as np #needed in the second step
im = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
Notice the cv2.IMREAD_UNCHANGED, this is equal to -1. This will load an image with the format BGRA
Then you find the bounding rect of the object
# axis 0 is the row(y) and axis(x) 1 is the column
y,x = im[:,:,3].nonzero() # get the nonzero alpha coordinates
minx = np.min(x)
miny = np.min(y)
maxx = np.max(x)
maxy = np.max(y)
Then you crop the object
cropImg = im[miny:maxy, minx:maxx]
Finally you show and save your results to disk
cv2.imwrite("cropped.png", cropImg)
cv2.imshow("cropped", cropImg)
cv2.waitKey(0)
I have no time to test this code, so I may have a typo. I hope it helps you. Any problems, just comment this answer
UPDATE
Here is a small update to remove the extra white part:
First get a boolean mask where it is white
whiteCellsMask = np.logical_and(cropImg[:,:,0] == 255, np.logical_and(cropImg[:,:,1] == 255, cropImg[:,:,2]== 255))
Then change the alpha of the masked values to 0
cropImg[whiteCellsMask,:] = [255, 255, 255, 0]
This will change all the pixels which are white (255,255,255) to transparent (alpha = 0).

Python: PIL replace a single RGBA color

I have already taken a look at this question: SO question and seem to have implemented a very similar technique for replacing a single color including the alpha values:
c = Image.open(f)
c = c.convert("RGBA")
w, h = c.size
cnt = 0
for px in c.getdata():
c.putpixel((int(cnt % w), int(cnt / w)), (255, 0, 0, px[3]))
cnt += 1
However, this is very slow. I found this recipe out on the interwebs, but have not had success using it thus far.
What I am trying to do is take various PNG images that consist of a single color, white. Each pixel is 100% white with various alpha values, including alpha = 0. What I want to do is basically colorize the image with a new set color, for instance #ff0000<00-ff>. SO my starting and resulting images would look like this where the left side is my starting image and the right is my ending image (NOTE: background has been changed to a light gray so you can see it since it is actually transparent and you wouldn't be able to see the dots on the left.)
Any better way to do this?
If you have numpy, it provides a much, much faster way to operate on PIL images.
E.g.:
import Image
import numpy as np
im = Image.open('test.png')
im = im.convert('RGBA')
data = np.array(im) # "data" is a height x width x 4 numpy array
red, green, blue, alpha = data.T # Temporarily unpack the bands for readability
# Replace white with red... (leaves alpha values alone...)
white_areas = (red == 255) & (blue == 255) & (green == 255)
data[..., :-1][white_areas.T] = (255, 0, 0) # Transpose back needed
im2 = Image.fromarray(data)
im2.show()
Edit: It's a slow Monday, so I figured I'd add a couple of examples:
Just to show that it's leaving the alpha values alone, here's the results for a version of your example image with a radial gradient applied to the alpha channel:
Original:
Result:
Try this , in this sample we set the color to black if color is not white .
#!/usr/bin/python
from PIL import Image
import sys
img = Image.open(sys.argv[1])
img = img.convert("RGBA")
pixdata = img.load()
# Clean the background noise, if color != white, then set to black.
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y] == (255, 255, 255, 255):
pixdata[x, y] = (0, 0, 0, 255)
you can use color picker in gimp to absorb the color and see that's rgba color
The Pythonware PIL online book chapter for the Image module stipulates that putpixel() is slow and suggests that it can be sped up by inlining. Or depending on PIL version, using load() instead.

How to invert colors of image with PIL (Python-Imaging)?

I need to convert series of images drawn as white on black background letters to images where white and black are inverted (as negative). How can I achieve this using PIL?
Try the following from the docs: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html
from PIL import Image
import PIL.ImageOps
image = Image.open('your_image.png')
inverted_image = PIL.ImageOps.invert(image)
inverted_image.save('new_name.png')
Note: "The ImageOps module contains a number of 'ready-made' image processing operations. This module is somewhat experimental, and most operators only work on L and RGB images."
If the image is RGBA transparent this will fail... This should work though:
from PIL import Image
import PIL.ImageOps
image = Image.open('your_image.png')
if image.mode == 'RGBA':
r,g,b,a = image.split()
rgb_image = Image.merge('RGB', (r,g,b))
inverted_image = PIL.ImageOps.invert(rgb_image)
r2,g2,b2 = inverted_image.split()
final_transparent_image = Image.merge('RGBA', (r2,g2,b2,a))
final_transparent_image.save('new_file.png')
else:
inverted_image = PIL.ImageOps.invert(image)
inverted_image.save('new_name.png')
For anyone working with an image in "1" mode (i.e., 1-bit pixels, black and white, stored with one pixel per byte -- see docs), you need to convert it into "L" mode before calling PIL.ImageOps.invert.
Thus:
im = im.convert('L')
im = ImageOps.invert(im)
im = im.convert('1')
now ImageOps must be:
PIL.ImageChops.invert(PIL.Image.open(imagepath))
note that this works for me in python 3.8.5
In case someone is inverting a CMYK image, the current implementations of PIL and Pillow don't seem to support this and throw an error. You can, however, easily circumvent this problem by inverting your image's individual bands using this handy function (essentially an extension of Greg Sadetsky's post above):
def CMYKInvert(img) :
return Image.merge(img.mode, [ImageOps.invert(b.convert('L')) for b in img.split()])
Of course ImageOps does its job well, but unfortunately it can't work with some modes like 'RGBA'. This code will solve this problem.
def invert(image: Image.Image) -> Image.Image:
drawer = ImageDraw.Draw(image)
pixels = image.load()
for x in range(image.size[0]):
for y in range(image.size[1]):
data = pixels[x, y]
if data != (0, 0, 0, 0) and isinstance(data, tuple):
drawer.point((x, y), (255 - data[0], 255 - data[1], 255 - data[2], data[3]))
return image
from PIL import Image
img = Image.open("archive.extension")
pixels = img.load()
for i in range(img.size[0]):
for j in range(img.size[1]):
x,y,z = pixels[i,j][0],pixels[i,j][1],pixels[i,j][2]
x,y,z = abs(x-255), abs(y-255), abs(z-255)
pixels[i,j] = (x,y,z)
img.show()
`

Categories