I'm trying to read and write tiff-images with the PIL library. While testing, I noticed that saving the seemingly same numpy array generated in different ways leads to different images on disk. Why is this and how can I fix it?
For testing purposes, I created an image with GIMP (upscaled from 8x8) which I saved as TIF, read to a numpy array and wrote back to a tif file:
img_gimp = Image.open('img_gimp.tif')
imgarray_gimp = np.array(img_gimp)
img_gimp = Image.fromarray(imgarray_gimp, mode = 'I;16')
img_gimp.save('final_gimp.tif')
The result is as expected, it is the same image as the original. So far so good.
Now I generated the same image directly in python code:
imgarray_direct = np.zeros(shape = (8, 8)).astype(int)
for i in range(2):
for j in range(2):
image[i][j] = 65535
Writing this array to disk...
img_direct = Image.fromarray(imgarray_direct, mode = 'I;16')
img_direct.save('final_direct.tif')
doesn't give me the expected result, instead I find this:
image generated by for loop (upscaled from 8x8)
Doing
print(np.array_equal(imgarray_gimp, imgarray_direct))
gives True, and looking at print(imgarray_gimp) and print(imgarray_direct), one cannot see any difference.
Is this intended behaviour? If yes, whats the reason for it?
Thank you for your answers!
As #MarkSetchell hinted in the comments, the issue is that your dtype for the numpy array of raw data does not match the PIL image mode string you are supplying afterwards. Changing the parameter passed into astype or simply passing in the right type on array creation fixed this issue for me. Here is what the modified code looks like:
import numpy as np
from PIL import Image
#Generate raw image data (16-bit!)
image_data = np.zeros(shape = (8, 8), dtype=np.uint16)#this is the big change
for i in range(2):
for j in range(2):
image_data[i][j] = 65535
#Save image as TIF to disk
image_direct = Image.fromarray(image_data, mode = 'I;16')
image_direct.save('final_direct.tif')
As a side note, I am surprised that the mode string I;16 you have used is valid; I could not find any mention about it in pillow's documentation.
Related
I'm using OpenCV version 4.1.1 in Python and cannot get a legitimate reading for a 32-bit image, even when I use cv.IMREAD_ANYDEPTH. Without cv.IMREAD_ANYDEPTH, it returns as None type; with it, I get a matrix of zeros. The issue persists after reinstalling OpenCV. os.path.isfile returns True. The error was replicated on another computer. The images open in ImageJ, so I wouldn't think they're corrupted. I would rather use Skimage since it reads the images just fine, but I have to use OpenCV for what I'm working on. Any advice is appreciated.
img = cv2.imread(file,cv2.IMREAD_ANYDEPTH)
Link for the image: https://drive.google.com/file/d/1IiHbemsmn2gLW12RG3i9fLYZQW2u8sQw/view?usp=sharing
It appears to be some bug in how OpenCV loads such TIFF images. Pillow seems to load the image in a sensible way. Running
from PIL import Image
import numpy as np
img_pil = Image.open('example_image.tiff')
img_pil_cv = np.array(img_pil)
print(img_pil_cv.dtype)
print(img_pil_cv.max())
I get
int32
40950
as an output, which looks reasonable enough.
When I do
import cv2
img_cv = cv2.imread('example_image.tiff', cv2.IMREAD_ANYDEPTH)
print(img_cv.dtype)
print(img_cv.max())
I get
float32
5.73832e-41
which is obviously wrong.
Nevertheless, the byte array holding the pixel data is correct, it's just not being interpreted correctly. You can use numpy.ndarray.view to reinterpret the datatype of a numpy array, so that it's treated as an array if 32bit integers instead.
img_cv = cv2.imread('example_image.tiff', cv2.IMREAD_ANYDEPTH)
img_cv = img_cv.view(np.int32)
print(img_cv.dtype)
print(img_cv.max())
Which prints out
int32
40950
Since the maximum value is small enough for 16bit integer, let's convert the array and see what it looks like
img_cv_16bit = img_cv.astype(np.uint16)
cv2.imwrite('output_cv_16bit.png', img_cv_16bit)
OK, there are some bright spots, and a barely visible pattern. With a little adjustment, we can get something visible:
img_cv_8bit = np.clip(img_cv_16bit // 16, 0, 255).astype(np.uint8)
cv2.imwrite('output_cv_8bit.png', img_cv_8bit)
That looks quite reasonable now.
I am modifying images using Image and Numpy in Python 2.7.
I am saving an image with a numpy Integer grayscale array using :
img = Image.fromarray(grayscale_arr, 'L')
img.save(output)
In an other function, i reopen the same image and get the grayscale array with :
img = Image.open(output)
grayscale_arr = np.array(img)
However the arrays do not match. The differences are not big but significant enough ...
My questions are :
First why is that ? Is it an imprecision of some sort ? (just out of curiosity)
And how can I fix this ?
Thank you in advance
I was curious about image processing with python, so I found this great library imageio,
I tried to manipulate the pixels of a picture and save them in a new file,
but i had some problems with the loops
this is what the code looks like
enter image description here
and this the error that i Got !
IndexError: index 3507 is out of bounds for axis 0 with size 3507
the code :
# -*- coding: iso-8859-1 -*-
import imageio
import numpy as np
im = imageio.imread("JAFFRE009a.png")
taille=im.shape #taille is a tuple (Width,Height)
print taille # (4961,3507)
matrice_pixels=open("matrice.txt",'w')
for i in range(taille[1]):
line=""
for j in range(taille[0]):
line+=repr(im[i][j])
matrice_pixels.write(line+'\n')
matrice_pixels.close()
Because your image doesn't have squarred shape, reshape it before you go through your loop
EDIT
We can iterate through each row/column position and save to a file as below.It will take very long time depending upon file size.
Instead of writing your own function, you may want to take advantage of inbuilt binary save (which is more efficient) as
np.save('matrix.py', np_array)
You can load this file as np array and manipulate
Or as a text file using np.save [ will take longer ]
np.save('matrix.txt', np_array)
Working Code:
import imageio
import numpy as np
im = imageio.imread("9v9zU.png")
matrice_pixels=open("matric.txt","wb")
nx,ny = im.shape
for i in range(nx):
line=""
for j in range(ny):
line+=repr(im[i][j])
matrice_pixels.write(line+'\n')
matrice_pixels.close()
#Save as Binary data
np.save('matrix1.npy', im)
#Save as Human readable data
np.savetxt('matrix1.txt', im)
Alternately, you may want to look into off the shelf libraries that will do what you are intending to do.
For e.g. This SO link discusses how to remove section of the picture based upon its color using PIL library.
Also , in future, please DO NOT post a picture of your code. Copy/pase to SO window so that we can copy and modify. In this case I had write everything down line by line to test(thankfully code was not that long).
When importing an image I receive a different value than when I create an image. If I import the image
img = open('16x9.gif','rb')
img.read().encode('base64')
# 'R0lGODlhEAAJAIAAAAAAAAAAACH5BAEAAAAALAAAAAAQAAkAAAIKhI+py+0Po5yUFQA7\n'
But, If I create the image in cStringIO:
import cStringIO
import base64
import Image
tmp = cStringIO.StringIO()
img = Image.new('RGB', (16,9))
img.save(tmp, format='GIF', transparency=0)
base64.b64encode(tmp.getvalue())
# Response truncated for readability
# 'R0lGODdhEAAJAIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMAAGYAAJkAAMwAAP8AA...
The value returned by this second snippet is different and much longer than the first.
Why are my return values different and how do i get the shorter (see first) base64 value?
The difference is between those images representations is format, as your first string is a gif in 89a format, while your tmp is 87a.
>>> 'R0lGODlh'.decode('base64')
'GIF89a'
>>> 'R0lGODdh'.decode('base64')
'GIF87a'
The only related discussion on the topic I found is this: GIF89A and PIL, so it seems that PIL can load 89a format, but can't create it on his own.
As #delnan pointed out, I was not using the equivalent image.
But, in my search I found that png images return shorter base64 strings than gif images.
Closer to what was retuned from a gif that was made in photoshop.
I have an image saved by another code of mine. The image is a normal JPG file. I saved it with imsave.
now when I'm reading it in another code, it seems to be 3d :S
the image is here.
and a simple code to read it is this :
import mahotas
img = mahotas.imread('d:/normal.jpg')
print img.shape, img.dtype
Try reading the jpg as greyscale like this:
mahotas.imread('d:/normal.jpg', as_grey = True)
(Author of mahotas here).
The suggestion by Junuxx is correct:
mahotas.imread('file.jpg', as_grey=True)
This reads the RGB file and converts it to grey scale by a weighted average of the components (they are not equally weighted, but use typical coefficients that attempt to be perceptually more accurate).
The alternative (which I rather prefer) is:
im = mahotas.imread('file.jpg')
im = im[:,:,0]
I assume that all the channels have the same values and just use the first one.