Write PIL image to EXR using OpenEXR in Python - python

I'm attempting to write a floating point PIL image object out to a channel in an EXR file using OpenEXR.
I can read EXR data into a PIL image fine:
import OpenEXR
import Imath
from PIL import Image
import numpy as np
exrPath = "path/to/image.exr"
exrFile = OpenEXR.InputFile(exrPath)
pt = Imath.PixelType(Imath.PixelType.FLOAT)
dw = curFile.header()['dataWindow']
size = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1)
rgbf = [Image.frombytes("F", size, exrFile.channel(c, pt)) for c in ("R", "G", "B")]
I then run some operations over the PIL image data and want to save a single channel out as a new EXR. This is what I have so far:
exrHeader = OpenEXR.Header(pilImage.size[0],pilImage.size[1])
exrHeader["channels"] = {"GRAY":Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT), 1, 1)}
exrOut = OpenEXR.OutputFile("path/to/new.exr", exrHeader)
exrOut.writePixels({"GRAY": np.array(pilImage).astype(np.float32).tostring()})
But I get this error:
TypeError: Data for channel 'GRAY' should have size 67108864 but got 16777216
How do I convert a floating point PIL image to the correct format to write to a float EXR channel?

I got it working but don't understand it completely yet.
npImage = np.squeeze(pilImage)
size = img.shape
exrHeader = OpenEXR.Header(size[1], size[0])
exrHeader['channels'] = {"GRAY":Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT), 1, 1)}
exrOut = OpenEXR.OutputFile("path/to/new.exr", exrHeader)
GRAY = (npImage[:,:]).astype(np.float32).tobytes()
exrOut.writePixels({'GRAY' : R})
exrOut.close()

Related

What is the conversion to save a jpg image from a numpy array?

im working with a hamamatsu camera, I get a NumPy array and I want to save the array like an image, I can do it to a TIF image but i don't know how to convert the TIF image or the array to get a correct jpg image, I have this code:
img = Image.fromarray(self.val_fin)
if int(self.vTIFF.get()) == 1:
imgTIFF = img.convert('I')
img.save('name1.tiff')
if int(self.vJPG.get()) == 1:
imgJPG = img.convert('RGB')
imgJPG.save('name2.jpg')
Where val_fin is a 32bit array whose negative values ​​have been changed to 0, the result of the jpg image is a black image.
Thanks.
using tiff 32bites float image:
I can run this code:
from PIL import Image
import numpy as np
def normalize8(I):
mn = I.min()
mx = I.max()
mx -= mn
I = ((I - mn)/mx) * 255.0
return np.round(I).astype(np.uint8)
img1 = Image.open('test_fl.tif', 'r')
arr = np.asarray(img1)
print(arr.size, arr.shape, arr.ndim , arr.dtype)
img = Image.fromarray(arr, mode='F')
print(img.size, img.format, img.mode)
img.save('test_saved.tif')
# doesnt work
# imgTIFF = img.convert(mode='I')
# imgTIFF.save('name1.tif')
# img2 = Image.open('name1.tif', 'r')
# print(img2.size, img2.format, img2.mode)
imgTIFF = Image.fromarray(normalize8(arr))
imgTIFF.save('name1.tif')
img2 = Image.open('name1.tif', 'r')
print(img2.size, img2.format, img2.mode)
imgJPG = imgTIFF.convert('RGB')
imgJPG.save('name2.jpg')
img3 = Image.open('name2.jpg')
print(img3.size, img3.format, img3.mode)
img3.show()
print(img3.getpixel((0,0)))
taken from How should I convert a float32 image to an uint8 image?
the imgTIFF = img.convert(mode='I')
trying to convert tiff 32float to 32int gives a black Image to me too

How to perform operations on images in python

I am trying to complete a challenge where i use an equation to construct a new image (d) from other images. Then i must get the flag in the image (d). The given images are a.png, b.png c.png and y.png and they can be found here: https://drive.google.com/drive/folders/1bZOm_0apr5ZmaRNf9R5UVIEmtMuYSphn?usp=sharing
The equation: d = y - 21a - 3b + 41c
My current code
from PIL import Image
imagey = Image.open('y.png')
imagea = Image.open('a.png')
imageb = Image.open('b.png')
imagec = Image.open('c.png')
size = width, height = imagey.size
new = Image.new('RGB', size)
imgy = imagey.load()
imga = imagea.load()
imgb = imageb.load()
imgc = imagec.load()
data = new.load()
for x in range(width):
for y in range(height):
they = imgy[x, y]
thea = imga[x, y]
theb = imgb[x, y]
thec = imgc[x, y]
new_color = ((int(they[0])) & ~(int((21 * thea[0])) ^ int((3 * theb[0])) ^ int(~(41 * thec[0]))),
(int(they[1])) & ~(int((21 * thea[1])) ^ int((3 * theb[1])) ^ int(~(41 * thec[1]))),
(int(they[2])) & ~(int((21 * thea[2])) ^ int((3 * theb[2])) ^ int(~(41 * thec[2]))))
data[x, y] = new_color
new.save('final.png')
new.show()
If you would convert Pillow image to numpy array or you would use OpenCV or imageio to load image (and get directly numpy array) then you could do
directly
new = imagey - 21*imagea - 3*imageb + 41*imagec
Result:
Not ideal but much better than with your code.
It can be problem with overflow. It may create array with 8bits values and calculations can gives 16bits or 32bits values which can be reduced to 8bits in every calculation.
Full working code:
import imageio
imagey = imageio.imread('y.png')
imagea = imageio.imread('a.png')
imageb = imageio.imread('b.png')
imagec = imageio.imread('c.png')
new = imagey - 21*imagea - 3*imageb + 41*imagec
imageio.imwrite('final.png', new)
# --- imageio doesn't have function to display it ---
import matplotlib.pyplot as plt
plt.imshow(new)
plt.show()
EDIT:
If I use OpenCV then I get ideal result
Full working code:
import cv2
imagey = cv2.imread('y.png')
imagea = cv2.imread('a.png')
imageb = cv2.imread('b.png')
imagec = cv2.imread('c.png')
new = imagey - 21*imagea - 3*imageb + 41*imagec
cv2.imwrite('final.png', new)
# --- show window with image and wait for press any key ---
cv2.imshow('Image', new)
cv2.waitKey(0)
cv2.destroyAllWindows()
EDIT:
By the way: version which converts PIL Image to numpy array and later it converts back to PIL Image - but it gives the same result as imageio.
from PIL import Image
import numpy as np
imagey = Image.open('y.png')
imagea = Image.open('a.png')
imageb = Image.open('b.png')
imagec = Image.open('c.png')
arr_y = np.array(imagey)
arr_a = np.array(imagea)
arr_b = np.array(imageb)
arr_c = np.array(imagec)
arr_new = arr_y - 21*arr_a - 3*arr_b + 41*arr_c
new = Image.fromarray(arr_new)
new.save('final.png')
new.show()
BTW:
If I check images on Linux using program file then it shows that b.png and c.png are JPEG, not PNG.
$ file b.png
b.png: JPEG image data, JFIF standard 1.01, resolution (DPI),
density 300x300, segment length 16,
Exif Standard: [TIFF image data, big-endian, direntries=0], baseline,
precision 8, 960x640, components 3
I found that cv2.imread() gives little different values for c.png(which is JPG file) then other modules - and I don't mean that cv2 gives colors in BGR instead of RGB - and later this gives correct result. Probably cv2 uses different C library to read JPG.

How to convert JPG to fits in Python?

I am trying to convert a raw cr2 image into .fits using imageio and PIL. But, I am unable to convert the image into .fits format
I am currently converting .cr2 into jpg as I am unable to convert .cr2 into .fits. (if possible = best). After conversion into jpg, I am opening the file and splitting the r,g,b into 3 different arrays and pass each array to a separate .fits file but, whenever I try to pass the entire data of r,g,b combined to array it never works.
import numpy as np
from astropy.io import fits
import matplotlib.pyplot as plt
from PIL import Image
im = imageio.imread('E:\FYP\cr.cr2', format="RAW-FI")
imageio.imwrite(r'E:\FYP\1.jpg', im)
image = Image.open(r'E:\FYP\1.jpg')
xsize, ysize = image.size
print("Image size: {} x {}".format(xsize, ysize))
plt.imshow(image)
r, g, b = image.split()
r_data = np.array(r.getdata()) # data is now an array of length ysize*xsize
g_data = np.array(g.getdata())
b_data = np.array(b.getdata())
print(r_data.shape)
r_data = r_data.reshape(ysize, xsize)
g_data = g_data.reshape(ysize, xsize)
b_data = b_data.reshape(ysize, xsize)
red = fits.PrimaryHDU(data=r_data)
red.header['LATOBS'] = "32:11:56" # add spurious header info
red.header['LONGOBS'] = "110:56"
red.writeto(r'E:\FYP\red.fits')
It gives me single-colored .fits image while i want is an image with all R,G,B colors as .fits image.

Images saved as HDF5 arent colored

Im currently working on a program that converts text files and jpg-images into the HDF5-Format. Opened with the HDFView 3.0, it seems that the Images are only saved in greyscales.
hdf = h5py.File("Sample.h5")
img = Image.open("Image.jpg")
data = np.asarray((img), dtype="uint8")
hdf.create_dataset("Photos/Image 1", data=data, dtype='uint8')
dset = hdf.get("Photos/Image 1")
dset.attrs['CLASS'] = 'IMAGE'
dset.attrs['IMAGE_VERSION'] = '1.2'
arr = np.asarray([0, 255], dtype=np.uint8)
dset.attrs['IMAGE_MINMAXRANGE'] = list(arr)
dset.attrs['IMAGE_SUBCLASS'] = 'IMAGE_TRUECOLOR'
dset.attrs['INTERLACE_MODE'] = 'INTERLACE_PIXEL'
In python it is possible to show the original colored image with the Image.show() method:
hdf = h5py.File("Sample.h5")
array = np.array(list(hdf.get("Photos/Image 1")))
img = Image.fromarray(array.astype('uint8'))
img.show()
First part of the question.
Don't ask me why but maybe one of the maintainers of HDFview can step up.
To enable HDFview to correctly display images the attributes must be finite length string to be correctly interpreted.
Use np.string_(<string>) from numpy package
import h5py
import numpy as np
from PIL import Image
hdf = h5py.File("Sample.h5",'w')
img = Image.open("Image.jpg")
data = np.asarray((img), dtype="uint8")
hdf.create_dataset("Photos/Image 1", data=data, dtype='uint8')
dset = hdf.get("Photos/Image 1")
dset.attrs['CLASS'] = np.string_('IMAGE')
dset.attrs['IMAGE_VERSION'] = np.string_('1.2')
arr = np.asarray([0, 255], dtype=np.uint8)
dset.attrs['IMAGE_MINMAXRANGE'] = list(arr)
dset.attrs['IMAGE_SUBCLASS'] = np.string_('IMAGE_TRUECOLOR')
dset.attrs['INTERLACE_MODE'] = np.string_('INTERLACE_PIXEL')
hdf.close()
This gives in HDFview by double clicking on dataset "Image 1"
Second question.
I suppose you are using the PIL package
The function fromarray expects the "mode of the image" see https://pillow.readthedocs.io/en/3.1.x/handbook/concepts.html#concept-modes
In your case it's RBG
Therefore
import h5py
import numpy as np
from PIL import Image
hdf = h5py.File("Sample.h5",'r')
array = np.array(list(hdf.get("Photos/Image 1")))
img = Image.fromarray(array.astype('uint8'), 'RGB')
img.show()
will give you

how to convert an RGB image to numpy array?

I have an RGB image. I want to convert it to numpy array. I did the following
im = cv.LoadImage("abc.tiff")
a = numpy.asarray(im)
It creates an array with no shape. I assume it is a iplimage object.
You can use newer OpenCV python interface (if I'm not mistaken it is available since OpenCV 2.2). It natively uses numpy arrays:
import cv2
im = cv2.imread("abc.tiff",mode='RGB')
print(type(im))
result:
<type 'numpy.ndarray'>
PIL (Python Imaging Library) and Numpy work well together.
I use the following functions.
from PIL import Image
import numpy as np
def load_image( infilename ) :
img = Image.open( infilename )
img.load()
data = np.asarray( img, dtype="int32" )
return data
def save_image( npdata, outfilename ) :
img = Image.fromarray( np.asarray( np.clip(npdata,0,255), dtype="uint8"), "L" )
img.save( outfilename )
The 'Image.fromarray' is a little ugly because I clip incoming data to [0,255], convert to bytes, then create a grayscale image. I mostly work in gray.
An RGB image would be something like:
out_img = Image.fromarray( ycc_uint8, "RGB" )
out_img.save( "ycc.tif" )
You can also use matplotlib for this.
from matplotlib.image import imread
img = imread('abc.tiff')
print(type(img))
output:
<class 'numpy.ndarray'>
As of today, your best bet is to use:
img = cv2.imread(image_path) # reads an image in the BGR format
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR -> RGB
You'll see img will be a numpy array of type:
<class 'numpy.ndarray'>
Late answer, but I've come to prefer the imageio module to the other alternatives
import imageio
im = imageio.imread('abc.tiff')
Similar to cv2.imread(), it produces a numpy array by default, but in RGB form.
You need to use cv.LoadImageM instead of cv.LoadImage:
In [1]: import cv
In [2]: import numpy as np
In [3]: x = cv.LoadImageM('im.tif')
In [4]: im = np.asarray(x)
In [5]: im.shape
Out[5]: (487, 650, 3)
You can get numpy array of rgb image easily by using numpy and Image from PIL
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
im = Image.open('*image_name*') #These two lines
im_arr = np.array(im) #are all you need
plt.imshow(im_arr) #Just to verify that image array has been constructed properly
When using the answer from David Poole I get a SystemError with gray scale PNGs and maybe other files. My solution is:
import numpy as np
from PIL import Image
img = Image.open( filename )
try:
data = np.asarray( img, dtype='uint8' )
except SystemError:
data = np.asarray( img.getdata(), dtype='uint8' )
Actually img.getdata() would work for all files, but it's slower, so I use it only when the other method fails.
load the image by using following syntax:-
from keras.preprocessing import image
X_test=image.load_img('four.png',target_size=(28,28),color_mode="grayscale"); #loading image and then convert it into grayscale and with it's target size
X_test=image.img_to_array(X_test); #convert image into array
OpenCV image format supports the numpy array interface. A helper function can be made to support either grayscale or color images. This means the BGR -> RGB conversion can be conveniently done with a numpy slice, not a full copy of image data.
Note: this is a stride trick, so modifying the output array will also change the OpenCV image data. If you want a copy, use .copy() method on the array!
import numpy as np
def img_as_array(im):
"""OpenCV's native format to a numpy array view"""
w, h, n = im.width, im.height, im.channels
modes = {1: "L", 3: "RGB", 4: "RGBA"}
if n not in modes:
raise Exception('unsupported number of channels: {0}'.format(n))
out = np.asarray(im)
if n != 1:
out = out[:, :, ::-1] # BGR -> RGB conversion
return out
I also adopted imageio, but I found the following machinery useful for pre- and post-processing:
import imageio
import numpy as np
def imload(*a, **k):
i = imageio.imread(*a, **k)
i = i.transpose((1, 0, 2)) # x and y are mixed up for some reason...
i = np.flip(i, 1) # make coordinate system right-handed!!!!!!
return i/255
def imsave(i, url, *a, **k):
# Original order of arguments was counterintuitive. It should
# read verbally "Save the image to the URL" — not "Save to the
# URL the image."
i = np.flip(i, 1)
i = i.transpose((1, 0, 2))
i *= 255
i = i.round()
i = np.maximum(i, 0)
i = np.minimum(i, 255)
i = np.asarray(i, dtype=np.uint8)
imageio.imwrite(url, i, *a, **k)
The rationale is that I am using numpy for image processing, not just image displaying. For this purpose, uint8s are awkward, so I convert to floating point values ranging from 0 to 1.
When saving images, I noticed I had to cut the out-of-range values myself, or else I ended up with a really gray output. (The gray output was the result of imageio compressing the full range, which was outside of [0, 256), to values that were inside the range.)
There were a couple other oddities, too, which I mentioned in the comments.
We can use following function of open CV2 to convert BGR 2 RGB format.
RBG_Image = cv2.cvtColor(Image, cv.COLOR_BGR2RGB)
Using Keras:
from keras.preprocessing import image
img = image.load_img('path_to_image', target_size=(300, 300))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
images = np.vstack([x])
Try timing the options to load an image to numpy array, they are quite similar. Go for plt.imread for simplicity and speed.
def time_this(function, times=100):
cum_time = 0
for t in range(times):
st = time.time()
function()
cum_time += time.time() - st
return cum_time / times
import matplotlib.pyplot as plt
def load_img_matplotlib(img_path):
return plt.imread(img_path)
import cv2
def load_img_cv2(img_path):
return cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
from PIL import Image
import numpy as np
def load_img_pil(img_path):
img = Image.open(img_path)
img.load()
return np.asarray( img, dtype="int32" )
if __name__=='__main__':
img_path = 'your_image_path'
for load_fn in [load_img_pil, load_img_cv2, load_img_matplotlib]:
print('-'*20)
print(time_this(lambda: load_fn(img_path)), 10000)
Result:
--------------------
0.0065201687812805175 10000 PIL, as in [the second answer][1]https://stackoverflow.com/a/7769424/16083419)
--------------------
0.0053211402893066405 10000 CV2
--------------------
0.005320906639099121 10000 matplotlib
You can try the following method. Here is a link to the docs.
tf.keras.preprocessing.image.img_to_array(img, data_format=None, dtype=None)
from PIL import Image
img_data = np.random.random(size=(100, 100, 3))
img = tf.keras.preprocessing.image.array_to_img(img_data)
array = tf.keras.preprocessing.image.img_to_array(img)

Categories