How to convert a set of images into a numpy array? - python

I'm building my first Neural Network taking as example those on the book "Deep Learning with Python - Francois Chollet" and I immediately found my first issue. When the author imported the MNIST dataset he also printed the shape, obtaining that it is a 1D tensor. I'm trying to import a folder containing all caddies images to check whether the NN catalogs them correctly or not. The problem is that I can't obtain a 1D tensor from all these images. I've tried to convert each of them with numpy.asarray(my_image). I also tried to convert the whole list but turns out it became a tuple... any hint?
train_label = 'Caddies'
train_images = list()
for filename in listdir('/content/drive/MyDrive/Data set/Caddies/'):
img_data = image.imread('/content/drive/MyDrive/Data set/Caddies/' +\
filename)
img_data = np.asarray(img_data)
#print(str(img_data.dtype) + str(img_data.shape))
train_images.append(img_data)
print('> loaded %s images' % (len(train_images)))
train_images = np.array(train_images)
print(train_images.shape())

If you want to feed your images to a neural network, I suggest you don't use a for-loop to load all your images in memory. Rather, you can use this function:
import tensorflow as tf
import os
os.chdir('pictures')
files = tf.data.Dataset.list_files('*jpg')
def load_images(path):
image = tf.io.read_file(path)
image = tf.io.decode_jpeg(image)
image = tf.image.convert_image_dtype(image, tf.float32) # optional
image = tf.image.resize(image, (224, 224)) # optional
return image
ds = files.map(load_images).batch(1)
next(iter(ds)).shape
(1, 224, 224, 3)

Related

How to load images with different image shape to tf.data pipe?

My goal is to have a preprocessing layers so it can handle any image size. This is because the data set that I use have 2 different image shape. The solution is simple, just resize it when I load the image. However, I believe this wont work when the model is deployed, I can't do manual resize like that. So I must use preprocessing layers.
The docs I used
What I've tried:
Put the preprocessing layers part of the model, it does not work.
I am thinking to use TensorSliceDataset.map(resize_and_rescale).
The problem is I need to convert the [tensor image 1, tensor image 2] to TensorSliceDataset. However, I can't convert it.
What I've tried:
tf.data.Dataset.from_tensor_slices((X_train, y_train))
It throws error
InvalidArgumentError: {{function_node __wrapped__Pack_N_9773_device_/job:localhost/replica:0/task:0/device:GPU:0}} Shapes of all inputs must match: values[0].shape = [258,320,3] != values[23].shape = [322,480,3]
[[{{node Pack}}]] [Op:Pack] name: component_0
The load images function:
def load_images(df):
paths = df['path'].values
X = []
for path in paths:
raw = tf.io.read_file(path)
img = tf.image.decode_png(raw, channels=3)
X.append(img)
y = df['kind'].cat.codes
return X, y
As far as I understand you wish to train on both image sizes simultaneously. The simplest way is probably to create two different datasets for each image size and concatenate them after the batching as follows:
dataset_1 = tf.data.Dataset.from_tensor_slices((X_train_1, y_train_1))
dataset_1 = dataset_1.batch(batch_size_1)
dataset_2 = tf.data.Dataset.from_tensor_slices((X_train_2, y_train_2))
dataset_2 = dataset_2.batch(batch_size_2)
dataset = dataset_1.concatenate(dataset_2)
dataset = dataset.shuffle(shuffle_buffer_size)
This case each batch consists of images of the same size. If you use .repeat() do not forget to put if after the concatination.
You need to use ragged tensors to handle different image sizes:
dataset = tf.data.Dataset.from_tensor_slices((tf.ragged.constant(img_list), label_list))
dataset = dataset.apply(tf.data.experimental.dense_to_ragged_batch(batch_size=3))
Example

How to get an image to array, Tensorflow 1.9

So I have to use Tensorflow 1.9 for system specific reasons.
I want to train a cnn with a custom dataset consisting of images.
The folder structure looks very much like this:
./
+ circles
- circle-0.jpg
- circle-1.jpg
- ...
+ hexagons
- hexagon-0.jpg
- hexagon-1.jpg
- ...
+ ...
So the example I have to work with uses MNIST and has these two particular lines of code:
mnist_dataset = tf.keras.datasets.mnist.load_data('mnist_data')
(x_train, y_train), (x_test, y_test) = mnist_dataset
In my work, I also have to use this data format (x_train, y_train), (x_test, y_test), which seems to be quite common. As far as I was able to find out up to now, the format of those datasets are: (image_data, label), and is something like ((60000, 28, 28), (60000,)), at least with the MNIST dataset. The image_data here is supposedly of dtype uint8 (according to this post). I was able to find out, that a tf.data.Dataset() object looks like the tuples I need here (image_data, label).
So far so good. But a few questions arise from this information which I wasn't able to figure out yet, and where I would kindly request your help:
(60000, 28, 28) means 60k a 28 x 28 image value array, right?
If 1. is right, how do I get my images (like in the directory structure I described above) into this format? Is there a function which yields an array that I can use like that?
I know I need some kind of generator function which should get all the images with label, because in Tensorflow 1.9 the tf.keras.utils.image_dataset_from_directory() does not seem to exist yet.
How do the labels actually look like? For example, with my directory structure, would I have something like this:
(A)
File
Label
circle-0.jpg
circle
circle-233.jpg
circle
hexagon-1.jpg
hexagon
triangle-12.jpg
triangle
or (B)
File
Label
circle-0.jpg
circle-0
circle-233.jpg
circle-233
hexagon-1.jpg
hexagon-1
triangle-12.jpg
triangle-12
, where the respective image is already converted to a "(60000, 28, 28)" format? It seems as if I need to create all my functions by myself, since there does not seem to be a good function which takes a directory structure like mine to a dataset which can be utilized by Tensorflow 1.9, or is there?. I know of the tf.keras.preprocessing.image.ImageDataGenerator and image_dataset_from_directory as well as flow_from_directory(), however, all of them don't seem to bring me my desired dataset value tuple format.
I would really appreciate any help!
You have to build a custom data generator for that. If you have two arrays, train_paths containing the paths to images and train_labels containing the labels for the images, then this function (datagen) would yield the images as array and with their respective label as a tuple (image_array, label).
And I have also added a way to integer-encode your labels, with a dictionary encode_label
For example, train_paths and train_labels should look like this:
train_paths = np.array(['path/to/image1.jpg','path/to/image2.jpg','path/to/image3.jpg'])
train_labels = np.array(['circle','square','hexagon'])
where the image of path 'path/to/image1.jpg' has a label of 'circle', the image of path 'path/to/image2.jpg' has a label of 'square'.
This generator function will return data as a batch and you can write your custom augmentation techniques as well (inside the augment function)
import tensorflow as tf
# Hyperparameters
HEIGHT = 224 # Image height
WIDTH = 224 # Image width
CHANNELs = 3 # Image channels
# This function will encode your labels
encode_label = {'hexagon':0, 'circle':1, 'square':2}
def augment(image):
# All your augmentation techniques are done here
return image
def encode_labels(labels):
encoded = []
for label in labels:
encoded.append(encode_label[label])
return encoded
def open_images(paths):
'''
Given a list of paths to images, this function loads
the images from the paths, then augments them, then returns it as a batch
'''
images = []
for path in paths:
image = tf.keras.preprocessing.image.load_img(path, target_size=(HEIGHT, WIDTH, CHANNELS))
image = np.array(image)
image = augment(image)
images.append(image)
return np.array(images)
# This is the data generator
def datagen(paths, labels, batch_size=32):
for x in range(0,len(paths), batch_size):
# Load batch of images
batch_paths = paths[x:x+batch_size]
batch_images = open_images(batch_paths)
# Load batch of labels
batch_labels = labels[x:x+batch_size]
batch_labels = encode_labels(batch_labels)
batch_labels = np.array(batch_labels, dtype='float').reshape(-1)
yield batch_images, batch_labels
If you cannot get tf.keras.preprocessing.image.load_img working in your tensorflow version, try using an alternative method to load image and resize it. One alternative way would be to load the image with matplotlib and then resizing it with skimage. So the open_images function would be this:
import matplotlib
from skimage.transform import resize
def open_images(paths):
'''
Given a list of paths to images, this function loads
the images from the paths, then augments them, then returns it as a batch
'''
images = []
for path in paths:
image = matplotlib.image.imread(path)
image = np.array(image)
image = resize(image, (HEIGHT, WIDTH, CHANNELS))
image = augment(image)
images.append(image)
return np.array(images)

Image Shape Issue with Tensorflow and Numpy

I am trying to run a basic GAN Neural Network from: https://www.tensorflow.org/tutorials/generative/dcgan
Following along with the code in here it works fine when I use the mnist dataset. I would like to try this with my own custom images instead.
I am loading the images as follows:
import glob
import imageio
import matplotlib.pyplot as plt
import numpy as np
import os
import PIL
from tensorflow.keras import layers
import time
import tensorflow as tf
from PIL import Image
from IPython import display
#Set Max image pixels to none to avoid pixel limit breach
Image.MAX_IMAGE_PIXELS = None
#Create empty list for images
images = []
#Glob together images from file and create numpy aray with them
for f in glob.iglob("...Images/*"):
images.append(np.asarray(Image.open(f)))
#Load image array into empty list
images = np.array(images)
#Show array shape
images.shape
Output of shape is:
(100,)
Following the tensorflow doc to load and preprocess images they use the following:
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # Normalize the images to [-1, 1]
BUFFER_SIZE = 60000
BATCH_SIZE = 256
# Batch and shuffle the data
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
My question is how can I reshape my current batch set of images to match the input needed to follow along with the doc?
If I try to just plug in my own data I get:
ValueError: cannot reshape array of size 100 into shape (100,28,28,3)
Maybe the images you are loading have a different number of channels. Try printing out the shape of each image array in your for loop. Anyway, I would recommend using open opencv to read your images. It is pretty straightforward and apparently faster than PIL:
import glob
import cv2
Image.MAX_IMAGE_PIXELS = None
images = []
for f in glob.iglob("images/*"):
images.append(np.asarray(cv2.imread(f)))
images = np.array(images)
images.shape
# (3, 100, 100, 3) 3 images 100x100x3

how to fix this error : numpy.ndarray " object has no attribute "append"

I hope that you are doing well. I tried to run the below code. I am getting this error " numpy.ndarray object has no attribute append". I tried to used the solution recommended in other questions, like numpy.append(), numpy.concatenate() but I could not solve the problem.
from keras.applications import VGG16
from keras.applications import imagenet_utils
from keras.preprocessing.image import img_to_array
from keras.preprocessing.image import load_img
from sklearn.preprocessing import LabelEncoder
from hdf5datasetwriter import HDF5DatasetWriter
from imutils import paths
import progressbar
import argparse
import random
import numpy as np
import os
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required= True,
help=" path to the input dataset ")
ap.add_argument("-o", "--output", required= True,
help=" path to output HDF5 file ")
ap.add_argument("-b","--batch_size", type= int, default=32,
help =" batch size of images to be passed through network ")
ap.add_argument("-s","--buffer_size", type =int, default=1000,
help=" size of feature extraction buffer")
args= vars(ap.parse_args())
# store the batch size in a convenience variable
bs = args["batch_size"]
# grab the list of images that we will be describing then randomly shuffle them to
# allow for easy training and testing splits via array slicing during training time
print ("[INFO] loading images ...")
imagePaths= list(paths.list_images(args["dataset"]))
random.shuffle(imagePaths)
# extract the class labels from the images paths then encode the labels
labels = [p.split(os.path.sep)[-2] for p in imagePaths]
le= LabelEncoder()
labels= le.fit_transform(labels)
# load the VGG16 network
print("[INFO] loading network ...")
model= VGG16(weights="imagenet", include_top=False)
# initialize the HDF5 dataset writer then store the class label names in the
# dataset
dataset = HDF5DatasetWriter((len(imagePaths), 512*7*7), args["output"], dataKey="features",
bufSize= args["buffer_size"])
dataset.storeClassLabels(le.classes_)
# initialize the prograss bar
widgets = [" extracting features:", progressbar.Percentage(), " " , progressbar.Bar(),
" " , progressbar.ETA()]
pbar= progressbar.ProgressBar(maxval=len(imagePaths), widgets= widgets ).start()
# loop over the image patches
for i in np.arange(0, len(imagePaths),bs):
# extract the batch of images and labels, then initalize the
# list of actualimages that will be passed through the network for feature
# extraction
batchPaths= imagePaths[i:i + bs]
batchLabels = labels[i:i+bs]
batchImages = []
for (j, imagePath) in enumerate(batchPaths):
# load the input image using the keras helper utility
# while ensuring the image is resized to 224x224 pixels
image = load_img(imagePath, target_size = (224,224))
image = img_to_array(image)
# preprocess the image by (1) expanding the dimensions and
# (2) substracting the mean RGB pixel intensity from the imagenet dataset
image = np.expand_dims(image, axis =0)
#image = imagenet_utils.preprocess_input(image)
# add the image to the batch
batchImages.append(image)
# pass the images through the network and use the outputs as our
# actual featues
batchImages = np.vstack(batchImages)
features = model.predict(batchImages, batch_size = bs)
# reshape the features so that each image is represented by a flattened feature vector of the maxPooling2D outputs
features = features.reshape((features.shape[0], 512*7*7))
# add the features and the labels to HDF5 dataset
dataset.add(features, batchLabels)
pbar.update(i)
dataset.close()
pbar.finish()
I am getting this
I would like that you help me to solve this problem. thanks in to all in advance
From the documentation:
You should be doing something like batchImages = np.append(batchImages, image) because "append" is not actually a defined function on numpy arrays, which is what the error message is saying. If you want to insert somewhere specific in the array, np.insert(batchImages, index, image) works as well.
Numpy array instance doesn't have append function.
Call
numpy.append(your_arr, value_to_append)
which is class function.
You start with
batchImages = []
then successfully append to the list
batchImages.append(image)
then in the same iteration, you make an array and assign it to the same variable:
batchImages = np.vstack(batchImages)
next iteration, batchImages is no longer a list, so the append doesn't work!
I wonder if that vstack has the wrong indentation. Is it supposed to happen in the j iteration, or the i one?
Ignore the recommendations to use np.append. It should not be used iteratively, and is hard to use correctly. It's just a crude cover function for concatenate. vstack is better.

Tensorflow record: how to read and plot image values?

I have data in a tensorflow record file (data.record), and I seem to be able to read that data. I want to do something simple: just display the (png-encoded) image for a given example. But I can't get the image as a numpy array and simply show it. I mean, the data are in there how hard can it be to just pull it out and show it? I imagine I am missing something really obvious.
height = 700 # Image height
width = 500 # Image width
file_path = r'/home/train.record'
with tf.Session() as sess:
feature = {'image/encoded': tf.FixedLenFeature([], tf.string),
'image/object/class/label': tf.FixedLenFeature([], tf.int64)}
filename_queue = tf.train.string_input_producer([data_path], num_epochs=1)
reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue)
parsed_example = tf.parse_single_example(serialized_example, features=feature)
image_raw = parsed_example['image/encoded']
image = tf.decode_raw(image_raw, tf.uint8)
image = tf.cast(image, tf.float32)
image = tf.reshape(image, (height, width))
This seems to have extracted an image from train.record, with the right dimensions, but it is of type tensorflow.python.framework.ops.Tensor, and when I try to plot it with something like:
cv2.imshow("image", image)
I just get an error: TypeError: Expected cv::UMat for argument 'mat'.
I have tried using eval, as recommended at a link below:
array = image.eval(session = sess)
But it did not work. The program just hangs at that point (for instance if I put it after the last line above).
More generally, it seems I am just missing something, for even when I try to get the class label:
label = parsed_example['label']
I get the same thing: not the value, but an object of type tensorflow.python.framework.ops.Tensor. I can literally see the value is there when I type the name in my ipython notebook, but am not sure how to access it as an int (or whatever).
Note I tried this, which has some methods that seem to directly convert to a numpy array but they did not work: https://github.com/yinguobing/tfrecord_utility/blob/master/view_record.py
I just got the error there is no numpy method for a tensor object.
Note I am using tensorflow 1.13, Python 3.7, working in Ubuntu 18. I get the same results whether I run from Spyder or the command line.
Related questions
- How to print the value of a Tensor object in TensorFlow?
- https://github.com/aymericdamien/TensorFlow-Examples/issues/40
To visualize a single image from the TFRecord file, you could do something along the lines of:
import tensorflow as tf
import matplotlib.pyplot as plt
def parse_fn(data_record):
feature = {'image/encoded': tf.FixedLenFeature([], tf.string),
'image/object/class/label': tf.FixedLenFeature([], tf.int64)}
sample = tf.parse_single_example(data_record, feature)
return sample
file_path = r'/home/train.record'
dataset = tf.data.TFRecordDataset([file_path])
record_iterator = dataset.make_one_shot_iterator().get_next()
with tf.Session() as sess:
# Read and parse record
parsed_example = parse_fn(record_iterator)
# Decode image and get numpy array
encoded_image = parsed_example['image/encoded']
decoded_image = tf.image.decode_jpeg(encoded_image, channels=3)
image_np = sess.run(decoded_image)
# Display image
plt.imshow(image_np)
plt.show()
This assumes that the image is JPEG-encoded. You should use the appropriate decoding function (e.g. for PNG images, use tf.image.decode_png).
NOTE: Not tested.
import tensorflow as tf
with tf.Session() as sess:
r = tf.random.uniform([10, 10])
print(type(r))
# <class 'tensorflow.python.framework.ops.Tensor'>
a = r.eval()
print(type(a))
# <class 'numpy.ndarray'>
I could not reproduce your exact case. But, you need to evaluate Tensor to NumPy NDArray. As far as I understand, this is not an issue with TensorRecord. Colab link for the code.
Try tfrmaker, a tensorflow TFRrecord utility package. You can install the package with pip:
pip install tfrmaker
Then you could visualize the batches of your dataset like this example:
import os
from tfrmaker import images, display
# mapping label names with integer encoding.
LABELS = {"bishop": 0, "knight": 1, "pawn": 2, "queen": 3, "rook": 4}
# directory contains tfrecords
TFR_DIR = "tfrecords/chess/"
# fetch all tfrecords in the directory to a list
tfr_paths = [os.path.join(TFR_DIR,file) for file in os.listdir(TFR_DIR) if os.fsdecode(file).endswith(".tfrecord")]
# load one or more tfrecords as an iterator object.
dataset = images.load(tfr_paths, batch_size = 32)
# iterate one batch and visualize it along with labels.
databatch = next(iter(dataset))
display.batch(databatch, LABELS)
The package also has some cool features like:
dynamic resizing
splitting tfrecords into optimal shards
spliting training, validation, testing of tfrecords
count no of images in tfrecords
asynchronous tfrecord creation
NOTE: This package currently supports image datasets that are organised as directories with class names as sub directory names.

Categories