Saving list as an image in python - python

I am trying to save a 2D list as an image in python (greyscale image) so 0 values in the array would be black and 255 would be white. For example:
255 255 255
255 0 255
255 0 255
255 0 255
255 255 255
Would save an l like shape.
I have tried the following code utilising the PIL library as suggested by other questions on stack overflow:
WIDTH, HEIGHT = img.size
imgData = list(img.getdata())
imgData = [imgData[offset:offset + WIDTH] for offset in range(0, WIDTH * HEIGHT, WIDTH)]
#to print the image
for row in data:
print(' '.join('{:3}'.format(value) for value in row))
imgData = np.array(imgData)
**IMG VALUES AUGMENTED HERE**
newimg = Image.new('L', (WIDTH, HEIGHT), 'white')
newimg.putdata(imgData)
newimg.save('C:/File/Name.png')
However the image this creates does not reflect the list at all. If I was to have the 0s and 255s in different positions the same image is created. Anyone know a solution?

As your example is lacking any input data, I have just typed it in as you describe, made the image and then enlarged it. I also artificially added a red border so you can see the extent of it on StackOverflow's white background:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
pixels = [[255,255,255],
[255,0,255],
[255,0,255],
[255,0,255],
[255,255,255]]
# Make list of pixels into Image
im = Image.fromarray(np.array(pixels,dtype=np.uint8))
im.save('result.png')

Instead of:
newimg.putdata(imgData)
you need the line:
newimg.putdata([j[0] for i in imgData for j in i])
The grayscale data is specified in a 1d list, not a 2d list.
This creates the list:
>>> [j[0] for i in imgData for j in i]
[255, 255, 255, 255, 0, 255, 255, 0, 255, 255, 0, 255, 255, 255, 255]
Which is:
[255, 255, 255,
255, 0 , 255,
255, 0 , 255,
255, 0 , 255,
255, 255, 255]
EDIT
The above solution works if you edit imgData with imgData[0][0] = [0, 0, 0, 255]. If you're editing imgData with imgData[0][0] = 0, then you'll need the line to be:
[j[0] if hasattr(j, '__iter__') else j for i in imgData for j in i]
or, you can make it nicer with:
imgData = np.array([[j[0] for j in i] for i in imgData])
imgData[0][0] = 0
newimg.putdata(imgData.flatten())

Related

Is there a way to make a square pattern mask that matches the size of my image in python?

I was wondering if there is a way to make an array/column like [255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255....] that repeats until a certain length (length of a imported image) for every row/array. The goal of this will be to make an image that will show 255 as white "pixels" and 0 as the black "pixels". Is this possible?
proposed mask concept
final result
import numpy as np
from skimage import data
from skimage import io
path = r'C:\C:\Python36\bmp_files\slide_3.png'
sz = 400
image = data.astronaut()
patch1 = image[250:250+sz,250:250+sz,:]
patch1.shape
#(48, 48, 3)
mask = np.tile(np.array([[[1],[0]],[[0],[0]]],dtype=np.uint8),(sz/2,sz/2,3))
mask.shape
#(48, 48, 3)
print (mask)
patch2 = patch1 * mask
patch12 = np.hstack((patch1,patch2))`

How to convert binary image to rgba image with transparent background in opencv

Super simple question, I have a single channel image consisting of 0's and 255's and I would like to convert it into a 4 channel image of [0,0,0,0] and [255,255,255,255] in the most efficient manner possible. cv2.COLOR_GRAY2RGBA causes the alpha to all be at 255 and np.where() feels like black magic to me so any help would be appreciated.
One-liner solution using cv2.merge function.
Let your input image be "img" which is of 1 channel.
To convert it to 4 channel, do this:
img = cv2.merge((img.copy(), img.copy(), img.copy(), img.copy()))
Now the "img" will contain 4 channels and all the channels will be the same wrt values.
Just create a numpy array with more layers and then change the values of this array to the values of your image.
img = cv2.imread('img.jpg')
h, w, d = img.shape #(217, 232, 3)
nr_of_new_layers = 1
alpha_img = np.zeros((h, w, d+nr_of_new_layers))
alpha_img[:,:,:3] = img #alpha_img shape = (217, 232, 4)
Or if there isn't any 3rd dimension then you can do
d = 0
img = cv2.imread('img.jpg', d)
h, w = img.shape #(217, 232)
nr_of_new_layers = 4
alpha_img = np.zeros((h, w, d+nr_of_new_layers))
alpha_img[:,:,d] = img #alpha_img shape = (217, 232, 4)
Just stack 4 copies of your single channel image with np.dstack():
RGBA = np.dstack((im,im,im,im)) # or more tersely RGBA = np.dstack(([im]*4))
Example:
import numpy as np
# Make image
im = np.array([0,255,255,0,0], dtype=np.uint8)
Look at it:
array([ 0, 255, 255, 0, 0], dtype=uint8)
Stack it depth-wise with np.dstack():
RGBA = np.dstack((im,im,im,im))
Look at it:
array([[[ 0, 0, 0, 0],
[255, 255, 255, 255],
[255, 255, 255, 255],
[ 0, 0, 0, 0],
[ 0, 0, 0, 0]]], dtype=uint8)

Python 3.7 + PIL How can I change pixel shading to equivalent alpha transparency level 0-255

Unable figure out how to convert pixel color list to equivalent alpha transparency in white.
pixel_data = list(img.getdata())
print(pixel_data)
alpha_range = range(0, 256) # increase count
color_range = range(256, -1, -1) # decrease count
for i, pixel in enumerate(pixel_data):
if pixel[:3] == (255, 255, 255):
pixel_data[i] = (255, 255, 255, 0)
img.putdata(pixel_data)
print(list(pixel_data))
I expect to parse a grey scale image's pixels then convert to equivalent alpha transparency level:
.R., .G., .B. = .R., .G., .B., Alpha
255, 255, 255 = 255, 255, 255, 0
254, 254, 254 = 255, 255, 255, 1
253, 253, 253 = 255, 255, 255, 2
252, 252, 252 = 255, 255, 255, 3
..., ..., ... = ..., ..., ..., 4
..., ..., ... = ..., ..., ..., 5
..., ..., ... = ..., ..., ..., 6
..., ..., ... = ..., ..., ..., .
..., ..., ... = ..., ..., ..., 255
I'd like the end result to be all white and shaded with matching levels of opacity(alpha transparency)
Provided image to show what the white end result should be like. Blue background for easy viewing:
I think you mean this:
import numpy as np
from PIL import Image
# Open image and ensure it has an alpha channel
im = Image.open('image.png').convert('RGBA')
# Make into Numpy array, shape [h,w,4]
ni = np.array(im)
# Set the alpha channel of each pixel to "255 - red"
ni[...,3] = 255 - ni[...,0]
# Set the RGB of each pixel to white (255,255,255)
ni[:,:,(0,1,2)] = 255
# Make back into PIL Image and save
Image.fromarray(ni).save('result.png')
If, for some reason, you object to Numpy, you can do it in straight PIL/Pillow like this:
#!/usr/bin/env python3
from PIL import Image
# Open image, ensuring in RGB mode, rather than palette or greyscale
im = Image.open('image.png').convert('RGB')
# Extract Red channel
R, _, _ = im.split()
# Generate Alpha channel as (255-Red)
Alpha = R.point(lambda p: 255-p)
# Fill original image with white
im.paste((255,255,255),box=(0,0,im.width,im.height))
# Add in alpha channel
im.putalpha(Alpha)
# Save
im.save('result.png')
Was able to add a few more bits to Mark's code and achieve the exact results I wanted. Thank you!
#!/usr/bin/env python3
from PIL import Image
# Open image
img = Image.open('firefox.png').convert('RGBA')
img2 = img.copy()
# Extract Red channel
r, g, b, a = img.split()
# Generate Alpha channel as (255-Red)
alpha_r = r.point(lambda p: 255 - p)
alpha_g = g.point(lambda p: 255 - p)
alpha_b = b.point(lambda p: 255 - p)
alpha_a = b.point(lambda p: 255 - p)
# Fill original image with white
img.paste((255, 255, 255), box=(0, 0, img.width, img.height))
# Add in alpha channel
img.putalpha(alpha_r), img.putalpha(alpha_g), img.putalpha(alpha_b), img.putalpha(alpha_a)
# crop out white area
img = Image.composite(img, img2, img2)
# Save
img.save('result.png')

How to get the pixel indices of a specific RGB value of a image in python?

I have imported a color image using openCV imread function.
im = cv2.imread('test.jpg')
I am looking to find the indices of white pixels, which have a pixel value of [255, 255, 255]. I know im is actually a 3D array. But what is weird is the value of im[0,0] is [255 255 255], rather than [255, 255, 255]. And im[0,0,0] is 255. So [255 255 255] seems like a list or something, but actually not equivalent to [255, 255, 255].
>>> print im[0,0]
[255 255 255]
>>> print im[0,0,0]
255
So my questions are:
What is the difference between [255 255 255] and [255, 255, 255]?
How can I get the indices of all withe pixels? What search algorithm should I use?
If you want to get the indices of all white pixels, you can convert your image into a 2D array and then find the indices
import operator
image = cv2.imread("test.jpg")
img = image.astype('float')
img = img[:,:,0] # convert to 2D array
row, col = img.shape
for i in range(row):
for j in range(col):
if img[i,j] == 255:
x.append(i) # get x indices
y.append(j) # get y indices
Note that image[0,0] shows 2D matrix whereas image[0,0,0] shows a 3D matrix with 0-color band. This element basically defines the color band of the image.
If you do not specify third element, it will show you three values for each color band of the image. But when you mention the color band, it will show you the exact pixel value.
NOTE:
There is no need to convert the image into a 2D array. you can always do it for the actual array too. I did so to simply explain the algorithm to you.
You are right neuxx. the value [255 255 255] is not equal to [255, 255, 255]. so you need to convert pixel value ( [255 255 255] ) into array value( [255, 255, 255] ) to compare. so you need to use '[:]' to convert it to array.
Moreover, if you want to find all white pixels you can use this code. While pixel indices will be stored in the list "White_pix_ind" as a tuple.
White_pix_ind = []
row,col,depth = Img.shape
for i in range(row):
for j in range(col):
if( (Img[(i,j)][:]==White_pix_ind ).all() ):
print(i,j)
White_pix_ind.append((i,j))
break;

Python Imaging Library- trouble with altering the alpha value

I'm trying to make a number of white pixels transparent, however I'm doing something wrong.
I can alter the colour of the pixels, however my code seems to ignore any alterations I make to the alpha value. I'm fairly new to PIL, and Python in general, so it's probably a relatively simple mistake.
Here's the code:
image_two = Image.open ("image_two.bmp")
image_two = image_two.convert ("RGBA")
pixels = image_two.load()
for y in xrange (image_two.size[1]):
for x in xrange (image_two.size[0]):
if pixels[x, y] == (0, 0, 0, 255):
pixels[x, y] = (0, 0, 0, 255)
else:
pixels[x, y] = (255, 255, 255, 0)
image_two.save("image_two")
My version of PIL does not support alpha channel in BMP files. I was able to use your code to load a PNG file with alpha. When I try to write that back out to a BMP file, I get a python exception that tells me "IOError: cannot write mode RGBA as BMP".
Your code says:
if pixels[x, y] == (0, 0, 0, 255): #black with alpha of 255
pixels[x, y] = (0, 0, 0, 255) #black with alpha of 255
else:
pixels[x, y] = (255, 255, 255, 0) #white with alpha of 255
A white pixel would have the r,g, and b set to "255". So probably what you want to do is something like this:
if pixels[x,y] == (255,255,255,255):
pixels[x,y] = (pixels[x,y][0], pixels[x,y][1], pixels[x,y][2], 0) #just set this pixel's alpha channel to 0
You may not need an else here, because if the pixels are not white with alpha of 255, you probably don't want to touch them.
I modified your code like this:
import Image
image_two = Image.open ("image_two.png")
image_two = image_two.convert ("RGBA")
pixels = image_two.load()
for y in xrange (image_two.size[1]):
for x in xrange (image_two.size[0]):
if pixels[x, y][3] == 255:
pixels[x, y] = (255, 0, 0, 255)
else:
pixels[x, y] = (255, 255, 255, 255)
image_two.save("image_two2.png")
This code takes my image and writes out a mask - a white pixel where the alpha is 0 and a red pixel where the alpha is 255.

Categories