Loading multiple image URLs to 4D numpy array - python

I have around 300 Images which I am fetching from Image URL. Each Image (RGB) is 256x256x3. I want a numpy array that I can feed into CNN model of shape = (300,256,256,3). How to do this in Python?
This code below is giving me error
X_data = np.array([])
print('Iterating across ',len(df_train),' rows')
for index,row in tqdm_notebook(df_train.iterrows()):
img = {}
try:
img = img_to_array(load_img(BytesIO(requests.get(row['IMAGE_URL_1']).content), target_size=(256, 256)))
X_data.append(img)
except Exception:
print('Error in Fetching Image_URL_1 = ',row['IMAGE_URL_1'],' lot = ',row['LOT_NUMBER'])
pass

You can the Pillow library for reading the image data from URL content. Here's is a simple example.
from io import BytesIO
from PIL import Image
import numpy as np
import requests
X_data = []
print('Iterating across ',len(df_train),' rows')
for index,row in tqdm_notebook(df_train.iterrows()):
try:
# https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#PIL.Image.Image.resize
img = Image.open(BytesIO(requests.get(row['IMAGE_URL_1']).content)).resize((256, 256), PIL.Image.LANCZOS)
X_data.append(np.array(img))
except Exception:
print('Error in Fetching Image_URL_1 = ',row['IMAGE_URL_1'],' lot = ',row['LOT_NUMBER'])
pass

You are right: After the Code of Sanjay you need to convert the list to an array.
You do this by simply writing X_data_4D=np.stack(X_data, axis=0) after the for-loop.
Like that, you will convert a list of (256,256,3) images to one (300,256,256,3) array.

Related

Large .tif image dataset - How do I convert it to a usable dataset for a CNN using Tensorflow with labels in a separate CSV? Python 3

I have been trying to find a way to convert my 270,000 .tif images and their corresponding labels into a usable Tensorflow dataset for a few days now and I have decided to post here for help.
What I have currently done is just taken a subset of the images and converted them to numpy arrays and then put them into a list. I also did this with the labels. So I now have a pandas dataframe that has two columns; id (the images file name) and the label (a 1 or a 0) and a list of numpy arrays. Below is the code that I use to do that.
from PIL import Image
import matplotlib.pyplot as plt
import glob
import tensorflow as tf
import pandas as pd
import re
path = "./training_images/*"
img_names = []
img_arr_list = []
for file in glob.glob(path):
img = Image.open(file)
img_arr = np.asarray(img).astype("float32")
img_arr_list.append(img_arr)
name = re.split(' |/|\\\\' , file)
name = name[2][:-4]
img_names.append(name)
labels = pd.read_csv("train_labels.csv")
# Convert list of file names to a pandas dataframe and change the 0 field to "id" to match the field
# name in labels
name_df = pd.DataFrame(img_names)
name_df = name_df.rename(columns={0:"id"})
# inner join to get only the images names and their labels that exist in the image subset
labels_df = pd.merge(name_df,labels,on='id',how='inner')
From here, I have tried to convert my list of numpy arrays as well is it's corresponding labels into a Tensorflow dataset that I can then preprocess and then feed into a CNN that I will later create. When I execute the below code.....
train_dataset = tf.data.Dataset.from_tensor_slices((img_arr_list, labels_df))
.... I get this error..
ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type int).
I believe I am doing this incorrectly and need to instead be doing something where I iterate over my list of numpy arrays and my labels_df to create a tensor during each loop in the for loop and then append them to a Tensorflow dataset. Below is some pseudo-code that I have thought of using.
for row in [0,len(img_arr_list)]:
tensor = tf.data.Dataset.from_tensor_slices((img_arr_list[row], labels_df.iloc[row]))
<append tensor to tensorflow dataset>
Any help would be appreciated. I am also open to using a pytorch-based approach.
Load data like in this article:
def create_dataset(img_folder):
img_data_array=[]
class_name=[]
for dir1 in os.listdir(img_folder):
for file in os.listdir(os.path.join(img_folder, dir1)):
image_path= os.path.join(img_folder, dir1, file)
image= cv2.imread( image_path, cv2.COLOR_BGR2RGB)
image=cv2.resize(image, (IMG_HEIGHT, IMG_WIDTH),interpolation = cv2.INTER_AREA)
image=np.array(image)
image = image.astype('float32')
image /= 255
img_data_array.append(image)
class_name.append(dir1)
return img_data_array, class_name

How to get only possible non overlapping blocks from images of any given size and show them in Python

I have images of varying resolutions, and I would like to extract non-overlapping blocks from these images.
However, because the images have not fixed size and my block size is big (64x64), I would like to get only non-overlapping blocks that could be found in an image. If the block exceeds the image borders, I don't want to get them.
I tried the view_as_blocks function from scikit-image as below:
from skimage.util import view_as_blocks
for elem in listOfFiles:
# Reading image
print("Reading image "+elem)
img = cv2.imread(elem)
print(img.shape) #for example, one image is (2059, 2059, 3)
Blocks = view_as_blocks(img, block_shape=(64, 64, 3))
The code returns the following error:
ValueError: 'block_shape' is not compatible with 'arr_in'
I also tried the Patch Extractor from scikit-learn, as follows:
from sklearn.feature_extraction import image
import cv2
import numpy
for elem in listOfFiles:
# Reading image
print("Reading image "+elem)
img = cv2.imread(elem)
print(img.shape)
pe = image.PatchExtractor(patch_size=(64,64))
pe_fit = pe.fit(img)
pe_trans = pe.transform(img)
print('Patches shape: {}'.format(pe_trans.shape))
The error that returns to me is the following:
ValueError: negative dimensions are not allowed
the function image.extract_patches_2d from sklearns runs perfectly, but unfortunatelly it works only for overlapping blocks as you can see here.
These functions also don't help me because I also want to show the image with these blocks selected, so I also need another matrix with coordinates of such blocks and show the selected blocks.
Is that possible to that in Python?
Since you don't care about the incomplete blocks at the edges, you can manually check the number of blocks along each dimension, and crop your image to that shape:
from skimage.util import view_as_blocks
for elem in listOfFiles:
# Reading image
print("Reading image "+elem)
img = cv2.imread(elem)
print(img.shape) #for example, one image is (2059, 2059, 3)
block_shape = np.array((64, 64, 3))
nblocks = np.array(img.shape) // block_shape # integer division
crop_r, crop_c, crop_ch = nblocks * block_shape
cropped_img = img[:crop_r, :crop_c, :crop_ch]
Blocks = view_as_blocks(cropped_img, block_shape=(64, 64, 3))

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()

image file is truncated (bytes not processed)

I was trying to read a batch of images in Python and resize them. It gives error as in the title, which I think is because the images are in different sizes. How do I solve this? The code is as following:
from PIL import Image
import numpy as np
for x in range(1,10):
fin = 'path/image%03d.jpg'%(x,)
im = Image.open(fin)
im1 = im.resize((400,400),Image.ANTIALIAS)
fout = 'path/resize/image%03d.jpg'%(x,)
im1.save(fout)

To get image from cifat10-dataset

I am trying to get images from cifar10-dataset. When i rebuild image from array,
i see 9 same images in one picture, i don't know what is the problem.
When i load image from data, single_img shape (3072,). After that, i reshape
my single_img varible (32, 32, 3). I don't know where is the problem.
Here my code;
import cPickle
from PIL import Image
import numpy as np
f = open("/home/leo/Downloads/cifar-10-batches-py/data_batch_1", "rb")
tupled_data= cPickle.load(f)
f.close()
img = tupled_data['data']
single_img = np.array(img[0])
single_img_reshaped = single_img.reshape(32, 32 ,3)
j = Image.fromarray(single_img_reshaped)
j.save("/home/leo/Desktop/blabla.bmp")
Example image;
Be sure to be careful about the format of the pixel array of the image..
[R....G....B]
So you just change its format to
[[[R,G,B],....,[R,G,B]]
[[R,G,B],....,[R,G,B]]
[[R,G,B],....,[R,G,B]]]
But
single_img_reshaped = single_img.reshape(32, 32 ,3)
don't do it like before.

Categories