PIL image to array (numpy array to array) - Python - python

I have a .jpg image that I would like to convert to Python array, because I implemented treatment routines handling plain Python arrays.
It seems that PIL images support conversion to numpy array, and according to the documentation I have written this:
from PIL import Image
im = Image.open("D:\Prototype\Bikesgray.jpg")
im.show()
print(list(np.asarray(im)))
This is returning a list of numpy arrays. Also, I tried with
list([list(x) for x in np.asarray(im)])
which is returning nothing at all since it is failing.
How can I convert from PIL to array, or simply from numpy array to Python array?

I highly recommend you use the tobytes function of the Image object. After some timing checks this is much more efficient.
def jpg_image_to_array(image_path):
"""
Loads JPEG image into 3D Numpy array of shape
(width, height, channels)
"""
with Image.open(image_path) as image:
im_arr = np.fromstring(image.tobytes(), dtype=np.uint8)
im_arr = im_arr.reshape((image.size[1], image.size[0], 3))
return im_arr
The timings I ran on my laptop show
In [76]: %timeit np.fromstring(im.tobytes(), dtype=np.uint8)
1000 loops, best of 3: 230 µs per loop
In [77]: %timeit np.array(im.getdata(), dtype=np.uint8)
10 loops, best of 3: 114 ms per loop
```

I think what you are looking for is:
list(im.getdata())
or, if the image is too big to load entirely into memory, so something like that:
for pixel in iter(im.getdata()):
print pixel
from PIL documentation:
getdata
im.getdata() => sequence
Returns the contents of an image as a sequence object containing pixel
values. The sequence object is flattened, so that values for line one
follow directly after the values of line zero, and so on.
Note that the sequence object returned by this method is an internal
PIL data type, which only supports certain sequence operations,
including iteration and basic sequence access. To convert it to an
ordinary sequence (e.g. for printing), use list(im.getdata()).

Based on zenpoy's answer:
import Image
import numpy
def image2pixelarray(filepath):
"""
Parameters
----------
filepath : str
Path to an image file
Returns
-------
list
A list of lists which make it simple to access the greyscale value by
im[y][x]
"""
im = Image.open(filepath).convert('L')
(width, height) = im.size
greyscale_map = list(im.getdata())
greyscale_map = numpy.array(greyscale_map)
greyscale_map = greyscale_map.reshape((height, width))
return greyscale_map

I use numpy.fromiter to invert a 8-greyscale bitmap, yet no signs of side-effects
import Image
import numpy as np
im = Image.load('foo.jpg')
im = im.convert('L')
arr = np.fromiter(iter(im.getdata()), np.uint8)
arr.resize(im.height, im.width)
arr ^= 0xFF # invert
inverted_im = Image.fromarray(arr, mode='L')
inverted_im.show()

Related

Convert python wand hdr image to numpy array and back

Python wand supports converting images directly to a Numpy arrays, such as can be seen in related questions.
However, when doing this for .hdr (high dynamic range) images, this appears to compress the image to 0/255. As a result, converting from a Python Wand image to a np array and back drastically reduces file size/quality.
# Without converting to a numpy array
img = Image('image.hdr') # Open with Python Wand Image
img.save(filename='test.hdr') # Save with Python wand
Running this opens the image and saves it again, which creates a file with a size of 41.512kb. However, if we convert it to numpy before saving it again..
# With converting to a numpy array
img = Image(filename=os.path.join(path, 'N_SYNS_89.hdr')) # Open with Python Wand Image
arr = np.asarray(img, dtype='float32') # convert to np array
img = Image.from_array(arr) # convert back to Python Wand Image
img.save(filename='test.hdr') # Save with Python wand
This results in a file with a size of 5.186kb.
Indeed, if I look at arr.min() and arr.max() I see that the min and max values for the numpy array are 0 and 255. If I open the .hdr image with cv2 however as an numpy array, the range is much higher.
img = cv2.imread('image.hdr'), -1)
img.min() # returns 0
img.max() # returns 868352.0
Is there a way to convert back and forth between numpy arrays and Wand images without this loss?
As per the comment of #LudvigH, the following worked as in this answer.
img = Image(filename='image.hdr'))
img.format = 'rgb'
img.alpha_channel = False # was not required for me, including it for completion
img_array = np.asarray(bytearray(img.make_blob()), dtype='float32')
Now we much reshape the returned img_array. In my case I could not run the following
img_array.reshape(img.shape)
Instead, for my img.size was a (x,y) tuple that should have been an (x,y,z) tuple.
n_channels = img_array.size / img.size[0] / img.size[1]
img_array = img_array.reshape(img.size[0],img.size[1],int(n_channels))
After manually calculating z as above, it worked fine. Perhaps this is also what caused the original fault in converting using arr = np.asarray(img, dtype='float32')

How to create an image with height as pixel values

I want to create a jpg image with size 343 by 389 (Height by Width) with height as pixel values. For example for the whole topmost pixels, I need to give it as value 1, the next row of pixels should have a value 2. and finally, the last pixel with value 343. then export that image in jpg format. How to do this? either in python or in Matlab?
In MATLAB
A solution in MATLAB using the meshgrid() function may work. An important part is to caste the array Image of type double into an unsigned 8-bit integer array, uint8 before exporting it as a .jpg using the imwrite() function.
[~,Image] = meshgrid((1:389),(1:343));
imwrite(uint8(Image),"Depth.jpg");
Did it
from PIL import Image
import numpy as np
a = np.empty(shape=(343, 389), dtype=int)
for i in range(343):
for j in range(389):
a[i,j]=i
im = Image.fromarray(a,'L')
im.save('depth.jpg')

Converting a given 2d array into a binary image using opencv in python

So here is the incomplete code
import cv2
import numpy as np
arr= np.zeros((30,30))
# bw_image = <missing part>
cv2.imshow("BW Image",bw_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
#please use arr at 4th line to complete the code
I am actually new to this and don't know how to convert a given 2d array into a binary image using opencv.
Please use the name "arr" for the missing part, as in my code, the array is not a zeros array, instead it has some random values of 0 and 255 of 400x400 shape.
I think you want a Numpy array of random integers:
arr = np.random.randint(0, 256, (400,400), dtype=np.uint8)
If your question is actually about thresholding, maybe you want:
_, bw_image = cv2.threshold(arr, 128,255,cv2.THRESH_BINARY)

Convert PythonMagick Image object to numpy array (for OpenCV) and then to PIL image object

I want to convert a PythonMagick Image Object to a NumPy array that can be used in OpenCV, and then I want to convert it into a PIL image object. I have searched Google but cannot find any sources explaining how to do this. Can someone show me how to convert image objects between these different modules?
The fastest way that I've found consist in saving and opening it:
import PythonMagic
import cv2
# pm_img is a PythonMagick.Image
pm_img.write('path/to/temporary/file.png')
np_img = cv2.imread('path/to/temporary/file.png')
I haven't found any satisfactory way to convert PythonMagick images to NumPy arrays without saving them, but there is a slow way that involves using python loops:
import PythonMagick
import numpy as np
pm_img = PythonMagick.Image('path/to/image.jpg')
h, w = pm_img.size().height(), pm_img.size().width()
np_img = np.empty((h, w, 3), np.uint16) # PythonMagick opens images with 16 bit depth
# It seems to store the same byte twice (weird)
for i in range(h):
for j in range(w):
# OpenCV stores pixels as BGR
np_img[i, j] = (pm_img.pixelColor(j, i).quantumBlue(),
pm_img.pixelColor(j, i).quantumGreen(),
pm_img.pixelColor(j, i).quantumRed())
np_img = np_img.astype(np.uint8)
Converting NumPy arrays to PIL images is easier:
from PIL import Image
pil_img = Image.fromarray(np_img[:, :, ::-1].astype(np.uint8))
Since PIL stores images in RGB but OpenCV stores them in BGR it's necessary to change the order of the channels ([:, :, ::-1]).
Image.fromarray() takes a NumPy array with dtype np.uint8.

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