python PIL.Image give me incorrect tiff - python

I use PIL.Image library to open a tif file and convert it to numpy array:
PIL.ImageFile.LOAD_TRUNCATED_IMAGES = True # because my tif file is big
im = PIL.Image.open('1.tif')
arr = numpy.asarray(im)
I found weird result, so I did the following test:
im1 = PIL.ImageFile.fromarray(arr,"CMYK") # tif file is CMYK value
im1.save('new.tif')
It turned out that the new tif file doesn't look like the original tif file. Suppose the 1.tif looks like A, then new.tif looks like several A tiled together, each of A is smaller, but the total size is the same. I don't know where I do it wrong.
Update:
Both x and y direction, the image is repeated 4 times, so together is 16 times. But the total size of the new tif remains the same. Any ideas?

Related

Opening a single image

I'm trying to open an image with size (520,696) but when I use this
array = np.array([np.array(Image.open(folder_path+folders+'/'+'images'+'/'+image))], np.int32).shape`
I'm getting the shape as
(1, 520, 696, 4)
The problem is with this shape I can't convert it to image using toimage(array); I get
'arr' does not have a suitable array shape for any mode.
Any suggestions on how may I read that image using only (520,696)?
The problem is the additional dumb dimension. You can remove it using:
arr = np.squeeze(arr)
You should load the picture as a single picture instead of loading it as a stack and then removing the irrelevant stack dimension. The basic procedure could be something like this:
from PIL import Image
pic = Image.open("test.jpg")
pic.show() #yup, that's the picture
arr = np.array(pic) #convert it to a numpy array
print(arr.shape, arr.dtype) #dimension and data type
arr //= 2 #now manipulate this array
new_pic = Image.fromarray(arr) #and keep it for later
new_pic.save("newpic.bmp") #maybe in a different format

How to read multiple images in python

I want read 100 colour images and use them for further processing. Suppose one image size is 256x 256 by reading it in python openCV its size is (256,256,3). I now want to read 100 images and after reading I have to get the size as (100,256,256,3).
You could do something like this, supposing that your images are named like 0.png to 99.png:
import numpy as np
result=np.empty((100,256,256,3))
for i in range (100):
result[i,:,:,:] = cv2.imread('{}.png'.format(i),1)
## your image names
#fnames = sorted(glob.glob("images/*.png"))
## read and stack
img = np.stack([cv2.imread(fname) for fname in fnames])

Create Numpy array of images

I have some (950) 150x150x3 .jpg image files that I want to read into an Numpy array.
Following is my code:
X_data = []
files = glob.glob ("*.jpg")
for myFile in files:
image = cv2.imread (myFile)
X_data.append (image)
print('X_data shape:', np.array(X_data).shape)
The output is (950, 150). Please let me know why the list is not getting converted to np.array correctly and whether there is a better way to create the array of images.
Of what I have read, appending to numpy arrays is easier done through python lists and then converting them to arrays.
EDIT: Some more information (if it helps), image.shape returns (150,150,3) correctly.
I tested your code. It works fine for me with output
('X_data shape:', (4, 617, 1021, 3))
however, all images were exactly the same dimension.
When I add another image with different extents I have this output:
('X_data shape:', (5,))
So I'd recommend checking the sizes and the same number of channels (as in are really all images coloured images)? Also you should check if either all images (or none) have alpha channels (see #Gughan Ravikumar's comment)
If only the number of channels vary (i.e. some images are grey), then force loading all into the color format with:
image = cv2.imread (myFile, cv2.IMREAD_COLOR)
EDIT:
I used the very code from the question, only replaced with a directory of mine (and "*.PNG"):
import cv2
import glob
import numpy as np
X_data = []
files = glob.glob ("C:/Users/xxx/Desktop/asdf/*.PNG")
for myFile in files:
print(myFile)
image = cv2.imread (myFile)
X_data.append (image)
print('X_data shape:', np.array(X_data).shape)
Appending images in a list and then converting it into a numpy array, is not working for me. I have a large dataset and RAM gets crashed every time I attempt it. Rather I append the numpy array, but this has its own cons. Appending into list and then converting into np array is space complex, but appending a numpy array is time complex. If you are patient enough, this will take care of RAM crasing problems.
def imagetensor(imagedir):
for i, im in tqdm(enumerate(os.listdir(imagedir))):
image= Image.open(im)
image= image.convert('HSV')
if i == 0:
images= np.expand_dims(np.array(image, dtype= float)/255, axis= 0)
else:
image= np.expand_dims(np.array(image, dtype= float)/255, axis= 0)
images= np.append(images, image, axis= 0)
return images
I am looking for better implementations that can take care of both space and time. Please comment if someone has a better idea.
Here is a solution for images that have certain special Unicode characters, or if we are working with PNGs with a transparency layer, which are two cases that I had to handle with my dataset. In addition, if there are any images that aren't of the desired resolution, they will not be added to the Numpy array. This uses the Pillow package instead of cv2.
resolution = 150
import glob
import numpy as np
from PIL import Image
X_data = []
files = glob.glob(r"D:\Pictures\*.png")
for my_file in files:
print(my_file)
image = Image.open(my_file).convert('RGB')
image = np.array(image)
if image is None or image.shape != (resolution, resolution, 3):
print(f'This image is bad: {myFile} {image.shape if image is not None else "None"}')
else:
X_data.append(image)
print('X_data shape:', np.array(X_data).shape)
# If you have 950 150x150 images, this would print 'X_data shape: (950, 150, 150, 3)'
If you aren't using Python 3.6+, you can replace the r-string with a regular string (except with \\ instead of \, if you're using Windows), and the f-string with regular string interpolation.
Your definition for the .JPG frame that will be put into a matrix of the same size should should be x, y, R, G, B, A. "A" is not used, but it does take up 8 bits at the end of each pixel.

Averaging multiple images in python

I'm trying to average 300 .tif images with this code :
import os, numpy, PIL
from PIL import Image
# Access all PNG files in directory
allfiles=os.listdir(os.getcwd())
imlist=[filename for filename in allfiles if filename[-4:] in[".tif",".TIF"]]
# Assuming all images are the same size, get dimensions of first image
w,h = Image.open(imlist[0]).size
N = len(imlist)
# Create a numpy array of floats to store the average (assume RGB images)
arr = numpy.zeros((h,w,3),numpy.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr = numpy.array(Image.open(im),dtype=numpy.float)
arr = arr+imarr/N
# Round values in array and cast as 16-bit integer
arr = numpy.array(numpy.round(arr),dtype=numpy.uint16)
# Generate, save and preview final image
out = Image.fromarray(arr,mode="RGB")
out.save("Average.tif")
And it gives me a TypeError like that :
imarr = numpy.array(Image.open(im),dtype=numpy.float)
TypeError: float() argument must be a string or a number, not 'TiffImageFile'
I understand that it doesn't really like to put a TIF image in the numpy array (it also doesn't work with PNG images). What should I do ? Splitting each image into R, G and B arrays to average and then merge everything seems too memory consuming.
It should work as is, checked right now with PIL (pillow 2.9.0) and numpy 1.9.2.

Conversion between Pillow Image object and numpy array changes dimension

I am using Pillow and numpy, but have a problem with conversion between Pillow Image object and numpy array.
when I execute following code, the result is weird.
im = Image.open(os.path.join(self.img_path, ifname))
print im.size
in_data = np.asarray(im, dtype=np.uint8)
print in_data.shape
result is
(1024, 768)
(768, 1024)
Why dimension is changed?
im maybe column-major while arrays in numpy are row-major
do in_data = in_data.T to transpose the python array
probably should check in_data with matplotlib's imshow to make sure the picture looks right.
But do you know that matplotlib comes with its own loading functions that gives you numpy arrays directly? See: http://matplotlib.org/users/image_tutorial.html
If your image is greyscale do:
in_data = in_data.T
but if you are working with rbg images you want to make sure your transpose operation is along only two axis:
in_data = np.transpose(in_data, (1,0,2))
actually this is because most image libraries give you images that are transpozed compared to numpy arrays. this is (i think) because you write image files line by line, so the first index (let's say x) refers to the line number (so x is the vertical axis) and the second index (y) refers to the subsequent pixel in line (so y is the horizontal axis), which is against our everyday coordinates sense.
If you want to handle it correctly you need to remember to write:
image = library.LoadImage(path)
array = (library.FromImageToNumpyArray(image)).T
and consequently:
image = library.FromNumpyArrayToImage(array.T)
library.WriteImage(image, path)
Which works also for 3D images. But i'm not promising this is the case for ALL image libraries - just these i worked with.

Categories

Resources