How to shuffle pixels in an image using Python Pillow? - python

My goal is to shuffle all pixels in a 512x512 Python Pillow image. Also, I need the time performance to be relatively good. What I've tried:
from PIL import Image
import numpy as np
orig = Image.open('img/input2.jpg')
orig_px = orig.getdata()
np_px = np.asarray(orig_px)
np.random.shuffle(np_px)
res = Image.fromarray(np_px.astype('uint8')).convert('RGB')
res.show()
The Preview app gives me the following error:
The file “tmp11g28d6z.PNG” could not be opened.
It may be damaged or use a file format that Preview doesn’t recognise.
I cannot figure out, what went wrong. I would be grateful for any suggestions about fixing this code or trying a different approach to solving this problem.

Main problem that getdata provide you 1d array, and fromarray requires 2d or 3d array. see corrected code. You maybe notice two reshapes. So first reshape make array of pixels. Each pixel has 3 values. Than shuffle them, than reshape in image. If you comment np.random.shuffle(orig_px) you will get original image as is.
from PIL import Image
import numpy as np
orig = Image.open('test.jpg')
orig_px = orig.getdata()
orig_px = np.reshape(orig_px, (orig.height * orig.width, 3))
np.random.shuffle(orig_px)
orig_px = np.reshape(orig_px, (orig.height, orig.width, 3))
res = Image.fromarray(orig_px.astype('uint8'))
res.save('out.jpg')

Related

How do I generate a random colored image using Image.fromaray() in python?

I am trying to create a random image using NUMPY. First I am creating a random 3D array as it should be in the case of an image e.g. (177,284,3).
random_im = np.random.rand(177,284,3)
data = np.array(random_im)
print(data.shape)
Image.fromarray(data)
But when I am using Image.fromarray(random_array), this is throwing the following error.
Just to check if there is any issue with the shape of the array, I converted an image back to the array and converted it back after copying it to the other variable. And I got the output I was looking for.
img = np.array(Image.open('Sample_imgs/dog4.jpg'))
git = img.copy()
git.shape
Image.fromarray(git)
They both have the same shape, I don't understand where am I making the mistake.
When I am creating a 2D array and then converting it back it is giving me a black canvas of that size (even though the pixels should not be black).
random_im = np.random.randint(0,256,size=(231,177))
print(random_im)
# data = np.array(random_im)
print(data.shape)
Image.fromarray(random_im)
I was able to get this working with the solution detailed here:
import numpy as np
from PIL import Image
random_array = np.random.rand(177,284,3)
random_array = np.random.random_sample(random_array.shape) * 255
random_array = random_array.astype(np.uint8)
random_im = Image.fromarray(random_array)
random_im.show()
----EDIT
A more elegant way to get a random array of the correct type without conversions is like so:
import numpy as np
from PIL import Image
random_array = np.random.randint(low=0, high=255,size=(250,250),dtype=np.uint8)
random_im = Image.fromarray(random_array)
random_im.show()
Which is almost what you were doing in your solution, but you have to specify the dtype to be np.uint8:
random_im = np.random.randint(0,256,size=(231,177),dtype=np.uint8)

Convert 8 bit image to 24 bit [duplicate]

I have a matrix in the type of a Numpy array. How would I write it to disk it as an image? Any format works (png, jpeg, bmp...). One important constraint is that PIL is not present.
An answer using PIL (just in case it's useful).
given a numpy array "A":
from PIL import Image
im = Image.fromarray(A)
im.save("your_file.jpeg")
you can replace "jpeg" with almost any format you want. More details about the formats here
This uses PIL, but maybe some might find it useful:
import scipy.misc
scipy.misc.imsave('outfile.jpg', image_array)
EDIT: The current scipy version started to normalize all images so that min(data) become black and max(data) become white. This is unwanted if the data should be exact grey levels or exact RGB channels. The solution:
import scipy.misc
scipy.misc.toimage(image_array, cmin=0.0, cmax=...).save('outfile.jpg')
With matplotlib:
import matplotlib.image
matplotlib.image.imsave('name.png', array)
Works with matplotlib 1.3.1, I don't know about lower version. From the docstring:
Arguments:
*fname*:
A string containing a path to a filename, or a Python file-like object.
If *format* is *None* and *fname* is a string, the output
format is deduced from the extension of the filename.
*arr*:
An MxN (luminance), MxNx3 (RGB) or MxNx4 (RGBA) array.
There's opencv for python (documentation here).
import cv2
import numpy as np
img = ... # Your image as a numpy array
cv2.imwrite("filename.png", img)
useful if you need to do more processing other than saving.
Pure Python (2 & 3), a snippet without 3rd party dependencies.
This function writes compressed, true-color (4 bytes per pixel) RGBA PNG's.
def write_png(buf, width, height):
""" buf: must be bytes or a bytearray in Python3.x,
a regular string in Python2.x.
"""
import zlib, struct
# reverse the vertical line order and add null bytes at the start
width_byte_4 = width * 4
raw_data = b''.join(
b'\x00' + buf[span:span + width_byte_4]
for span in range((height - 1) * width_byte_4, -1, - width_byte_4)
)
def png_pack(png_tag, data):
chunk_head = png_tag + data
return (struct.pack("!I", len(data)) +
chunk_head +
struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))
return b''.join([
b'\x89PNG\r\n\x1a\n',
png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
png_pack(b'IDAT', zlib.compress(raw_data, 9)),
png_pack(b'IEND', b'')])
... The data should be written directly to a file opened as binary, as in:
data = write_png(buf, 64, 64)
with open("my_image.png", 'wb') as fh:
fh.write(data)
Original source
See also: Rust Port from this question.
Example usage thanks to #Evgeni Sergeev: https://stackoverflow.com/a/21034111/432509
You can use PyPNG. It's a pure Python (no dependencies) open source PNG encoder/decoder and it supports writing NumPy arrays as images.
If you have matplotlib, you can do:
import matplotlib.pyplot as plt
plt.imshow(matrix) #Needs to be in row,col order
plt.savefig(filename)
This will save the plot (not the images itself).
for saving a numpy array as image, U have several choices:
1) best of other: OpenCV
import cv2
cv2.imwrite('file name with extension(like .jpg)', numpy_array)
2) Matplotlib
from matplotlib import pyplot as plt
plt.imsave('file name with extension(like .jpg)', numpy_array)
3) PIL
from PIL import Image
image = Image.fromarray(numpy_array)
image.save('file name with extension(like .jpg)')
4) ...
scipy.misc gives deprecation warning about imsave function and suggests usage of imageio instead.
import imageio
imageio.imwrite('image_name.png', img)
You can use 'skimage' library in Python
Example:
from skimage.io import imsave
imsave('Path_to_your_folder/File_name.jpg',your_array)
Addendum to #ideasman42's answer:
def saveAsPNG(array, filename):
import struct
if any([len(row) != len(array[0]) for row in array]):
raise ValueError, "Array should have elements of equal size"
#First row becomes top row of image.
flat = []; map(flat.extend, reversed(array))
#Big-endian, unsigned 32-byte integer.
buf = b''.join([struct.pack('>I', ((0xffFFff & i32)<<8)|(i32>>24) )
for i32 in flat]) #Rotate from ARGB to RGBA.
data = write_png(buf, len(array[0]), len(array))
f = open(filename, 'wb')
f.write(data)
f.close()
So you can do:
saveAsPNG([[0xffFF0000, 0xffFFFF00],
[0xff00aa77, 0xff333333]], 'test_grid.png')
Producing test_grid.png:
(Transparency also works, by reducing the high byte from 0xff.)
For those looking for a direct fully working example:
from PIL import Image
import numpy
w,h = 200,100
img = numpy.zeros((h,w,3),dtype=numpy.uint8) # has to be unsigned bytes
img[:] = (0,0,255) # fill blue
x,y = 40,20
img[y:y+30, x:x+50] = (255,0,0) # 50x30 red box
Image.fromarray(img).convert("RGB").save("art.png") # don't need to convert
also, if you want high quality jpeg's
.save(file, subsampling=0, quality=100)
matplotlib svn has a new function to save images as just an image -- no axes etc. it's a very simple function to backport too, if you don't want to install svn (copied straight from image.py in matplotlib svn, removed the docstring for brevity):
def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, origin=None):
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
fig = Figure(figsize=arr.shape[::-1], dpi=1, frameon=False)
canvas = FigureCanvas(fig)
fig.figimage(arr, cmap=cmap, vmin=vmin, vmax=vmax, origin=origin)
fig.savefig(fname, dpi=1, format=format)
Imageio is a Python library that provides an easy interface to read and write a wide range of image data, including animated images, video, volumetric data, and scientific formats. It is cross-platform, runs on Python 2.7 and 3.4+, and is easy to install.
This is example for grayscale image:
import numpy as np
import imageio
# data is numpy array with grayscale value for each pixel.
data = np.array([70,80,82,72,58,58,60,63,54,58,60,48,89,115,121,119])
# 16 pixels can be converted into square of 4x4 or 2x8 or 8x2
data = data.reshape((4, 4)).astype('uint8')
# save image
imageio.imwrite('pic.jpg', data)
The world probably doesn't need yet another package for writing a numpy array to a PNG file, but for those who can't get enough, I recently put up numpngw on github:
https://github.com/WarrenWeckesser/numpngw
and on pypi: https://pypi.python.org/pypi/numpngw/
The only external dependency is numpy.
Here's the first example from the examples directory of the repository. The essential line is simply
write_png('example1.png', img)
where img is a numpy array. All the code before that line is import statements and code to create img.
import numpy as np
from numpngw import write_png
# Example 1
#
# Create an 8-bit RGB image.
img = np.zeros((80, 128, 3), dtype=np.uint8)
grad = np.linspace(0, 255, img.shape[1])
img[:16, :, :] = 127
img[16:32, :, 0] = grad
img[32:48, :, 1] = grad[::-1]
img[48:64, :, 2] = grad
img[64:, :, :] = 127
write_png('example1.png', img)
Here's the PNG file that it creates:
Also, I used numpngw.write_apng to create the animations in Voronoi diagram in Manhattan metric.
Assuming you want a grayscale image:
im = Image.new('L', (width, height))
im.putdata(an_array.flatten().tolist())
im.save("image.tiff")
If you happen to use [Py]Qt already, you may be interested in qimage2ndarray. Starting with version 1.4 (just released), PySide is supported as well, and there will be a tiny imsave(filename, array) function similar to scipy's, but using Qt instead of PIL. With 1.3, just use something like the following:
qImage = array2qimage(image, normalize = False) # create QImage from ndarray
success = qImage.save(filename) # use Qt's image IO functions for saving PNG/JPG/..
(Another advantage of 1.4 is that it is a pure python solution, which makes this even more lightweight.)
If you are working in python environment Spyder, then it cannot get more easier than to just right click the array in variable explorer, and then choose Show Image option.
This will ask you to save image to dsik, mostly in PNG format.
PIL library will not be needed in this case.
Use cv2.imwrite.
import cv2
assert mat.shape[2] == 1 or mat.shape[2] == 3, 'the third dim should be channel'
cv2.imwrite(path, mat) # note the form of data should be height - width - channel
In the folowing answer has the methods as proposed by #Nima Farhadi in time measurement.
The fastest is CV2 , but it's important to change colors order from RGB to BGR. The simples is matplotlib.
It's important to assure, that the array have unsigned integer format uint8/16/32.
Code:
#Matplotlib
from matplotlib import pyplot as plt
plt.imsave('c_plt.png', c.astype(np.uint8))
#PIL
from PIL import Image
image = Image.fromarray(c.astype(np.uint8))
image.save('c_pil.png')
#CV2, OpenCV
import cv2
cv2.imwrite('c_cv2.png', cv2.cvtColor(c, cv2.COLOR_RGB2BGR))
With pygame
so this should work as I tested (you have to have pygame installed if you do not have pygame install it by using pip -> pip install pygame (that sometimes does not work so in that case you will have to download the wheel or sth but that you can look up on google)):
import pygame
pygame.init()
win = pygame.display.set_mode((128, 128))
pygame.surfarray.blit_array(win, yourarray)
pygame.display.update()
pygame.image.save(win, 'yourfilename.png')
just remember to change display width and height according to your array
here is an example, run this code:
import pygame
from numpy import zeros
pygame.init()
win = pygame.display.set_mode((128, 128))
striped = zeros((128, 128, 3))
striped[:] = (255, 0, 0)
striped[:, ::3] = (0, 255, 255)
pygame.surfarray.blit_array(win, striped)
pygame.display.update()
pygame.image.save(win, 'yourfilename.png')
I attach an simple routine to convert a npy to an image.
from PIL import Image
import matplotlib
img = np.load('flair1_slice75.npy')
matplotlib.image.imsave("G1_flair_75.jpeg", img)
You can use this code for converting your Npy data into an image:
from PIL import Image
import numpy as np
data = np.load('/kaggle/input/objects-dataset/nmbu.npy')
im = Image.fromarray(data, 'RGB')
im.save("your_file.jpeg")

Load a tiff stack in a numpy array with python

I am having a little issue with .tif files. I am sure it is only a minor problem that I can´t get around (keep in mind, I am a relatively new programmer).
Basically: I have prepared .tif files that are 64x64xn in size (n up until 1000). The image is only a single file that contains all of this slices. I would like to load the image into a (multidimensional) numpy array. I have tried:
from PIL import Image as pilimage
file_path=(D:\luca\test\test.tif)
print("The selected stack is a .tif")
dataset = pilimage(file_path)
tiffarray = np.array(dataset)
expim = tiffarray.astype(np.double);
print(expim.shape)
and other things (like tifffile). I only seem to be able to read the first slice of the stack. Is it possible for "expim" to contain all information that is saved in the tiff stack?
I am not sure if there is a way to get PIL to open multiple slices of a tiff stack.
If you are not bound to using PIL, however, an alternative is scikit-image, which opens multiple slices from a tiff stack by default. Here is some sample code of how to load a tiff stack into a Numpy array using scikit-image:
>>> from skimage import io
>>> im = io.imread('an_image.tif')
>>> print(im.shape)
(2, 64, 64)
Note that the imread function loads the image directly into a Numpy array. Also, the dimensions of the resulting array are ordered (z, y, x) where z represents the depth, y represents the height, and x represents the width. Thus, to get a single slice from the stack all you have to do is:
>>> print(im[1].shape)
(64, 64)
PIL has a function seek to move to different slices of a tiff stack.
from PIL import Image
file_path=(D:\luca\test\test.tif)
print("The selected stack is a .tif")
dataset = Image.open(file_path)
h,w = np.shape(dataset)
tiffarray = np.zeros((h,w,dataset.n_frames))
for i in range(dataset.n_frames):
dataset.seek(i)
tiffarray[:,:,i] = np.array(dataset)
expim = tiffarray.astype(np.double);
print(expim.shape)

Is it possible to generate a gif animation using pillow library only?

I want to be able to transform numpy arrays into images. First, I have learned how to transform a 3D (hight x width x color) array into an image. After some research it looks to me that PIL (or Pillow) is the most natural way to do it. This is how I do it at the moment (and it works fine):
from PIL import Image
import numpy as np
if __name__ == '__main__':
h = 4
w = 8
arr = np.zeros((h,w,3), dtype=np.uint8)
arr[0, 0, :] = [255,255,0]
arr[3, 7, :] = [0,255,0]
img = Image.fromarray(arr, 'RGB')
img.save('viz.png')
As a next step, I want to be able to take a list of 3D array (or a 4D array, where time is the additional dimension) and generate the corresponding animation. So, far I did not find how to do it.
It looks like Pillow is able to read gif-animation. Using ImageSequence we can access its frames. However, I cannot find out how one can put a sequence of images into animation.
I saw some solutions that use ìmages2gif but I would like to stay withing a single library.
ADDED
The answers here do not answer my question. They use gifmaker library that I cannot even install by pip.
So, the main objection of the question was to generate a gif animation represented by a list of 3D arrays (frames) or by a 4D matrix (with width, height, color and time as dimension) without a use of tools that are "external" to Python.
It looks like PIL library cannot do that. At least not in a simple way without hacks or workarounds. However, the goal can be achieved by using the moviepy library. Here is the elegant solution provided by this library:
import numpy as np
import moviepy.editor as mpy
def make_frame(t):
h = 100
w = 100
ar = np.zeros((h, w, 3))
for hi in range(h):
for wi in range(w):
for ci in range(3):
ar[hi, wi, ci] = 255.0*t/15.0
return ar
if __name__ == '__main__':
clip = mpy.VideoClip(make_frame, duration=15.0)
clip.write_gif('ani.gif', fps=15)

Convert Image ( png ) To Matrix And Then To 1D Array

I have 5 pictures and i want to convert each image to 1d array and put it in a matrix as vector. I want to be able to convert each vector to image again.
img = Image.open('orig.png').convert('RGBA')
a = np.array(img)
I'm not familiar with all the features of numpy and wondered if there other tools I can use.
Thanks.
import numpy as np
from PIL import Image
img = Image.open('orig.png').convert('RGBA')
arr = np.array(img)
# record the original shape
shape = arr.shape
# make a 1-dimensional view of arr
flat_arr = arr.ravel()
# convert it to a matrix
vector = np.matrix(flat_arr)
# do something to the vector
vector[:,::10] = 128
# reform a numpy array of the original shape
arr2 = np.asarray(vector).reshape(shape)
# make a PIL image
img2 = Image.fromarray(arr2, 'RGBA')
img2.show()
import matplotlib.pyplot as plt
img = plt.imread('orig.png')
rows,cols,colors = img.shape # gives dimensions for RGB array
img_size = rows*cols*colors
img_1D_vector = img.reshape(img_size)
# you can recover the orginal image with:
img2 = img_1D_vector.reshape(rows,cols,colors)
Note that img.shape returns a tuple, and multiple assignment to rows,cols,colors as above lets us compute the number of elements needed to convert to and from a 1D vector.
You can show img and img2 to see they are the same with:
plt.imshow(img) # followed by
plt.show() # to show the first image, then
plt.imshow(img2) # followed by
plt.show() # to show you the second image.
Keep in mind in the python terminal you have to close the plt.show() window to come back to the terminal to show the next image.
For me it makes sense and only relies on matplotlib.pyplot. It also works for jpg and tif images, etc. The png I tried it on has float32 dtype and the jpg and tif I tried it on have uint8 dtype (dtype = data type); each seems to work.
I hope this is helpful.
I used to convert 2D to 1D image-array using this code:
import numpy as np
from scipy import misc
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
face = misc.imread('face1.jpg');
f=misc.face(gray=True)
[width1,height1]=[f.shape[0],f.shape[1]]
f2=f.reshape(width1*height1);
but I don't know yet how to change it back to 2D later in code, Also note that not all the imported libraries are necessary, I hope it helps

Categories