I'm trying to create a 4D array for a bunch of 3D images. I can load the image and show the image correctly, but after storing it to the 4D array and show the image from the array, it shows gibberish.
I tried to compare if the image loaded and the one read from the 4D array is equal, and it prints True.
import os
from glob import glob
import numpy as np
from PIL import Image
IMG_PATH = '32x32'
img_paths = glob(os.path.join(IMG_PATH, '*.jpg'))
images = np.empty((len(img_paths), 32, 32, 3))
for i, path_i in enumerate(img_paths):
img_i = np.array(Image.open(path_i))
Image.fromarray(img_i, 'RGB').show() # showing correct image
images[i] = img_i
Image.fromarray(images[i], 'RGB').show() # showing gibberish
print(np.array_equal(img_i, images[i])) # True
if i == 0:
break
I expect to show the exact same image as I run images[i] = img_i.
This line is performing a cast:
images[i] = img_i
Since images.dtype == np.float64, but img_i.dtype is probably np.uint8.
You can catch this type of mistake by specifying a casting rule:
np.copy_to(images[i], img_i, casting='no')
# TypeError: Cannot cast scalar from dtype('uint8') to dtype('float64') according to the rule 'no'
You can fix this by allocating the array with the right type:
images = np.empty((len(img_paths), 32, 32, 3), dtype=np.uint8)
Or you can let numpy do the allocation for you, but this will temporarily use almost twice the memory:
images = np.stack([
Image.open(path_i)
for path_i in img_paths
], axis=0)
Related
I have been trying to use PIL for loading and storing images with the help of numpy arrays.
I am trying to load an image of dimensions 192x192 , pad it to make it 256x256 and then store it back.
Here is the script which I am trying to run :
from PIL import Image
from numpy import asarray
import numpy as np
#function to pad to 256x256
def pad_2d(data, r, c):
res = np.zeros((r,c))
m, n = data.shape
res[(r-m)//2:(r-m)//2+m , (c-n)//2:(c-n)//2+n] = data
return res
#function to remove padding
def crop_2d(data, r, c):
m, n = data.shape
return data[(m-r)//2:(m-r)//2+r , (n-c)//2:(n-c)//2+c]
file = "img1.png"
#image is successfully loaded in the form of numpy array and normalized
data = asarray(Image.open(file)) # the data loaded, is of the shape (192,192,4)
data = (255.0 / data.max() * (data - data.min())).astype(np.uint8)
# dummy numpy array to store the 256x256 variant of 192x192 image by padding zeros
t = np.zeros((256,256,4))
for i in range(4):
t[:,:,i] = pad_2d(data[:,:,i],256,256)
print(data.shape, t.shape) # prints : (192, 192, 4) (256, 256, 4)
img = Image.fromarray(t) # error occurs in this line
img.save('img2.png')
Error:
TypeError: Cannot handle this data type: (1, 1, 4), <f8
I have cross-checked both pad_2d and crop_2d functions. They both work as expected. If I try to execute img = Image.fromarray(data) instead, then it runs fine by saving the same image as expected.
Any help would be appreciated. Thanks for reading.
You are inadvertently making a float array. Change to this:
t = np.zeros((256,256,4), dtype=np.uint8)
I have a folder with 230400 images, each representing one pixel in a 480 x 480 image.
How can I use Python to make a single image out of each image?
I tried to creat a npy-array but I believe it resulted in a 3d array instead of a 2d array:
import cv2
import glob
import numpy as np
data = []
files = glob.glob("./data/*.PNG")
for myFile in files:
print(myFile)
image = cv2.imread(myFile)
data.append(image)
print('shape:', np.array(data).shape)
np.save('data',data)
Output: shape: (230400, 100, 100, 3)
How do I create a 2d array of images? And how do I convert it to an image?
Start by creating an empty numpy image with the size of your output image. For each pixel load the image in.
import numpy as np
import cv2
import glob
image_x = 480
image_y =480
files = glob.glob("./data/*.PNG")
output = np.zeros((image_x, image_y, 3))
for i in range(image_x):
for j in range(image_y):
pixel = cv2.imread(files[image_x*i+j])
output[i,j] = pixel[0,0]
Note: This is neither fast nor nice, but explicit.
For saving, use cv2.imwrite on the resulting array as in:
cv2.imwrite('output.png', output)
How do I convert a PIL Image back and forth to a NumPy array so that I can do faster pixel-wise transformations than PIL's PixelAccess allows? I can convert it to a NumPy array via:
pic = Image.open("foo.jpg")
pix = numpy.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)
But how do I load it back into the PIL Image after I've modified the array? pic.putdata() isn't working well.
You're not saying how exactly putdata() is not behaving. I'm assuming you're doing
>>> pic.putdata(a)
Traceback (most recent call last):
File "...blablabla.../PIL/Image.py", line 1185, in putdata
self.im.putdata(data, scale, offset)
SystemError: new style getargs format but argument is not a tuple
This is because putdata expects a sequence of tuples and you're giving it a numpy array. This
>>> data = list(tuple(pixel) for pixel in pix)
>>> pic.putdata(data)
will work but it is very slow.
As of PIL 1.1.6, the "proper" way to convert between images and numpy arrays is simply
>>> pix = numpy.array(pic)
although the resulting array is in a different format than yours (3-d array or rows/columns/rgb in this case).
Then, after you make your changes to the array, you should be able to do either pic.putdata(pix) or create a new image with Image.fromarray(pix).
Open I as an array:
>>> I = numpy.asarray(PIL.Image.open('test.jpg'))
Do some stuff to I, then, convert it back to an image:
>>> im = PIL.Image.fromarray(numpy.uint8(I))
Source: Filter numpy images with FFT, Python
If you want to do it explicitly for some reason, there are pil2array() and array2pil() functions using getdata() on this page in correlation.zip.
I am using Pillow 4.1.1 (the successor of PIL) in Python 3.5. The conversion between Pillow and numpy is straightforward.
from PIL import Image
import numpy as np
im = Image.open('1.jpg')
im2arr = np.array(im) # im2arr.shape: height x width x channel
arr2im = Image.fromarray(im2arr)
One thing that needs noticing is that Pillow-style im is column-major while numpy-style im2arr is row-major. However, the function Image.fromarray already takes this into consideration. That is, arr2im.size == im.size and arr2im.mode == im.mode in the above example.
We should take care of the HxWxC data format when processing the transformed numpy arrays, e.g. do the transform im2arr = np.rollaxis(im2arr, 2, 0) or im2arr = np.transpose(im2arr, (2, 0, 1)) into CxHxW format.
You need to convert your image to a numpy array this way:
import numpy
import PIL
img = PIL.Image.open("foo.jpg").convert("L")
imgarr = numpy.array(img)
Convert Numpy to PIL image and PIL to Numpy
import numpy as np
from PIL import Image
def pilToNumpy(img):
return np.array(img)
def NumpyToPil(img):
return Image.fromarray(img)
The example, I have used today:
import PIL
import numpy
from PIL import Image
def resize_image(numpy_array_image, new_height):
# convert nympy array image to PIL.Image
image = Image.fromarray(numpy.uint8(numpy_array_image))
old_width = float(image.size[0])
old_height = float(image.size[1])
ratio = float( new_height / old_height)
new_width = int(old_width * ratio)
image = image.resize((new_width, new_height), PIL.Image.ANTIALIAS)
# convert PIL.Image into nympy array back again
return array(image)
If your image is stored in a Blob format (i.e. in a database) you can use the same technique explained by Billal Begueradj to convert your image from Blobs to a byte array.
In my case, I needed my images where stored in a blob column in a db table:
def select_all_X_values(conn):
cur = conn.cursor()
cur.execute("SELECT ImageData from PiecesTable")
rows = cur.fetchall()
return rows
I then created a helper function to change my dataset into np.array:
X_dataset = select_all_X_values(conn)
imagesList = convertToByteIO(np.array(X_dataset))
def convertToByteIO(imagesArray):
"""
# Converts an array of images into an array of Bytes
"""
imagesList = []
for i in range(len(imagesArray)):
img = Image.open(BytesIO(imagesArray[i])).convert("RGB")
imagesList.insert(i, np.array(img))
return imagesList
After this, I was able to use the byteArrays in my Neural Network.
plt.imshow(imagesList[0])
I can vouch for svgtrace, I found it both super simple and relatively fast. Find it here: https://pypi.org/project/svgtrace/
This is how I used it:
from svgtrace import trace
asset_path = 'image.png'
save_path = 'traced_image.svg'
Path(save_path).write_text(trace(asset_path), encoding='utf-8')
It took an average of 3 seconds for a 1080x1080px image on my machine. (MacBook Pro 2017)
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
You can transform the image into numpy
by parsing the image into numpy() function after squishing out the features( unnormalization)
I am reading thousands of images (all three channels), one by one, in form of a numpy ndarray and append them to a list. At the end I want to convert this list into a numpy array:
import numpy as np
from PIL import Image
def read_image_path(path, img_size=227):
img = Image.open(path)
img = np.array(img.resize([img_size, img_size]))
return img
I read each image path from a dictionary that looks like:
{1:{'img_path': 'path-to-image', 'someOtherKeys':'...'}, 2:{...}}
images = []
for key in key:
img = read_image_path(dataset_dictionary[key]['img_path'])
images.append(img)
Up to here it's all fine. I have a list of ndarray image matrices of size (227,227,3). But when I try to convert "images" to numpy array and return it from the function, it gives the following error:
return np.array(images)
return np.array(images)
ValueError: could not broadcast input array from shape (227,227,3) into shape (227,227)
I will be grateful to have anyone's idea about this.
Most likely you have a img (or images) which has the shape of (227,227) instead of (227,227,3).
The following code should tell you which image is the offender.
for key in key:
img = read_image_path(dataset_dictionary[key]['img_path'])
if img.shape != (227,227,3):
print(key)
How do I convert a PIL Image back and forth to a NumPy array so that I can do faster pixel-wise transformations than PIL's PixelAccess allows? I can convert it to a NumPy array via:
pic = Image.open("foo.jpg")
pix = numpy.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)
But how do I load it back into the PIL Image after I've modified the array? pic.putdata() isn't working well.
You're not saying how exactly putdata() is not behaving. I'm assuming you're doing
>>> pic.putdata(a)
Traceback (most recent call last):
File "...blablabla.../PIL/Image.py", line 1185, in putdata
self.im.putdata(data, scale, offset)
SystemError: new style getargs format but argument is not a tuple
This is because putdata expects a sequence of tuples and you're giving it a numpy array. This
>>> data = list(tuple(pixel) for pixel in pix)
>>> pic.putdata(data)
will work but it is very slow.
As of PIL 1.1.6, the "proper" way to convert between images and numpy arrays is simply
>>> pix = numpy.array(pic)
although the resulting array is in a different format than yours (3-d array or rows/columns/rgb in this case).
Then, after you make your changes to the array, you should be able to do either pic.putdata(pix) or create a new image with Image.fromarray(pix).
Open I as an array:
>>> I = numpy.asarray(PIL.Image.open('test.jpg'))
Do some stuff to I, then, convert it back to an image:
>>> im = PIL.Image.fromarray(numpy.uint8(I))
Source: Filter numpy images with FFT, Python
If you want to do it explicitly for some reason, there are pil2array() and array2pil() functions using getdata() on this page in correlation.zip.
I am using Pillow 4.1.1 (the successor of PIL) in Python 3.5. The conversion between Pillow and numpy is straightforward.
from PIL import Image
import numpy as np
im = Image.open('1.jpg')
im2arr = np.array(im) # im2arr.shape: height x width x channel
arr2im = Image.fromarray(im2arr)
One thing that needs noticing is that Pillow-style im is column-major while numpy-style im2arr is row-major. However, the function Image.fromarray already takes this into consideration. That is, arr2im.size == im.size and arr2im.mode == im.mode in the above example.
We should take care of the HxWxC data format when processing the transformed numpy arrays, e.g. do the transform im2arr = np.rollaxis(im2arr, 2, 0) or im2arr = np.transpose(im2arr, (2, 0, 1)) into CxHxW format.
You need to convert your image to a numpy array this way:
import numpy
import PIL
img = PIL.Image.open("foo.jpg").convert("L")
imgarr = numpy.array(img)
Convert Numpy to PIL image and PIL to Numpy
import numpy as np
from PIL import Image
def pilToNumpy(img):
return np.array(img)
def NumpyToPil(img):
return Image.fromarray(img)
The example, I have used today:
import PIL
import numpy
from PIL import Image
def resize_image(numpy_array_image, new_height):
# convert nympy array image to PIL.Image
image = Image.fromarray(numpy.uint8(numpy_array_image))
old_width = float(image.size[0])
old_height = float(image.size[1])
ratio = float( new_height / old_height)
new_width = int(old_width * ratio)
image = image.resize((new_width, new_height), PIL.Image.ANTIALIAS)
# convert PIL.Image into nympy array back again
return array(image)
If your image is stored in a Blob format (i.e. in a database) you can use the same technique explained by Billal Begueradj to convert your image from Blobs to a byte array.
In my case, I needed my images where stored in a blob column in a db table:
def select_all_X_values(conn):
cur = conn.cursor()
cur.execute("SELECT ImageData from PiecesTable")
rows = cur.fetchall()
return rows
I then created a helper function to change my dataset into np.array:
X_dataset = select_all_X_values(conn)
imagesList = convertToByteIO(np.array(X_dataset))
def convertToByteIO(imagesArray):
"""
# Converts an array of images into an array of Bytes
"""
imagesList = []
for i in range(len(imagesArray)):
img = Image.open(BytesIO(imagesArray[i])).convert("RGB")
imagesList.insert(i, np.array(img))
return imagesList
After this, I was able to use the byteArrays in my Neural Network.
plt.imshow(imagesList[0])
I can vouch for svgtrace, I found it both super simple and relatively fast. Find it here: https://pypi.org/project/svgtrace/
This is how I used it:
from svgtrace import trace
asset_path = 'image.png'
save_path = 'traced_image.svg'
Path(save_path).write_text(trace(asset_path), encoding='utf-8')
It took an average of 3 seconds for a 1080x1080px image on my machine. (MacBook Pro 2017)
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
You can transform the image into numpy
by parsing the image into numpy() function after squishing out the features( unnormalization)