Images saved as HDF5 arent colored - python

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

Related

How to read the tiles into the tensor if the images are tif float32?

I am trying to run a CNN where the input images have three channels (rgb) and the label (target) images are grayscale images (1 channel). The input and label images are in float32 and tif format.
I got the list of image and label tile pairs as below:
def get_train_test_lists(imdir, lbldir):
imgs = glob.glob(imdir+"/*.tif")
dset_list = []
for img in imgs:
filename_split = os.path.splitext(img)
filename_zero, fileext = filename_split
basename = os.path.basename(filename_zero)
dset_list.append(basename)
x_filenames = []
y_filenames = []
for img_id in dset_list:
x_filenames.append(os.path.join(imdir, "{}.tif".format(img_id)))
y_filenames.append(os.path.join(lbldir, "{}.tif".format(img_id)))
print("number of images: ", len(dset_list))
return dset_list, x_filenames, y_filenames
train_list, x_train_filenames, y_train_filenames = get_train_test_lists(img_dir, label_dir)
test_list, x_test_filenames, y_test_filenames = get_train_test_lists(test_img_dir, test_label_dir)
from sklearn.model_selection import train_test_split
x_train_filenames, x_val_filenames, y_train_filenames, y_val_filenames =
train_test_split(x_train_filenames, y_train_filenames, test_size=0.1, random_state=42)
num_train_examples = len(x_train_filenames)
num_val_examples = len(x_val_filenames)
num_test_examples = len(x_test_filenames)
In order to read the tiles into tensor, firstly I defined the image dimensions and batch size:
img_shape = (128, 128, 3)
batch_size = 2
I noticed that there is no decoder in tensorflow for tif image based on this link. tfio.experimental.image.decode_tiff can be used but it decodes to unit8 tensor.
here is a sample code for png images:
def _process_pathnames(fname, label_path):
# We map this function onto each pathname pair
img_str = tf.io.read_file(fname)
img = tf.image.decode_png(img_str, channels=3)
label_img_str = tf.io.read_file(label_path)
# These are png images so they return as (num_frames, h, w, c)
label_img = tf.image.decode_png(label_img_str, channels=1)
# The label image should have any values between 0 and 9, indicating pixel wise
# cropt type class or background (0). We take the first channel only.
label_img = label_img[:, :, 0]
label_img = tf.expand_dims(label_img, axis=-1)
return img, label_img
Is it possible to modify this code by tf.convert_to_tensor or any other option to get float32 tensor from tif images? (I asked this question before, but I don't know how to integrate tf.convert_to_tensor with the mentioned codes)
You can read almost any image format and convert it to a numpy array with the Pillow image package:
from PIL import Image
import numpy as np
img = Image.open("image.tiff")
img = np.array(img)
print(img.shape, img.dtype)
# (986, 1853, 4) uint8
You can integrate this function into your code and then convert the numpy array to a tensorflow tensor as well as doing the appropriated image conversions.
Side note: you can simplify a lot your get_train_test_lists function using the pathlib package (which is integrated to Python3 like os but much simpler to use).
def get_train_test_lists(imdir, lbldir):
x_filenames = list(Path(imdir).glob("*.tif"))
y_filenames = [Path(lbldir) / f.name for f in x_filenames]
dset_list = [f.stem for f in x_filenames]
return dset_list, x_filenames, y_filenames
Note that x_filenames and y_filenames are now absolute paths but this shouldn't be an issue in your code.

Input multiple images in a shape of an array

import os
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
from PIL import Image
train_path_positive = "/content/Dataset_P5/Train/Positive"
positive_patches = []
for filename in os.listdir(train_path_positive):
image = cv2.imread(train_path_positive + "/" +filename,0)
image = cv2.resize(image, (500,500))
print(image.shape)
positive_patches.append(image)
positive_patches_array = np.array(positive_patches)
I have 15 pictures in jpg format
When i try to print the shape, I got (15,) and
I was trying to input those picture and store it on array with the format (15, 500,500)
You need to preallocate a numpy array
# Use your required dtype in below line
positive_patches_array = np.empty((15,500,500), dtype='uint16')
for num, filename in enumerate(os.listdir(train_path_positive)):
image = cv2.imread(train_path_positive + "/" +filename,0)
image = cv2.resize(image, (500,500))
positive_patches_array[num, :, :] = image

How to efficiently load thousands of HD photos into pandas df and convert to HDF?

I want to load thousands of animal images into a pandas df, add features and maybe convert to HDF.
I tried the following approach using cv2.imread()
import cv2
import os
import numpy as np
import pandas as pd
def images_to_hdf(folder_path, label):
"""
Save a folder of images to hdf format.
Args:
folder_path: Path to folder containing images.
label: A string of the image content.
Return:
None
"""
image_data = [np.array(cv2.imread(folder_path + img)) for img in os.listdir(folder_path)]
data = pd.DataFrame()
data['Images'] = image_data
data['Label'] = label
data.to_hdf(path, key)
But it's taking longer than 1 minute for reading only 100 images plus an error(too much numerical value to store...) and I'm sure that's a very inefficient way of doing it.
I tried np.fromfile() instead of cv2.imread() it's ultra fast in comparison(I'm not really sure what it does) but it returns rank1 arrays and I want to have image 3 dimensional data stored in a pd dataframe to add labels which I'll be using to train a classifier and I'm thinking this might be a way of doing it.
With the help of h5py you can directly save your images and labels into a hdf5 file (without using pandas). Here's one example how to do it (adaptation from here):
import os
import glob
import cv2
import h5py
import numpy as np
def images_to_hdf5(images_path='/path/to/images',
label=0,
hdf5_path='/path/to/hdf5_file/file.hdf5'):
image_names = glob.glob(os.path.join(images_path, '*.jpg'))
n = len(image_names)
labels = [label]*n
hdf5_file = h5py.File(hdf5_path, mode='w')
hdf5_file.create_dataset("Images", (n, 3, 224, 224), np.int8)
hdf5_file.create_dataset("Labels", (n,), np.int8)
hdf5_file["Labels"][...] = labels
for i, image_name in enumerate(image_names):
img = cv2.imread(image_name)
img = cv2.resize(img, (224, 224)) # shape (224, 224, 3)
img = np.rollaxis(img, 2)[None] # shape (1, 3, 224, 224)
hdf5_file["Images"][i, ...] = img
hdf5_file.close()
To open it:
hdf5_file = h5py.File(hdf5_path, "r")
To access e.g. the first image and label:
hdf5_file["Images"][0]
hdf5_file["Labels"][0]
#hdf5_file.close()

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.

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