How to get pixel values of image in rgba format? - python

I'm trying to get the rgba values of the pixels of an image.
Google suggests I use code similar to this:
from PIL import Image
im = Image.open("C:/Stuff/image.png", "r")
px = list(im.getdata())
My problem is the data not always being in rgba format.
On some images it does return rgba
[(0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 255, 255), [...]
while on others it returns rgb
[(0, 0, 0), (0, 0, 0), (0, 0, 255), [...]
and on some it returns whatever this is
[0, 0, 1, [...]
Is there a way to always get rgba returned?

Related

Delete Rasters that contain pixel values of Zero

I have a working code that will iterate through a folder, identify and delete if a .tif only contains pixel values of all zero, hence a black image. The problem is that I have 12,000 images in the folder and it take quite a long time for the process to finish. I am wondering if there is a faster way I could do this?
from PIL import Image
import os
directory = 'D:/images/'
for image in os.listdir(directory):
indiv = Image.open(directory + image)
pixel_values = list(indiv.getdata())
y = len(pixel_values)
list_yes = []
for RGBA in pixel_values:
if RGBA == (0, 0, 0, 0):
Black_image = 'yes'
list_yes.append(Black_image)
x = len(list_yes)
if x == y:
os.remove(directory + image)
Output of black .tif:
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
(0, 0, 0, 0)
....
Like 400,000 more rows of this
This should be substantially faster
directory = 'D:/images/'
for image in os.listdir(directory):
indiv = Image.open(directory + image)
if all(pixels == (0, 0, 0, 0) for pixels in list(indiv.getdata())):
os.remove(directory + image)
I'm not sure the list(...) is needed either, I'm not too familiar with PIL. If it works without, removing it should cause another speedup.

How can I construct a PIL Image from an array of RGBA pixels?

My goal is to use PIL to extract some details from an image, effectively cropping it down.
For this, I use Image.getdata() to get a list of the pixels in the image, since checking and modifying this is easier for me.
After all the changes I made, I am left with an array of pixels represented in tuples. For simplicity, an array like that could look like this:
new_pixels = [
(255, 0, 0, 255),
(0, 255, 0, 255),
(0, 0, 255, 255),
(0, 0, 0, 255)
]
I've seen something interesting in the PIL documentation, namely the fromarray classmethod, however passing the array to this function gives an error message:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python39\lib\site-packages\PIL\Image.py", line 2741, in fromarray
arr = obj.__array_interface__
AttributeError: 'list' object has no attribute '__array_interface__'
Trying the same with a two dimensional list gives the same result.
The question finally is, how would I go about turning this array into a PIL Image object that I can later save?
not sure if thats what you are looking for:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Feb 4 17:10:02 2021
#author: Pietro
"""
from PIL import Image
import numpy as np
new_pixels = np.array([
(255, 0, 0, 255),
(0, 255, 0, 255),
(0, 0, 255, 255),
(0, 0, 0, 255)
]).astype('uint8')
new_pixelsRGBA = np.array([[
[255, 0, 0, 255],
[0, 255, 0, 255],
[0, 0, 255, 255],
[0, 0, 0, 255]]
]).astype('uint8')
new_pixelsRGBA2 = np.array([[
[255, 0, 0, 255],
[0, 255, 0, 255]],
[[0, 0, 255, 255],
[0, 0, 0, 255]]
]).astype('uint8')
pippo = Image.fromarray(new_pixels)
pippoRGBA = Image.fromarray(new_pixelsRGBA, mode='RGBA')
# pippoRGBA = Image.fromarray(new_pixelsRGBA)
print('pippo image size : ', pippo.size)
print('pippo image mode : ', pippo.mode)
pippo.show()
print('pippoRGBA image size : ', pippoRGBA.size)
print('pippoRGBA image mode : ', pippoRGBA.mode)
pippoRGBA.show()
pippoRGBA2 = Image.fromarray(new_pixelsRGBA2)
print('pippoRGBA2 image size : ', pippoRGBA2.size)
print('pippoRGBA2 image mode : ', pippoRGBA2.mode)
pippoRGBA2.show()
the image I got is:
pippo image size : (4, 4)
pippo image mode : L : (8-bit pixels, black and white)
Apparently your array is not a RGBA pixel array ?! Or not ?
using my new_imageRGBA or new_imageRGBA2 array see above I got:
pippoRGBA image size : (4, 1)
pippoRGBA image mode : RGBA
or (pippoRGBA2 image):
pippoRGBA2 image size : (2, 2)
pippoRGBA2 image mode : RGBA
note that :
pippoRGBA = Image.fromarray(new_pixelsRGBA)
works as well; PIL knows we are talking about RGBA array
u can use matplotlib here
import matplotlib.pyplot as plt
plt.imshow(img_pixels)
plt.show()

How to pad a RGB image with RGB values using numpy.pad

I'm trying to pad a RGB image with magenta (255, 0, 255) color with np.pad. But I'm getting an error when using RGB values as constant_values. For example:
import numpy as np
from scipy.misc import face
import matplotlib.pyplot as plt
def pad_img(img, pad_with):
pad_value = max(img.shape[:-1])
img_padded = np.pad(img,
((0, (pad_value - img.shape[0])), # pad bottom
(0, (pad_value - img.shape[1])), # pad right
(0, 0)), # don't pad channels
mode='constant',
constant_values=pad_with)
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.imshow(img)
ax2.imshow(img_padded)
plt.show()
This works fine (padding with white color):
img = face()
pad_img(img, pad_with=255)
And this not (padding with magenta):
img = face()
pad_img(img, pad_with=(255, 0, 255))
Throwing:
ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (3,) and requested shape (3,2)
I think what you are looking for is:
img = face()
pad_img(img, pad_with=(((255, 0, 255), (255, 0, 255)), ((255, 0, 255), (255, 0, 255)), (0, 0)))
According to numpy doc constant_values is of form:
((before_1, after_1), ... (before_N, after_N))
And I think that is why the error says it gets shape (3,) ((255, 0, 255)) for pad_width while it requests shape (3,2) ((((255, 0, 255), (255, 0, 255)), ((255, 0, 255), (255, 0, 255)), (0, 0)))

RGB color codes in Semantic Segmentation

I am using the Semantic Segmentation network (SegNet). I am trying to reduce the number of classes and thus rearranging the network.
Therefore, I am also changing the color-coding of the predictions as well. My problem is I don't get the intended colors in the output image.
For e.g.
pascal_palette = np.array([(0, 0, 0),
(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
(0, 0, 128), (0, 128, 0), (0, 0, 0), (0, 0, 0), (128, 0, 0),
(0, 0, 0), (0, 0, 0)
], dtype=np.uint8)
The above line gives perfect results for the three classes as the pixels are only in 1 channel.
The output is as below:
However, if I modify the line and add values to different channels it gives weird output. The output is attached below:
pascal_palette = np.array([(0, 0, 0),
(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0),
(0, 0, 128), (124, 252, 0), (0, 0, 0), (0, 0, 0), (128, 0, 0),
(0, 0, 0), (0, 0, 0)
], dtype=np.uint8)
Changed the color code to (124, 252, 0). The code should be for lawn green color. I also checked it on a website like RBG codes
What am I missing here? Any explanation will be helpful.
Prediciton code:
prob = model.predict(net_in)[0]
# Reshape to 2d here since the networks outputs a flat array per channel
prob_edge = np.sqrt(prob.shape[0]).astype(np.int)
prob = prob.reshape((prob_edge, prob_edge, 13))
# Upsample
if args.zoom > 1:
prob = interp_map(prob, args.zoom, image_size[1], image_size[0])
# Recover the most likely prediction (actual segment class)
prediction = np.argmax(prob, axis=2)
# Apply the color palette to the segmented image
color_image = np.array(pascal_palette)[prediction.ravel()].reshape(
prediction.shape + (3,))
print('Saving results to: ', args.output_path)
with open(args.output_path, 'wb') as out_file:
Image.fromarray(np.multiply(color_image,255)).save(out_file)
PS. I have used same model for predictions in both case
The problem is very probably in np.multiply(color_image,255).
As you created a pallete already with values from 0 to 255 and you're simply gathering values from this pallete, you don't need to multiply it by 255.
Use simply Image.fromarray(color_image).save(out_file).

PIL's Image.frombuffer creates wrong image

I'm trying to create an image from 1d numpy array of integers so that changes to this array reflects in the image. It seems that Image.frombuffer perfectly fits my needs. There's my attempts:
from PIL import Image
import numpy as np
data = np.full(100, 255, dtype = np.int32)
img = Image.frombuffer('RGB', (10, 10), data)
print(list(img.getdata()))
I expected to see a list of 100 tuples (0, 0, 255). But what I'm actually getting is (0, 0, 255), (0, 0, 0), (0, 0, 0), (0, 255, 0), (0, 0, 0), (0, 0, 0), (255, 0, 0), (0, 0, 0), (0, 0, 255), (0, 0, 0), (255, 0, 0), ...
What is the reason of that behavior?
'RGB' uses three bytes per pixel. The buffer that you provided is an array with data type numpy.int32, which uses four bytes per element. So you have a mismatch.
One way to handle it is to use mode 'RGBA':
img = Image.frombuffer('RGBA', (10, 10), data)
Whether or not that is a good solution depends on what you are going to do with the image.
Also note that whether you get (255, 0, 0, 0) or (0, 0, 0, 255) for the RGBA pixels depends on the endianess of the integers in data.
For an RGB image, here's an alternative:
data = np.zeros(300, dtype=np.uint8)
# Set the blue channel to 255.
data[2::3] = 255
img = Image.frombuffer('RGB', (10, 10), data)
Without more context for the problem, I don't know if that is useful for you.

Categories

Resources