Commit 30dd85ea authored by Tiago de Freitas Pereira's avatar Tiago de Freitas Pereira
Browse files

Merge branch 'new-features' into 'master'

A lot of new features

See merge request !75
parents 2ff7ed83 8c9315ae
Pipeline #29609 passed with stages
in 161 minutes and 4 seconds
......@@ -4,17 +4,18 @@ import os
import bob.io.base
DEFAULT_FEATURE = {
'data': tf.FixedLenFeature([], tf.string),
'label': tf.FixedLenFeature([], tf.int64),
'key': tf.FixedLenFeature([], tf.string)
"data": tf.FixedLenFeature([], tf.string),
"label": tf.FixedLenFeature([], tf.int64),
"key": tf.FixedLenFeature([], tf.string),
}
def from_hdf5file_to_tensor(filename):
import bob.io.image
data = bob.io.image.to_matplotlib(bob.io.base.load(filename))
#reshaping to ndim == 3
# reshaping to ndim == 3
if data.ndim == 2:
data = numpy.reshape(data, (data.shape[0], data.shape[1], 1))
data = data.astype("float32")
......@@ -25,7 +26,7 @@ def from_hdf5file_to_tensor(filename):
def from_filename_to_tensor(filename, extension=None):
"""
Read a file and it convert it to tensor.
If the file extension is something that tensorflow understands (.jpg, .bmp, .tif,...),
it uses the `tf.image.decode_image` otherwise it uses `bob.io.base.load`
"""
......@@ -33,19 +34,22 @@ def from_filename_to_tensor(filename, extension=None):
if extension == "hdf5":
return tf.py_func(from_hdf5file_to_tensor, [filename], [tf.float32])
else:
return tf.cast(
tf.image.decode_image(tf.read_file(filename)), tf.float32)
def append_image_augmentation(image,
gray_scale=False,
output_shape=None,
random_flip=False,
random_brightness=False,
random_contrast=False,
random_saturation=False,
random_rotate=False,
per_image_normalization=True):
return tf.cast(tf.image.decode_image(tf.read_file(filename)), tf.float32)
def append_image_augmentation(
image,
gray_scale=False,
output_shape=None,
random_flip=False,
random_brightness=False,
random_contrast=False,
random_saturation=False,
random_rotate=False,
per_image_normalization=True,
random_gamma=False,
random_crop=False,
):
"""
Append to the current tensor some random image augmentation operation
......@@ -76,37 +80,43 @@ def append_image_augmentation(image,
"""
# Casting to float32
image = tf.cast(image, tf.float32)
# Changing the range from 0-255 to 0-1
image = tf.cast(image, tf.float32) / 255
# FORCING A SEED FOR THE RANDOM OPERATIONS
tf.set_random_seed(0)
if output_shape is not None:
assert len(output_shape) == 2
image = tf.image.resize_image_with_crop_or_pad(image, output_shape[0],
output_shape[1])
if random_crop:
image = tf.random_crop(image, size=list(output_shape) + [3])
else:
assert len(output_shape) == 2
image = tf.image.resize_image_with_crop_or_pad(
image, output_shape[0], output_shape[1]
)
if random_flip:
image = tf.image.random_flip_left_right(image)
if random_brightness:
image = tf.image.random_brightness(image, max_delta=0.5)
image = tf.image.random_brightness(image, max_delta=0.15)
image = tf.clip_by_value(image, 0, 1)
if random_contrast:
image = tf.image.random_contrast(image, lower=0, upper=0.5)
image = tf.image.random_contrast(image, lower=0.85, upper=1.15)
image = tf.clip_by_value(image, 0, 1)
if random_saturation:
image = tf.image.random_saturation(image, lower=0, upper=0.5)
image = tf.image.random_saturation(image, lower=0.85, upper=1.15)
image = tf.clip_by_value(image, 0, 1)
if random_rotate:
image = tf.contrib.image.rotate(
image,
angles=numpy.random.randint(-5, 5),
interpolation="BILINEAR")
if random_gamma:
image = tf.image.adjust_gamma(
image, gamma=tf.random.uniform(shape=[], minval=0.85, maxval=1.15)
)
image = tf.clip_by_value(image, 0, 1)
if gray_scale:
image = tf.image.rgb_to_grayscale(image, name="rgb_to_gray")
#self.output_shape[3] = 1
# normalizing data
if per_image_normalization:
......@@ -153,20 +163,29 @@ def triplets_random_generator(input_data, input_labels):
input_labels = numpy.array(input_labels)
total_samples = input_data.shape[0]
indexes_per_labels = arrange_indexes_by_label(input_labels,
possible_labels)
indexes_per_labels = arrange_indexes_by_label(input_labels, possible_labels)
# searching for random triplets
offset_class = 0
for i in range(total_samples):
anchor_sample = input_data[indexes_per_labels[possible_labels[
offset_class]][numpy.random.randint(
len(indexes_per_labels[possible_labels[offset_class]]))], ...]
positive_sample = input_data[indexes_per_labels[possible_labels[
offset_class]][numpy.random.randint(
len(indexes_per_labels[possible_labels[offset_class]]))], ...]
anchor_sample = input_data[
indexes_per_labels[possible_labels[offset_class]][
numpy.random.randint(
len(indexes_per_labels[possible_labels[offset_class]])
)
],
...,
]
positive_sample = input_data[
indexes_per_labels[possible_labels[offset_class]][
numpy.random.randint(
len(indexes_per_labels[possible_labels[offset_class]])
)
],
...,
]
# Changing the class
offset_class += 1
......@@ -174,9 +193,14 @@ def triplets_random_generator(input_data, input_labels):
if offset_class == len(possible_labels):
offset_class = 0
negative_sample = input_data[indexes_per_labels[possible_labels[
offset_class]][numpy.random.randint(
len(indexes_per_labels[possible_labels[offset_class]]))], ...]
negative_sample = input_data[
indexes_per_labels[possible_labels[offset_class]][
numpy.random.randint(
len(indexes_per_labels[possible_labels[offset_class]])
)
],
...,
]
append(str(anchor_sample), str(positive_sample), str(negative_sample))
# yield anchor, positive, negative
......@@ -214,17 +238,18 @@ def siamease_pairs_generator(input_data, input_labels):
total_samples = input_data.shape[0]
# Filtering the samples by label and shuffling all the indexes
#indexes_per_labels = dict()
# indexes_per_labels = dict()
# for l in possible_labels:
# indexes_per_labels[l] = numpy.where(input_labels == l)[0]
# numpy.random.shuffle(indexes_per_labels[l])
indexes_per_labels = arrange_indexes_by_label(input_labels,
possible_labels)
indexes_per_labels = arrange_indexes_by_label(input_labels, possible_labels)
left_possible_indexes = numpy.random.choice(
possible_labels, total_samples, replace=True)
possible_labels, total_samples, replace=True
)
right_possible_indexes = numpy.random.choice(
possible_labels, total_samples, replace=True)
possible_labels, total_samples, replace=True
)
genuine = True
for i in range(total_samples):
......@@ -234,10 +259,16 @@ def siamease_pairs_generator(input_data, input_labels):
class_index = left_possible_indexes[i]
# Now selecting the samples for the pair
left = input_data[indexes_per_labels[class_index][
numpy.random.randint(len(indexes_per_labels[class_index]))]]
right = input_data[indexes_per_labels[class_index][
numpy.random.randint(len(indexes_per_labels[class_index]))]]
left = input_data[
indexes_per_labels[class_index][
numpy.random.randint(len(indexes_per_labels[class_index]))
]
]
right = input_data[
indexes_per_labels[class_index][
numpy.random.randint(len(indexes_per_labels[class_index]))
]
]
append(left, right, 0)
# yield left, right, 0
else:
......@@ -248,7 +279,9 @@ def siamease_pairs_generator(input_data, input_labels):
# Finding the right pair
j = i
# TODO: Lame solution. Fix this
while j < total_samples: # Here is an unidiretinal search for the negative pair
while (
j < total_samples
): # Here is an unidiretinal search for the negative pair
if left_possible_indexes[i] != right_possible_indexes[j]:
class_index.append(right_possible_indexes[j])
break
......@@ -256,12 +289,16 @@ def siamease_pairs_generator(input_data, input_labels):
if j < total_samples:
# Now selecting the samples for the pair
left = input_data[indexes_per_labels[class_index[0]][
numpy.random.randint(
len(indexes_per_labels[class_index[0]]))]]
right = input_data[indexes_per_labels[class_index[1]][
numpy.random.randint(
len(indexes_per_labels[class_index[1]]))]]
left = input_data[
indexes_per_labels[class_index[0]][
numpy.random.randint(len(indexes_per_labels[class_index[0]]))
]
]
right = input_data[
indexes_per_labels[class_index[1]][
numpy.random.randint(len(indexes_per_labels[class_index[1]]))
]
]
append(left, right, 1)
genuine = not genuine
......@@ -295,8 +332,9 @@ def blocks_tensorflow(images, block_size):
# extract image patches for each color space:
output = []
for i in range(3):
blocks = tf.extract_image_patches(images[:, :, :, i:i + 1], block_size,
block_size, [1, 1, 1, 1], "VALID")
blocks = tf.extract_image_patches(
images[:, :, :, i : i + 1], block_size, block_size, [1, 1, 1, 1], "VALID"
)
if i == 0:
n_blocks = int(numpy.prod(blocks.shape[1:3]))
blocks = tf.reshape(blocks, output_size)
......
import six
import tensorflow as tf
import logging
logger = logging.getLogger(__name__)
class Generator:
"""A generator class which wraps samples so that they can
be used with tf.data.Dataset.from_generator
Attributes
----------
epoch : int
The number of epochs that have been passed so far.
multiple_samples : :obj:`bool`, optional
If true, it assumes that the bio database's samples actually contain
multiple samples. This is useful for when you want to for example treat
video databases as image databases.
reader : :obj:`object`, optional
A callable with the signature of ``data, label, key = reader(sample)``
which takes a sample and loads it.
samples : [:obj:`object`]
A list of samples to be given to ``reader`` to load the data.
output_types : (object, object, object)
The types of the returned samples.
output_shapes : ``(tf.TensorShape, tf.TensorShape, tf.TensorShape)``
The shapes of the returned samples.
"""
def __init__(self, samples, reader, multiple_samples=False, **kwargs):
super().__init__(**kwargs)
self.reader = reader
self.samples = list(samples)
self.multiple_samples = multiple_samples
self.epoch = 0
# load one data to get its type and shape
dlk = self.reader(self.samples[0])
if self.multiple_samples:
try:
dlk = dlk[0]
except TypeError:
# if the data is a generator
dlk = six.next(dlk)
# Creating a "fake" dataset just to get the types and shapes
dataset = tf.data.Dataset.from_tensors(dlk)
self._output_types = dataset.output_types
self._output_shapes = dataset.output_shapes
logger.info(
"Initializing a dataset with %d %s and %s types and %s shapes",
len(self.samples),
"multi-samples" if self.multiple_samples else "samples",
self.output_types,
self.output_shapes,
)
@property
def output_types(self):
return self._output_types
@property
def output_shapes(self):
return self._output_shapes
def __call__(self):
"""A generator function that when called will yield the samples.
Yields
------
(data, label, key) : tuple
A tuple containing the data, label, and the key.
"""
for sample in self.samples:
dlk = self.reader(sample)
if self.multiple_samples:
for sub_dlk in dlk:
yield sub_dlk
else:
yield dlk
self.epoch += 1
logger.info("Elapsed %d epoch(s)", self.epoch)
def dataset_using_generator(*args, **kwargs):
"""
A generator class which wraps samples so that they can
be used with tf.data.Dataset.from_generator
Attributes
----------
samples : [:obj:`object`]
A list of samples to be given to ``reader`` to load the data.
reader : :obj:`object`, optional
A callable with the signature of ``data, label, key = reader(sample)``
which takes a sample and loads it.
multiple_samples : :obj:`bool`, optional
If true, it assumes that the bio database's samples actually contain
multiple samples. This is useful for when you want to for example treat
video databases as image databases.
"""
generator = Generator(*args, **kwargs)
dataset = tf.data.Dataset.from_generator(
generator, generator.output_types, generator.output_shapes
)
return dataset
This diff is collapsed.
import tensorflow as tf
import os
import numpy as np
import logging
logger = logging.getLogger(__name__)
def normalize_checkpoint_path(path):
if os.path.splitext(path)[1] == ".meta":
filename = os.path.splitext(path)[0]
elif os.path.isdir(path):
filename = tf.train.latest_checkpoint(path)
else:
filename = path
return filename
class Base:
def __init__(self, output_name, input_shape, checkpoint, scopes,
input_transform=None, output_transform=None,
input_dtype='float32', **kwargs):
self.output_name = output_name
self.input_shape = input_shape
self.checkpoint = normalize_checkpoint_path(checkpoint)
self.scopes = scopes
self.input_transform = input_transform
self.output_transform = output_transform
self.input_dtype = input_dtype
self.session = None
super().__init__(**kwargs)
def load(self):
self.session = tf.Session(graph=tf.Graph())
with self.session.as_default(), self.session.graph.as_default():
self.input = data = tf.placeholder(self.input_dtype, self.input_shape)
if self.input_transform is not None:
data = self.input_transform(data)
self.output = self.get_output(data, tf.estimator.ModeKeys.PREDICT)
if self.output_transform is not None:
self.output = self.output_transform(self.output)
tf.train.init_from_checkpoint(
ckpt_dir_or_file=self.checkpoint,
assignment_map=self.scopes,
)
# global_variables_initializer must run after init_from_checkpoint
self.session.run(tf.global_variables_initializer())
logger.info('Restored the model from %s', self.checkpoint)
def __call__(self, data):
if self.session is None:
self.load()
data = np.ascontiguousarray(data, dtype=self.input_dtype)
return self.session.run(self.output, feed_dict={self.input: data})
def get_output(self, data, mode):
raise NotImplementedError()
import tensorflow as tf
from .Base import Base
class Estimator(Base):
def __init__(self, estimator, **kwargs):
self.estimator = estimator
kwargs['checkpoint'] = kwargs.get('checkpoint', estimator.model_dir)
super().__init__(**kwargs)
def get_output(self, data, mode):
features = {'data': data, 'key': tf.constant(['key'])}
self.estimator_spec = self.estimator._call_model_fn(
features, None, mode, None)
self.end_points = self.estimator.end_points
return self.end_points[self.output_name]
from .Base import Base
class Generic(Base):
def __init__(self, architecture, **kwargs):
self.architecture = architecture
super().__init__(**kwargs)
def get_output(self, data, mode):
self.end_points = self.architecture(data, mode=mode)[1]
return self.end_points[self.output_name]
from .Base import Base
from .Generic import Generic
from .Estimator import Estimator
# gets sphinx autodoc done right - don't remove it
def __appropriate__(*args):
"""Says object was actually declared here, an not on the import module.
Parameters:
*args: An iterable of objects to modify
Resolves `Sphinx referencing issues
<https://github.com/sphinx-doc/sphinx/issues/3048>`
"""
for obj in args:
obj.__module__ = __name__
__appropriate__(
Base,
Generic,
Estimator,
)
__all__ = [_ for _ in dir() if not _.startswith('_')]
......@@ -3,22 +3,19 @@
# @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
# @date: Fri 04 Aug 2017 14:14:22 CEST
## MAXOUT IMPLEMENTED FOR TENSORFLOW
from tensorflow.python.framework import ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import gen_array_ops
# MAXOUT IMPLEMENTED FOR TENSORFLOW
from tensorflow.python.layers import base
import tensorflow as tf
def maxout(inputs, num_units, axis=-1, name=None):
return MaxOut(num_units=num_units, axis=axis, name=name)(inputs)
return Maxout(num_units=num_units, axis=axis, name=name)(inputs)
class MaxOut(base.Layer):
class Maxout(base.Layer):
"""
Adds a maxout op from
Adds a maxout op from
"Maxout Networks"
......@@ -41,33 +38,37 @@ class MaxOut(base.Layer):
"""
def __init__(self, num_units, axis=-1, name=None, **kwargs):
super(MaxOut, self).__init__(name=name, trainable=False, **kwargs)
super(Maxout, self).__init__(name=name, trainable=False, **kwargs)
self.axis = axis
self.num_units = num_units
def call(self, inputs, training=False):
inputs = ops.convert_to_tensor(inputs)
inputs = tf.convert_to_tensor(inputs)
shape = inputs.get_shape().as_list()
if self.axis is None:
# Assume that channel is the last dimension
self.axis = -1
num_channels = shape[self.axis]
if num_channels % self.num_units:
raise ValueError('number of features({}) is not '
'a multiple of num_units({})'.format(
num_channels, self.num_units))
shape[self.axis] = -1
shape += [num_channels // self.num_units]
# Dealing with batches with arbitrary sizes
for i in range(len(shape)):
if shape[i] is None:
shape[i] = gen_array_ops.shape(inputs)[i]
outputs = math_ops.reduce_max(
gen_array_ops.reshape(inputs, shape), -1, keep_dims=False)
shape = outputs.get_shape().as_list()
shape[self.axis] = self.num_units
outputs.set_shape(shape)
shape[i] = tf.shape(inputs)[i]
num_channels = shape[self.axis]
if not isinstance(num_channels, tf.Tensor) and num_channels % self.num_units:
raise ValueError(
"number of features({}) is not "
"a multiple of num_units({})".format(num_channels, self.num_units)
)
if self.axis < 0:
axis = self.axis + len(shape)
else:
axis = self.axis
assert axis >= 0, "Find invalid axis: {}".format(self.axis)
expand_shape = shape[:]
expand_shape[axis] = self.num_units
k = num_channels // self.num_units
expand_shape.insert(axis, k)
outputs = tf.math.reduce_max(
tf.reshape(inputs, expand_shape), axis, keepdims=