InvalidArgumentError: Expected image (JPEG, PNG, or GIF), got unknown format starting with 'B2.jpg' - numpy

I have a dataset of tfrecords that I'm trying to parse.
I am using this code to parse it:
image_size = [224,224]
def read_tfrecord(tf_record):
features = {
"filename": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring
"fun": tf.io.FixedLenFeature([], tf.string),
"label": tf.io.VarLenFeature(tf.int64),
}
tf_record = tf.parse_single_example(tf_record, features)
filename = tf.image.decode_jpeg(tf_record['filename'], channels=3)
filename = tf.cast(filename, tf.float32) / 255.0 # convert image to floats in [0, 1] range
filename = tf.reshape(filename, [*image_size, 3]) # explicit size will be needed for TPU
label = tf.cast(tf_record['label'],tf.float32)
return filename, label
def load_dataset(filenames):
option_no_order = tf.data.Options()
option_no_order.experimental_deterministic = False
dataset = tf.data.Dataset.from_tensor_slices(filenames)
dataset = dataset.with_options(option_no_order)
#dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=16)
dataset = dataset.interleave(tf.data.TFRecordDataset, cycle_length=32, num_parallel_calls=AUTO) # faster
dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTO)
return dataset
train_data=load_dataset(train_filenames)
val_data=load_dataset(val_filenames)
test_data=load_dataset(test_filenames)
After running this code I get:
train_data
<DatasetV1Adapter shapes: ((224, 224, 3), (?,)), types: (tf.float32, tf.float32)>
I was trying to see the images in the dataset with:
def display_9_images_from_dataset(dataset):
subplot=331
plt.figure(figsize=(13,13))
images, labels = dataset_to_numpy_util(dataset, 9)
for i, image in enumerate(images):
title = CLASSES[np.argmax(labels[i], axis=-1)]
subplot = display_one_flower(image, title, subplot)
if i >= 8:
break;
plt.tight_layout()
plt.subplots_adjust(wspace=0.1, hspace=0.1)
plt.show()
def dataset_to_numpy_util(dataset, N):
dataset = dataset.batch(N)
if tf.executing_eagerly():
# In eager mode, iterate in the Datset directly.
for images, labels in dataset:
numpy_images = images.numpy()
numpy_labels = labels.numpy()
break;
else: # In non-eager mode, must get the TF note that
# yields the nextitem and run it in a tf.Session.
get_next_item = dataset.make_one_shot_iterator().get_next()
with tf.Session() as ses:
numpy_images, numpy_labels = ses.run(get_next_item)
return numpy_images, numpy_labels
display_9_images_from_dataset(train_data)
But I get the error:
InvalidArgumentError: Expected image (JPEG, PNG, or GIF), got unknown format starting with 'B2.jpg'
[[{{node DecodeJpeg}}]]
[[IteratorGetNext_3]]
I'm a bit confused, one because it says that the file is jpg format and it asks for jpeg, which from my understanding are the same.
And also because I'm not sure how to view the images or even know if I parsed it correctly.

Extensions ".jpg" and ".jpeg" are different in terms of the validation check done by the API which is consuming it.
tf.image.decode_jpeg takes images with ".jpeg" extensions.
Try renaming your .jpg images with .jpeg extensions and it should start working.

Related

Tensorflow: How to set tensor shape after reading image from tfrecord file for data augmentation?

I have a tf.data.Dataset that I read from a tfrecords file like so:
import tensorflow as tf
# given an existing record_file
raw_dataset = tf.data.TFRecordDataset(record_file)
example_description = {
"height": tf.io.FixedLenFeature([], tf.int64),
"width": tf.io.FixedLenFeature([], tf.int64),
"channels": tf.io.FixedLenFeature([], tf.int64),
"image": tf.io.FixedLenFeature([], tf.string),
}
dataset = raw_dataset.map(
lambda example: tf.io.parse_single_example(example, example_description)
)
Next, I combine the features into a single image like so:
dataset = dataset.map(_extract_image_from_sample)
# and
def _extract_image_from_sample(sample):
height = tf.cast(sample["height"], tf.int32) # always 1038
width = tf.cast(sample["width"], tf.int32) # always 1366
depth = tf.cast(sample["channels"], tf.int32) # always 3
shape = [height, width, depth]
image = sample["image"]
image = decode_tf_image(image)
image = tf.reshape(image, shape)
return image
At this point, any image in the dataset has shape (None, None, None) (which surprises me, because I reshape them).
I believe this to be the cause of error, when I try to augment the dataset using tf.keras.preprocessing.image.ImageDataGenerator:
augmented_dataset = dataset.map(random_image_augmentation)
# and
image_data_generator = tf.keras.preprocessing.image.ImageDataGenerator(
rotation_range=45,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=5.0,
zoom_range=[0.9, 1.2],
fill_mode="reflect",
horizontal_flip=True,
vertical_flip=True,
)
def random_image_augmentation(image: tf.Tensor) -> tf.Tensor:
transform = image_data_generator.get_random_transform(img_shape=image.shape)
image = image_data_generator.apply_transform(image, transform)
return image
This results in the error message:
TypeError: in user code:
# ...
C:\Users\[PATH_TO_ENVIRONMENT]\lib\site-packages\keras_preprocessing\image\image_data_generator.py:778 get_random_transform *
tx *= img_shape[img_row_axis]
TypeError: unsupported operand type(s) for *=: 'float' and 'NoneType'
However, if I don't use graph mode, but eager mode, this works like a charm:
it = iter(dataset)
for i in range(3):
image = it.next()
image = random_image_augmentation(image.numpy())
This leads me to the conclusion that the main error is the missing shape information after reading in the dataset. But I don't know how to define it more explicitly than I already do. Any ideas?
Use tf.py_function to wrap the preprocessing function that requires the tensors to have a shape like so:
augmented_dataset = dataset.map(
lambda x: tf.py_function(random_image_augmentation, inp=[x], Tout=tf.float32),
num_parallel_calls=tf.data.experimental.AUTOTUNE
)
# and
def random_image_augmentation(image: tf.Tensor) -> tf.Tensor:
image = image.numpy() # now we can do this, because tensors have this function in eager mode
transform = image_data_generator.get_random_transform(img_shape=image.shape)
image = image_data_generator.apply_transform(image, transform)
return image
This worked for me, but I'm not sure whether it's the only or even best solution.

Tensorflow Serving: InvalidArgumentError: Expected image (JPEG, PNG, or GIF), got unknown format starting with 'AAAAAAAAAAAAAAAA'

I'm trying to prepare my custom Keras model for deploy to be used with Tensorflow Serving, but I'm running into issues with preprocessing my images.
When i train my model i use the following functions to preprocess my images:
def process_image_from_tf_example(self, image_str_tensor, n_channels=3):
image = tf.image.decode_image(image_str_tensor)
image.set_shape([256, 256, n_channels])
image = tf.cast(image, tf.float32) / 255.0
return image
def read_and_decode(self, serialized):
parsed_example = tf.parse_single_example(serialized=serialized, features=self.features)
input_image = self.process_image_from_tf_example(parsed_example["image_raw"], 3)
ground_truth_image = self.process_image_from_tf_example(parsed_example["gt_image_raw"], 1)
return input_image, ground_truth_image
My images are PNGs saved locally, and when i write them on the .tfrecord files i use
tf.gfile.GFile(str(image_path), 'rb').read()
This works, I'm able to train my model and use it for local predictions.
Now I want to deploy my model to be used with Tensorflow Serving. My serving_input_receiver_fn function looks like this:
def serving_input_receiver_fn(self):
input_ph = tf.placeholder(dtype=tf.string, shape=[None], name='image_bytes')
images_tensor = tf.map_fn(self.process_image_from_tf_example, input_ph, back_prop=False, dtype=tf.float32)
return tf.estimator.export.ServingInputReceiver({'input_1': images_tensor}, {'image_bytes': input_ph})
where process_image_from_tf_example is the same function as above, but i get the following error:
InvalidArgumentError (see above for traceback): assertion failed: [Unable to decode bytes as JPEG, PNG, GIF, or BMP]
Reading here it looks like this error is due to the fact that i'm not using
tf.gfile.GFile(str(image_path), 'rb').read()
as with my training/test files, but i can't use it because i need to send encoded bytes formatted as
{"image_bytes": {'b64': base64.b64encode(image).decode()}}
as requested by TF Serving.
Examples online send JPEG encoded bytes and preprocess the image starting with
tf.image.decode_jpeg(image_buffer, channels=3)
but if i use a different preprocessing function in my serving_input_receiver_fn (different than the one used for training) that starts with
tf.image.decode_png(image_buffer, channels=3)
i get the following error:
InvalidArgumentError (see above for traceback): Expected image (JPEG, PNG, or GIF), got unknown format starting with 'AAAAAAAAAAAAAAAA'
(the same happens with decode_jpeg, by the way)
What am i doing wrong? Do you need more code from me to answer? Thanks a lot!
Edit!!
Changed the title because it was not clear enough
OK I solved it.
image was a numpy array but i had to do the following:
buffer = cv2.imencode('.jpg', image)[1].tostring()
bytes_image = base64.b64encode(buffer).decode('ascii')
{"image_bytes": {"b64": bytes_image}}
Also, my preprocessing and serving_input_receiver_fn functions changed:
def process_image_from_buffer(self, image_buffer):
image = tf.image.decode_jpeg(image_buffer, channels=3)
image = tf.image.convert_image_dtype(image, dtype=tf.float32)
image = tf.expand_dims(image, 0)
image = tf.image.resize_bilinear(image, [256, 256], align_corners=False)
image = tf.squeeze(image, [0])
image = tf.cast(image, tf.float32) / 255.0
return image
def serving_input_receiver_fn(self):
input_ph = tf.placeholder(dtype=tf.string, shape=[None])
images_tensor = tf.map_fn(self.process_image_from_buffer, input_ph, back_prop=False, dtype=tf.float32)
return tf.estimator.export.ServingInputReceiver({'input_1': images_tensor}, {'image_bytes': input_ph})
process_image_from_buffer is different than process_image_from_tf_example used above for training.
I also removed name='image_bytes' from input_ph above.
Hope it's clear enough to help someone else.
Excellent guide partially used for solving it

Got a mismatch result between writing and reading tfrecord files

Here I use this func to write multiple tfrecord files:
writer = tf.python_io.TFRecordWriter(save)
for pth, lb in tqdm(zip(piece_p, piece_l)):
# mind that the path should be read into image data first
# to convert the byteslist data format into raw bytes
data = Image.open(pth)
if resize is not None:
data.thumbnail(resize, Image.ANTIALIAS)
features = tf.train.Features(feature={
'image': tf.train.Feature(
bytes_list=tf.train.BytesList(value=[data.tobytes()])),
'label': tf.train.Feature(
int64_list=tf.train.Int64List(value=[lb]))
})
example = tf.train.Example(features=features)
# serialize the constructed data format before writing step
writer.write(example.SerializeToString())
sys.stdout.flush()
writer.close()
And parse the binary file using code as below:
def parse_fn(serialized):
features = {
'image': tf.FixedLenFeature([], tf.string),
'label': tf.FixedLenFeature([], tf.int64)
}
parse_exp = tf.parse_single_example(serialized=serialized,
features=features)
labels = parse_exp['label']
data = parse_exp['image']
data = tf.decode_raw(data, tf.uint8)
data = tf.cast(data, tf.float32)
del parse_exp
return data, labels
dataset = tf.data.Dataset.list_files(data_list, shuffle=True)
dataset = dataset.interleave(tf.data.TFRecordDataset,
cycle_length=file_num)
# dataset = tf.data.TFRecordDataset(data_list[0])
dataset = dataset.map(parse_fn, num_parallel_calls=4)
But why is the number of labels and data always mismatching...?
every time when adding the following code to make multiple batches or sth
dataset = dataset.batch(12)
dataset = dataset.repeat(1)
iterator = dataset.make_initializable_iterator()
sess = tf.Session()
sess.run(tf.global_variables_initializer())
sess.run(iterator.initializer)
data, labels = iterator.get_next()
and the labels quantity always remains half of data. are there something wrong with my arguments setting? I pretty sure that there are no wrong with my saving part and reading part separately... but there are some problems when combining them together.

Multi Layer Tiff labelled dataset conversion to format that tensor flow can use for model optimisation

I'm a Python and Tensor Flow newbie, and was wondering...
How best to convert a labelled dataset of Multi-Layer Tiffs into a format that Tensor Flow can use for model optimisation / fine tuning ?
I currently have this code that puts each layer of a folder of Multi-Tiffs into a 3D Array, but i need to preserve the label or filename of the Multi-Tiffs. I have seen some tensor flow scripts to convert to TFRecords, however, I'm not sure if these preserve the file name ? How best would you go about this ? It will be quite a big dataset.
Any help much appreciated
import os # For file handling
from PIL import Image# Import Pillow image processing library
import numpy
CroppedMultiTiffs = "MultiTiffs/"
for filename in os.listdir(MultiTiffs):
## Imports Multi-Layer TIFF into 3D Numpy Array.
img = Image.open(MultiTiffs + filename)
imgArray = numpy.zeros( ( img.n_frames, img.size[1], img.size[0] ),numpy.uint8 )
try:
# for frames in range, img.n_frames for whole folder.
for frame in range(2,img.n_frames):
img.seek( frame )
imgArray[frame,:,:] = img
frame = frame + 1
except (EOFError): img.seek( 0 )
# output error if it doesn't find a file.
pass
print(imgArray.shape) # imgArray is now 3D
print(imgArray.size)
best wishes
TWP
okay, so I figured it out using the thread from Daniils blog
http://warmspringwinds.github.io/tensorflow/tf-slim/2016/12/21/tfrecords-guide/
However my current implimentation creates multiple TFRecords, and I think it needs to be a single TFRecord, so trying to figure out how to make it a single TFRecord. How do I do that?
Then I can validate it using a TFRecord Reading script to read it back and check it is in the right format for Tensor Flow. I currently get errors using the reading script.
from PIL import Image
import numpy as np
import tensorflow as tf
import os
def _bytes_feature(value):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
def _int64_feature(value):
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
path = 'test/'
output = 'output/'
fileList = [os.path.join(dirpath, f) for dirpath, dirnames, files in os.walk(path) for f in files if f.endswith('.tif')]
print (fileList)
for filename in fileList:
basename = os.path.basename(filename)
file_name = basename[:-4]
print ("processing file: " , filename)
print (file_name)
if not os.path.exists(output):
os.mkdir(output)
writer = tf.python_io.TFRecordWriter(output+ file_name + '.tfrecord')
img = Image.open(filename)
imgArray = np.zeros( ( img.n_frames, img.size[1], img.size[0] ),np.uint8 )
## Imports Multi-Layer file into 3D Numpy Array.
try:
for frame in range(0,img.n_frames):
img.seek( frame )
imgArray[frame,:,:] = img
frame = frame + 1
except (EOFError): img.seek( 0 )
pass
print ("print img size:" , img.size)
print ("print image shape: " , imgArray.shape)
print ("print image size: " , imgArray.size)
annotation = np.array(Image.open(filename))
height = imgArray.shape[0]
width = imgArray.shape[1]
depth = imgArray.shape[2]
img_raw = imgArray.tostring()
annotation_raw = annotation.tostring()
example = tf.train.Example(features=tf.train.Features(feature={
'height': _int64_feature(height),
'width': _int64_feature(width),
'depth': _int64_feature(depth), # for 3rd dimension
'image_raw': _bytes_feature(img_raw),
'mask_raw': _bytes_feature(annotation_raw)}))
writer.write(example.SerializeToString())
My current TFRecords Reading script
import tensorflow as tf
import os
def read_and_decode(filename_queue):
reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
serialized_example,
# Defaults are not specified since both keys are required.
features={
'image_raw': tf.FixedLenFeature([], tf.string),
'label': tf.FixedLenFeature([], tf.int64),
'height': tf.FixedLenFeature([], tf.int64),
'width': tf.FixedLenFeature([], tf.int64),
'depth': tf.FixedLenFeature([], tf.int64)
})
image = tf.decode_raw(features['image_raw'], tf.uint8)
label = tf.cast(features['label'], tf.int32)
height = tf.cast(features['height'], tf.int32)
width = tf.cast(features['width'], tf.int32)
depth = tf.cast(features['depth'], tf.int32)
return image, label, height, width, depth
with tf.Session() as sess:
filename_queue = tf.train.string_input_producer(["output/A.3.1.tfrecord"])
image, label, height, width, depth = read_and_decode(filename_queue)
image = tf.reshape(image, tf.stack([height, width, 3]))
image.set_shape([32,32,3])
init_op = tf.initialize_all_variables()
sess.run(init_op)
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(coord=coord)
for i in range(1000):
example, l = sess.run([image, label])
print (example,l)
coord.request_stop()
coord.join(threads)
receiving the error:-
InvalidArgumentError (see above for traceback): Name: , Feature: label (data type: int64) is required but could not be found.
Images are grayscale multi-page

How to get misclassified files in TF-Slim's eval_image_classifier.py?

I'm using a script that comes with TF-Slim to validate my trained model. It works fine but I'd like to get a list of the misclassified files.
The script makes use of https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/slim/python/slim/evaluation.py but even there I cannot find any options for printing the misclassified files.
How can I achieve that?
At a high level, you need to do 3 things:
1) Get your filename from the data loader. If you are using a tf-slim dataset from tfrecords, it is likely that the filenames are not stored in the tfrecord so you may be out of luck there. However if you are consuming image files directly from the filesystem with tf.WholeFileReader, then you can get the tensor of filenames where you form your batch:
def load_data():
train_image_names = ... # list of filenames
filename_queue = tf.train.string_input_producer(train_image_names)
reader = tf.WholeFileReader()
image_filename, image_file = reader.read(filename_queue)
image = tf.image.decode_jpeg(image_file, channels=3)
.... # load your labels from somewhere
return image_filename, image, label
# in your eval code
image_fn, image, label = load_data()
filenames, images, labels = tf.train.batch(
[image_fn, image, label],
batch_size=32,
num_threads=2,
capacity=100,
allow_smaller_final_batch=True)
2) Mask your filename tensor with your result after doing inference:
logits = my_network(images)
preds = tf.argmax(logits, 1)
mislabeled = tf.not_equal(preds, labels)
mislabeled_filenames = tf.boolean_mask(filenames, mislabeled)
3) Put all this into your eval_op:
eval_op = tf.Print(eval_op, [mislabeled_filenames])
slim.evaluation.evaluate_once(
.... # other options
eval_op=eval_op,
.... # other options)
I don't have a setup to test this, unfortunately. Let me know if it works!
shadow chris pointed me in the right direction so I share my solution to make it work with a TF-records dataset.
For better unstanding I relate my code to the flower example of TF-Slim.
1) Modify your dataset script to store a filename feature in the TF-records.
keys_to_features = {
'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
'image/format': tf.FixedLenFeature((), tf.string, default_value='png'),
'image/class/label': tf.FixedLenFeature(
[], tf.int64, default_value=tf.zeros([], dtype=tf.int64)),
'image/filename': tf.FixedLenFeature((), tf.string, default_value=''),
}
items_to_handlers = {
'image': slim.tfexample_decoder.Image(),
'label': slim.tfexample_decoder.Tensor('image/class/label'),
'filename': slim.tfexample_decoder.Tensor('image/filename'),
}
2) Add filename parameter to data util's image_to_tfexample function
It should then look like:
def image_to_tfexample(image_data, image_format, height, width, class_id, filename):
return tf.train.Example(features=tf.train.Features(feature={
'image/encoded': bytes_feature(image_data),
'image/format': bytes_feature(image_format),
'image/class/label': int64_feature(class_id),
'image/height': int64_feature(height),
'image/width': int64_feature(width),
'image/filename': bytes_feature(filename)
}))
3) Modify download and convert script to save filenames
Feed your TF record with the filename.
example = dataset_utils.image_to_tfexample(
image_data, 'jpg', height, width, class_id, filenames[i])
4) In your evaluation map misclassified imgs to filename
I'm refering to eval_image_classifier.py.
Retrieve filenames with tf.train.batch:
images, labels, filenames = tf.train.batch(
[image, label, filename],
batch_size=FLAGS.batch_size,
num_threads=FLAGS.num_preprocessing_threads,
capacity=5 * FLAGS.batch_size)
Get misclassified imgs and map them to filenames:
predictions = tf.argmax(logits, 1)
labels = tf.squeeze(labels)
mislabeled = tf.not_equal(predictions, labels)
mislabeled_filenames = tf.boolean_mask(filenames, mislabeled)
Print:
eval_op = tf.Print(eval_op, [mislabeled_filenames])
slim.evaluation.evaluate_once(
.... # other options
eval_op=eval_op,
.... # other options)