Python Pillow Creating an Image with List of Pixels - python

I have a .txt file that contains coordinates and color codes. I want to create an image from this
My example txt file:
1,1#f8f3ed
1,2#fff9f1
1,3#faf2e7
1,4#fbf2e1
1,5#f6eed9
1,6#e1d6c0
1,7#e2d6be
1,8#ebdfc5
1,9#d0c4ac
1,10#cdc2ac
1,11#e3dbc6
1,12#ded7c5
.
.
187,249#1b2019
How can I create this image?
Edited code as below:
from PIL import Image
from PIL import ImageColor
path_to_file = 'data.txt'
img_data = []
height = 0
width = 0
# The first step is to read the file
with open(path_to_file) as f:
lines = f.readlines()
# Strip off new lines
lines = [x.strip('\n') for x in lines]
for line in lines:
x,y = line[:-7].split(',')
# I'm assuming from the data that the x,y vals start at 1,1. Subtract 1 from each value so they are zero indexed.
x = int(x) - 1
y = int(y) - 1
color = line[-7:]
# Use PIL's ImageColor.getrgb() to convert hex string to a rgb tuple
color = ImageColor.getrgb(color)
img_data.append((x,y,color))
# Keep track of the max x,y vals for to get the width and height of the image
height = max(height, x+1)
width = max(width, y+1)
# Create a new image
background = (0, 0, 0, 255)
img = Image.new('RGB', (width, height), background)
pixels = img.load()
# Set the pixel values from our data
for d in img_data:
pixels[d[0], d[1]] = d[2]
img.save("image.png")
Now it raises:
ValueError: too many values to unpack
How to solve this error?

This worked for me:
from PIL import Image
from PIL import ImageColor
path_to_file = 'test.txt'
img_data = []
height = 0
width = 0
# The first step is to read the file
with open(path_to_file) as f:
lines = f.readlines()
# Strip off new lines
lines = [x.strip('\n') for x in lines]
for line in lines:
x,y = line[:-7].split(',')
# I'm assuming from the data that the x,y vals start at 1,1. Subtract 1 from each value so they are zero indexed.
x = int(x) - 1
y = int(y) - 1
color = line[-7:]
# Use PIL's ImageColor.getrgb() to convert hex string to a rgb tuple
color = ImageColor.getrgb(color)
img_data.append((x,y,color))
# Keep track of the max x,y vals for to get the width and height of the image
height = max(height, y)
width = max(width, x)
# Create a new image
background = (0, 0, 0, 255)
img = Image.new('RGB', (width + 1, height + 1), background)
pixels = img.load()
# Set the pixel values from our data
for d in img_data:
pixels[d[0], d[1]] = d[2]
img.save("image.png")

Related

Python - image rows rearrangement and displaying afterwards

I am trying to write a code in Python to display an altered image according to some conditionals. It is a part of LED strip displaying, but I am not experienced with image processing. Maybe someone would help me.
The image with fixed dimensions [height x weight] is loaded and code has to change the rows rearrangement in two ways:
For the first half of rows, For example for max_height = 10: [1,2,3,4,5] --> [1,3,5,7,9]
i=0
for height_pixel =< max_height/2
height_pixel=height_pixel+1
i+=1
For the second half of rows, for example for max_height = 10: [6,7,8,9,10] --> [10,8,6,4,2]
i=4
for height_pixel > max_height/2
height_pixel=(max_height/2)+1+i
i-=2
The columns are not changed.
After that printing/showing/... new image.
The code I have so far, based on adafruit library:
from PIL import Image
import adafruit_dotstar as dotstar
NUMPIXELS = 10 #Length of strip
FILENAME = "image.png" # Image file to load
# Load image and get dimensions:
IMG = Image.open(FILENAME).convert("RGB")
PIXELS = IMG.load()
WIDTH = IMG.size[0]
HEIGHT = IMG.size[1]
HEIGHT = min(HEIGHT, NUMPIXELS)
######################################################
# CONDITIONAL PART
pixelMap = IMG.load()
img = Image.new( IMG.mode, IMG.size)
pixelsNew = img.load()
for i in range(img.size[0]):
if ...
else...
#######################################################
img.show() #altered image
#here I allocating list of lists one for each column of image
COLUMN = [0 for x in range(WIDTH)]
for x in range(WIDTH):
COLUMN[x] = [[0, 0, 0, 0] for _ in range(HEIGHT)]
#converts it into 2D list [column x row]
for x in range(WIDTH): # For each column of image
for y in range(HEIGHT): # For each pixel in column
value = PIXELS[x, y] # Read RGB pixel in image
COLUMN[x][y][0] = value[0]] # R
COLUMN[x][y][1] = value[1]] # G
COLUMN[x][y][2] = value[2]] # B
COLUMN[x][y][3] = 1.0 # Brightness
#and display in a loop
#here the columns will changed after pressing the button, I will add it later, for now it is in loop
while True:
for x in range(WIDTH): # For each column of image...
DOTS[0 : DOTS.n] = COLUMN[x]
DOTS.show()
Maybe someone would give hints or help me with the code.
Best regards
I would suggest to generate a mapping array, which maps the orignal column indices to the newly ordered ones (and by the way, indices should start at 0):
first_half = [2*idx + 1 for idx in range(5)] # 1,3,5,7,9
second_half = [8-2*idx for idx in range(5)] # 8,6,4,2,0
mapping = first_half + second_half
for idx in range(HEIGHT):
new_img[idx] = IMG[mapping[idx]] # introduced new_img to avoid confusion between img and IMG
And then you should be almost done. As far as I can see you do not need to generate this COLUMN list of lists.
for column in new_img:
DOTS[0 : DOTS.n] = column # if thats the DOTS notation, I am not familiar with that part of your code
DOTS.show()

Count number of White Pixels per row of an image in Python

I am stuck trying to count the number of white pixels per row in a image.
i have tried this
img = cv2.imread("11.jpg") # Its our Image object
mask = np.uint8(np.where(img == 0, 1, 0))
row_counts = cv2.reduce(mask, 1, cv2.REDUCE_SUM, dtype=cv2.CV_32SC1) # type: ignore
#Data Collection
White_row = 224- row_counts # 224 is max pixels in row
So i made my own loop algorithm that loops through every pixel and checks the pixel intensity. The image is converted into grey scale.
img = Image.open("11.jpg")
img = img.convert("L") # convert to grayscale
pix = img.load()
width, height = img.size
print(width, height)
print(pix[91,134]) #Tester
White_row = [0] * height
for y in range(height):
for x in range(width):
if pix[x,y] > 235: # 235 is the threshold
White_row[y] += 1
Max_Width = np.max(White_row)
Mean_Width = np.mean(White_row)
print ("Max_Width: ", Max_Width)
print ("Mean_Width: ", Mean_Width)

Combine all tiff images into one single image

I have 15 tiles or tiff files a folder and I would like combine it as a single file with all the images as one tiff image. All the tiles should be stitched as a single tiff image. How do I do that?
What I tried so far?
import imageio
import os
path = "path/to/dir"
image_path_list = os.listdir(path)
with imageio.get_writer("new_image.tif") as new_image:
for image_path in image_path_list:
image = imageio.imread(path+image_path)
new_image.append_data(image)
This saves as a separate image in a tiff file. I would like to stitch all the images together and save it like the following:
1,2,3...,15 represent the tiles. Needs to be stitched as a single image.
It seems from your comments that you are prepared to consider a non-Python solution, so I used ImageMagick in the Terminal to montage 15 images as follows:
magick montage -tile 3x -geometry +0+0 09*tif result.tif
To demonstrate how you can lay out 5 images across instead of 3, add a different background and affect the horizontal and vertical spacing differently, here is a variation:
magick montage -background magenta -tile 5x -geometry +5+15 09*tif result.tif
Just FYI, I made the 15 randomly coloured blocks like this:
for x in {a..o} ; do magick xc: +noise random -scale 80x50\! 09$x.tif ; done
given one directory with 15 images of same size
using PIL (pillow), I ended up with:
from PIL import Image
import os
path_to_file ='tiff-files'
images = []
for i in os.listdir(path_to_file):
with Image.open(path_to_file+'/'+i) as im:
images.append(im.copy())
new_image = Image.new(images[0].mode, (images[0].size[0]*3,images[0].size[1]*5))
new_image.paste(images[0])
new_image.paste(images[1],(images[0].size[0]*1,0))
new_image.paste(images[2],(images[0].size[0]*2,0))
new_image.paste(images[3],(0,images[0].size[1]*1))
new_image.paste(images[4],(images[0].size[0]*1,images[0].size[1]*1))
new_image.paste(images[5],(images[0].size[0]*2,images[0].size[1]*1))
new_image.paste(images[6],(0,images[0].size[1]*2))
new_image.paste(images[7],(images[0].size[0]*1,images[0].size[1]*2))
new_image.paste(images[8],(images[0].size[0]*2,images[0].size[1]*2))
new_image.paste(images[9],(0,images[0].size[1]*3))
new_image.paste(images[10],(images[0].size[0]*1,images[0].size[1]*3))
new_image.paste(images[11],(images[0].size[0]*2,images[0].size[1]*3))
new_image.paste(images[12],(0,images[0].size[1]*4))
new_image.paste(images[13],(images[0].size[0]*1,images[0].size[1]*4))
new_image.paste(images[14],(images[0].size[0]*2,images[0].size[1]*4))
new_image.show()
let me know if it works.....
After Mark Setchell suggestion here a new version, hope it is better
from PIL import Image
import os
path_to_file ='tiff-files'
def stich_tile(path_to_file, xx , yy):
images = []
for i in os.listdir(path_to_file):
images.append(i)
if len(images) >= xx*yy:
pass
else:
raise ValueError('not enough images in path_to_file !!!!!!!!!!!')
sq_x = xx
sq_y = yy
img_x = (Image.open(path_to_file+'/'+images[0]).size[0])
img_y = (Image.open(path_to_file+'/'+images[0]).size[1])
img_mode = (Image.open(path_to_file+'/'+images[0]).mode)
new_image = Image.new(img_mode, (img_x*sq_x, img_y*sq_y))
x = 0
y = 0
cnt = 0
for i in images:
with Image.open(path_to_file+'/'+i) as img:
new_image.paste(img, (x,y))
cnt += 1
x += img_x
if cnt == sq_x:
x = 0
y += img_y
cnt = 0
else:
pass
return new_image
stich_tile(path_to_file, 3, 5).show()
And thinking more along the lines of https://stackoverflow.com/a/68468658/2836621
import numpy as np
from PIL import Image
import os
# path_to_file ='tiff-files'
path_to_file ='tiff-files2'
# path_to_file ='tiff-files3'
image = []
for i in os.listdir(path_to_file):
with Image.open(path_to_file+'/'+i) as im:
image.append(im.copy())
w, h = image[0].size
new_image = np.zeros((4 * h, 3 * w)).astype('uint8')
col = 0
row = -1
for i, img in enumerate(image):
if not i % 3 :
row += 1
col = 0
img = np.array(img)
new_image[row * h: (row + 1) * h, col * w: (col + 1) * w] = img
col += 1
image_pillow = Image.fromarray(new_image, mode = 'L')
image_pillow.save('prova.tif', mode = 'L')
image_pillow.show()
tested with .tif images grayscale 8-bit
modify adding 3 channel for RGB et similia:
new_image = np.zeros((3 * h, 3 * w,3)).astype('uint8')
new_image[row * h: (row + 1) * h,col * w: (col + 1) * w,:] = img
once more the last example as function for 8 bit grayscale images:
import numpy as np
from PIL import Image
import os
path_to_file ='tiff-files'
# path_to_file ='tiff-files2'
# path_to_file ='tiff-files3'
# path_to_file ='tiff-files5'
def stich_img(path_to_file, x , y):
image = []
for i in os.listdir(path_to_file):
image.append(path_to_file+'/'+i)
print(image)
if len(image) >= x*y:
pass
else:
# raise ValueError('not enough images in path_to_file !!!!!!!!!!!')
raise ValueError('EXCEPTION not enough images in path_to_file !!!!!!!!!!!', x*y ,'images needed : ', len(image),'images present !!!')
image = image[:x*y] #-----> riduce lista immagini al numero richiesto
with Image.open(image[0]) as img0:
w, h = img0.size
# new_image = np.zeros((4 * h, 3 * w)).astype('uint8')
new_image = np.zeros((y * h, x * w)).astype('uint8')
col = 0
row = -1
for i, imgs in enumerate(image):
with Image.open(imgs) as img:
if not i % x :
row += 1
col = 0
img = np.array(img)
new_image[row * h: (row + 1) * h, col * w: (col + 1) * w] = img
col += 1
image_pillow = Image.fromarray(new_image, mode = 'L')
return image_pillow
img_stiched = stich_img(path_to_file, 3,5)
# img_stiched.save('prova.tif', mode = 'L')
img_stiched.show()
Read all images in a list. Iterate over this list using two nested for loops. One in range of 3 and one in range of 5. Use numpy.hstack() and numpy.vstack() to make a final 3x5 image assuming that the size of each tile image is same.
Using numpy:
This script accepts generator of images (to work faster with large images). It does not check their size in advance. If image height does not fit row height or if rows have not the same width, it will fail.
#!/usr/bin/env python3
import numpy as np
from imageio import imread, imwrite
from pathlib import Path
def tile_images(images, cols):
"""Tile images of same size to grid with given number of columns.
Args:
images (collection of ndarrays)
cols (int): number of colums
Returns:
ndarray: stitched image
"""
images = iter(images)
first = True
rows = []
i = 0
while True:
try:
im = next(images)
print(f"add image, shape: {im.shape}, type: {im.dtype}")
except StopIteration:
if first:
break
else:
im = np.zeros_like(im) # black background
if first:
row = im # start next row
first = False
else:
row = np.concatenate((row, im), axis=1) # append to row
i += 1
if not i % cols:
print(f"row done, shape: {row.shape}")
rows.append(row) # finished row
first = True
tiled = np.concatenate(rows) # stitch rows
return tiled
def main():
images = (imread(f) for f in Path().glob("*.*") if f.suffix in (".jpg", ".png") if f.name != "new.png")
new = tile_images(images, cols=3)
imwrite("new.png", new)
def test():
im1 = np.arange(65536).reshape(256,256)
im2 = np.arange(65536/2).reshape(128,256)
images = [im1,im1,im1,im2,im2,im2]
# works
new = tile_images(images, 3)
imwrite("new.png", new)
# failes
new = tile_images(images, 2)
imwrite("new2.png", new)
if __name__ == "__main__":
main()
# test()
The following elaborates on #saad_saeed answer.
Note, the following will break:
if your list_of_images doesn't have enough images to build the num_mosaic_rows x num_mosaic_cols mosaic. I've left it to the user to add the handling of this (e.g. adding an if/else).
if each img in your list_of_images doesn't have the same shape
def build_mosaic(list_of_images, num_mosaic_rows, num_mosaic_cols):
list_of_mosaic_rows = []
for row_number in range(num_mosaic_rows):
list_of_mosaic_rows = list_of_images[row_number*num_mosaic_cols,(row_number+1)*num_mosaic_cols]
mosaic = np.vstack(list_of_mosaic_rows)
return mosaic

Convert an images pixel value from rgb to grayscale manually python PIL?

Im trying to use a specific gamma corrected grayscale implementation - Gleam to convert an images pixels to grayscale. How can i do this manually with PIL python?
def tau_gamma_correct(pixel_channel):
pixel_channel = pixel_channel**(1/2.2)
return pixel_channel
##param: rgb
##result: returns grayscale value
def gleam(rgb):
#convert rgb tuple to list
rgblist = list(rgb)
#gamma correct each rgb channel
rgblist[0] = tau_gamma_correct(rgblist[0])
rgblist[1] = tau_gamma_correct(rgblist[1])
rgblist[2] = tau_gamma_correct(rgblist[2])
grayscale = 1/3*(rgblist[0] + rgblist[1] + rgblist[2])
return grayscale
# get a glob list of jpg filenames
files = glob.glob('*.jpg')
for file in files:
file = open(file)
filename = file.name
image = Image.open(file)
pix = image.load()
width, height = image.size
#print(width,height)
for x in range(0, width):
for y in range(0, height):
rgb = pix[x,y]
#print(rgb)
# calc new pixel value and set to pixel
image.mode = 'L'
pix[x,y] = gleam(rgb)
image.save(filename + 'gray.gleam'+'.jpg')
file.close()
SystemError: new style getargs format but argument is not a tuple
It is still expecting the rgb tuple i think.
I found that i could just build another image:
import sys
import os
import glob
import numpy
from PIL import Image
def tau_gamma_correct(pixel_channel):
pixel_channel = pixel_channel**(1/2.2)
return pixel_channel
##param: rgb
##result: returns grayscale value
def gleam(rgb):
#convert rgb tuple to list
rgblist = list(rgb)
#gamma correct each rgb channel
rgblist[0] = tau_gamma_correct(rgblist[0])
print('gleamed red ' + str(rgblist[0]))
rgblist[1] = tau_gamma_correct(rgblist[1])
print('gleamed green ' + str(rgblist[1]))
rgblist[2] = tau_gamma_correct(rgblist[2])
print('gleamed blue ' + str(rgblist[0]))
grayscale = (rgblist[0] + rgblist[1] + rgblist[2])/3
print('grayscale '+ str(grayscale))
return grayscale
# get a glob list of jpg filenames
files = glob.glob('*.jpg')
for file in files:
file = open(file)
filename = file.name
image = Image.open(file)
pix = image.load()
width, height = image.size
new_image = Image.new('L', image.size)
#pixelmatrix = [width][height]
pixelmatrix = numpy.zeros((width, height))
#print(width,height)
for x in range(0, width):
for y in range(0, height):
rgb = pix[x,y]
print('current pixel value: '+str(rgb))
# calc new pixel value and set to pixel
#print(gleam(rgb))
gray = gleam(rgb)
print('changing to pixel value: '+str(gray))
pixelmatrix[x,y] = gray
new_image.save(filename + 'gray.gleam'+'.jpg')
new_image.putdata(pixelmatrix)
file.close()
The problem is that image.mode = 'L' doesn't actually change the type of the image, it just changes the attribute so it's no longer accurate. To change the mode of the image you need to make a new copy with image.convert('L').
Once you have an image in grayscale mode, it won't require a tuple for a pixel value anymore.
Seeing the SystemError: new style getargs format but argument is not a tuple error it seems that you need to return a tuple, which is represented as :
sample_tuple = (1, 2, 3, 4)
So we edit the gleam() function as:
def gleam(rgb):
#convert rgb tuple to list
rgblist = list(rgb)
#gamma correct each rgb channel
rgblist[0] = tau_gamma_correct(rgblist[0])
rgblist[1] = tau_gamma_correct(rgblist[1])
rgblist[2] = tau_gamma_correct(rgblist[2])
grayscale = 1/3*(rgblist[0] + rgblist[1] + rgblist[2])
return (grayscale, )
Keep in mind that while returning a single element tuple you need to represent as :
sample_tuple = (1, )
This is due to the fact that (4) == 4 but (4, ) != 4

How to use PIL (Python Image Library) rotate image and let black background to be transparency

I want to rotate a gray "test" image and paste it onto a blue background image. Now I just can remove the black color after rotate my gray "test" image, but their is now a white color section. How can I use Python to change the "white" color section to blue?
Here is my code, can someone help me? I'd appreciate it.
dst_im = Image.new("RGBA", (196,283), "blue" )
im = src_im.convert('RGBA')
rot = im.rotate( angle, expand=1 ).resize(size)
f = Image.new( 'RGBA', rot.size, (255,)*4 )
im2 = Image.composite( rot, f, rot )
im2.convert(src_im.mode)
im2_width, im2_height = im2.size
cut_box = (0, 0, im2_width, im2_height )
paste_box = ( left, top, im2_width+left, im2_height+top )
region = im2.crop( cut_box )
dst_im.paste( region, paste_box )
dst_im.save("test.gif")
I have the impression that your code could be simplified as follows:
from PIL import Image
src_im = Image.open("winter3.jpg")
angle = 45
size = 100, 100
dst_im = Image.new("RGBA", (196,283), "blue" )
im = src_im.convert('RGBA')
rot = im.rotate( angle, expand=1 ).resize(size)
dst_im.paste( rot, (50, 50), rot )
dst_im.save("test.png")
This gives the following result:
Another answer using PIL is clearly more succinct. I had a similar problem and had the image in an ndarray. Yipes, mine came out way more complicated than user1202136. I'm posting it only because it demonstrates another solution using numpy and array stacking, but user1202136's solution is much better.
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
def rgba(rgb_img, alpha):
'''
' takes an rgb ndarray r x c x 3 of dtype=uint8
' and adds an alpha 0-255 to each pixel
'''
rows = len(rgb_img) # get image dimensions
columns = len(rgb_img[0])
rgb_flat = rgb_img.reshape([rows * columns, 3]) # list of rgb pixels
a = np.zeros([rows*columns, 1], dtype=np.uint8) # alpha for each pixel
a.fill(alpha)
rgba = np.column_stack([rgb_flat, a]) # place 4th column
return rgba.reshape([rows, columns, 4]) # reform into r x c x 4
def pad_with_transparent_pixels(rgba_img):
'''
' takes an rgba image r x c
' and places within a buffer of [ 0 0 0 0] to become square,
' with sides = diagonal of img
'''
rows = len(rgba_img) # get image dimensions
columns = len(rgba_img[0])
diag = (rows**2 + columns**2)**0.5
diag = int(diag) + 1
top_pad_height = (diag-rows)/2 + 1
left_pad_width = (diag-columns)/2 + 1
top_pad = np.zeros([top_pad_height, diag, 4], dtype=np.uint8)
left_pad = np.zeros([rows, left_pad_width, 4], dtype=np.uint8)
right_pad = np.zeros([rows,
# assures total width of top_pad for row_stack:
diag - left_pad_width - columns,
4 ],
dtype=np.uint8)
center = np.column_stack([left_pad, rgba_img, right_pad])
return np.row_stack([top_pad, center, top_pad])
def clean_rotate(rgba_img,angle):
rows = len(rgba_img)
columns = len(rgba_img[0])
diag = (rows**2 + columns**2)**.5
diag = int(diag)
pad_img = pad_with_transparent_pixels(rgba_img)
rot_img = scipy.ndimage.rotate(pad_img, angle)
rot_img_rows = len(rot_img)
rot_img_columns = len(rot_img[0])
crop_side = max(1,(rot_img_columns - diag) / 2) #max to avoid splicing [:0]
crop_top = max(1,(rot_img_rows - diag) / 2)
print diag, crop_side, crop_top
return rot_img[crop_top:-crop_top,crop_side:-crop_side]
img = plt.imread('C:\\Users\\bbrown\\Desktop\\Maurine.jpg') # read in a jpg
figure, axes = plt.subplots(1, 2) # create 1x2 grid of axes
axes[0].imshow(img) # place image on first axes
rgba_image = rgba(img, 255) # create an opaque rgba image
rot_img = clean_rotate(rgba_image,50)
#make a pattern of 10 images
for i in range(10):
rot_img = clean_rotate(rgba_image,5*i)
axes[1].imshow(rot_img)
plt.show()

Categories

Resources