-
Notifications
You must be signed in to change notification settings - Fork 19.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug Fix in Image Generator - Now Allows FCN's to be used #5228
Changes from all commits
08138dc
31e8ce9
3d34162
86826ce
d598805
a0ab1e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,14 +191,17 @@ def flip_axis(x, axis): | |
return x | ||
|
||
|
||
def array_to_img(x, dim_ordering='default', scale=True): | ||
def array_to_img(x, dim_ordering='default', scale=True, color_mode='rgb'): | ||
"""Converts a 3D Numpy array to a PIL Image instance. | ||
|
||
# Arguments | ||
x: Input Numpy array. | ||
dim_ordering: Image data format. | ||
scale: Whether to rescale image values | ||
to be within [0, 255]. | ||
color_mode: 'bgr' to reverse the | ||
channel dimensions, otherwise it picks | ||
depending on the shape of X. | ||
|
||
# Returns | ||
A PIL Image instance. | ||
|
@@ -232,6 +235,8 @@ def array_to_img(x, dim_ordering='default', scale=True): | |
x /= x_max | ||
x *= 255 | ||
if x.shape[2] == 3: | ||
if color_mode == 'bgr': | ||
x = x[:, :, ::-1] | ||
# RGB | ||
return pil_image.fromarray(x.astype('uint8'), 'RGB') | ||
elif x.shape[2] == 1: | ||
|
@@ -241,12 +246,15 @@ def array_to_img(x, dim_ordering='default', scale=True): | |
raise ValueError('Unsupported channel number: ', x.shape[2]) | ||
|
||
|
||
def img_to_array(img, dim_ordering='default'): | ||
def img_to_array(img, dim_ordering='default', color_mode='rgb'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed both, please tell me if the docstring doesn't suffice. |
||
"""Converts a PIL Image instance to a Numpy array. | ||
|
||
# Arguments | ||
img: PIL Image instance. | ||
dim_ordering: Image data format. | ||
color_mode: 'bgr' to reverse the | ||
channel dimensions, otherwise it picks | ||
depending on the shape of X. | ||
|
||
# Returns | ||
A 3D Numpy array (float32). | ||
|
@@ -262,6 +270,8 @@ def img_to_array(img, dim_ordering='default'): | |
# or (channel, height, width) | ||
# but original PIL image has format (width, height, channel) | ||
x = np.asarray(img, dtype='float32') | ||
if color_mode == 'bgr': | ||
x = x[:, :, ::-1] | ||
if len(x.shape) == 3: | ||
if dim_ordering == 'th': | ||
x = x.transpose(2, 0, 1) | ||
|
@@ -275,7 +285,7 @@ def img_to_array(img, dim_ordering='default'): | |
return x | ||
|
||
|
||
def load_img(path, grayscale=False, target_size=None): | ||
def load_img(path, grayscale=False, target_size=(None, None)): | ||
"""Loads an image into PIL format. | ||
|
||
# Arguments | ||
|
@@ -298,7 +308,7 @@ def load_img(path, grayscale=False, target_size=None): | |
img = img.convert('L') | ||
else: # Ensure 3 channel even when loaded image is grayscale | ||
img = img.convert('RGB') | ||
if target_size: | ||
if target_size[0] and target_size[1]: # Can't pass None in from iterator, hence tuple | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can keep the default to |
||
img = img.resize((target_size[1], target_size[0])) | ||
return img | ||
|
||
|
@@ -742,9 +752,9 @@ def __init__(self, directory, image_data_generator, | |
self.directory = directory | ||
self.image_data_generator = image_data_generator | ||
self.target_size = tuple(target_size) | ||
if color_mode not in {'rgb', 'grayscale'}: | ||
if color_mode not in {'rgb', 'bgr', 'grayscale'}: | ||
raise ValueError('Invalid color mode:', color_mode, | ||
'; expected "rgb" or "grayscale".') | ||
'; expected "rgb", "bgr" or "grayscale".') | ||
self.color_mode = color_mode | ||
self.dim_ordering = dim_ordering | ||
if self.color_mode == 'rgb': | ||
|
@@ -777,6 +787,9 @@ def __init__(self, directory, image_data_generator, | |
for subdir in sorted(os.listdir(directory)): | ||
if os.path.isdir(os.path.join(directory, subdir)): | ||
classes.append(subdir) | ||
# User passed in None and no sub-folders, hence FCN. N.B.: Pass in classes=['.'] for improvements. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cannot quite understand this comment. Can you clarify? Also: line too long. |
||
if not classes: | ||
classes = [directory] | ||
self.nb_class = len(classes) | ||
self.class_indices = dict(zip(classes, range(len(classes)))) | ||
|
||
|
@@ -822,22 +835,30 @@ def next(self): | |
index_array, current_index, current_batch_size = next(self.index_generator) | ||
# The transformation of images is not under thread lock | ||
# so it can be done in parallel | ||
batch_x = np.zeros((current_batch_size,) + self.image_shape) | ||
# If image_shape has None in position 1 it is intended to be used as a FCN | ||
if self.image_shape[1]: | ||
batch_x = np.zeros((current_batch_size,) + self.image_shape) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this assume all the images have the same shape? reference code: |
||
grayscale = self.color_mode == 'grayscale' | ||
# build batch of image data | ||
for i, j in enumerate(index_array): | ||
fname = self.filenames[j] | ||
img = load_img(os.path.join(self.directory, fname), | ||
grayscale=grayscale, | ||
target_size=self.target_size) | ||
x = img_to_array(img, dim_ordering=self.dim_ordering) | ||
x = img_to_array(img, dim_ordering=self.dim_ordering, color_mode=self.color_mode) | ||
# Allows shape to be None, but batch size must equal one. | ||
if not self.image_shape[1] and i == 0: | ||
if current_batch_size != 1: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why must batch size be one? Just looking for clarification because I'm not 100% sure what this case is checking for because dense prediction tasks including FCNs can train with batch sizes >1. |
||
raise ValueError('Current batch size is: %s , should be size one for current shape' | ||
% current_batch_size) | ||
batch_x = np.zeros((current_batch_size,) + x.shape) | ||
x = self.image_data_generator.random_transform(x) | ||
x = self.image_data_generator.standardize(x) | ||
batch_x[i] = x | ||
# optionally save augmented images to disk for debugging purposes | ||
if self.save_to_dir: | ||
for i in range(current_batch_size): | ||
img = array_to_img(batch_x[i], self.dim_ordering, scale=True) | ||
img = array_to_img(batch_x[i], self.dim_ordering, scale=True, color_mode=self.color_mode) | ||
fname = '{prefix}_{index}_{hash}.{format}'.format(prefix=self.save_prefix, | ||
index=current_index + i, | ||
hash=np.random.randint(1e4), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,6 +160,33 @@ def test_directory_iterator(self): | |
assert(sorted(dir_iterator.filenames) == sorted(filenames)) | ||
shutil.rmtree(tmp_folder) | ||
|
||
''' Test for the directory iterator when we wish to use it for a FCN.''' | ||
def test_directory_iterator_fcn(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest also checking if labels can be loaded from pngs without palette conversion as described in the following, perhaps an additonal color mode 'segmentation_label' would be appropriate: Though perhaps something like that is out of scope for this pull request. |
||
num_classes = 1 # 1 class encompassing all masks | ||
tmp_folder = tempfile.mkdtemp(prefix='test_images') | ||
|
||
# no subfolders | ||
|
||
# save the images in the paths | ||
count = 0 | ||
filenames = [] | ||
for test_images in self.all_test_images: | ||
for im in test_images: | ||
filename = 'image-{}.jpg'.format(count) | ||
filenames.append(filename) | ||
im.save(os.path.join(tmp_folder, filename)) | ||
count += 1 | ||
|
||
# create iterator | ||
generator = image.ImageDataGenerator() | ||
dir_iterator = generator.flow_from_directory(tmp_folder, classes=['.']) | ||
|
||
# check number of classes and images | ||
assert (len(dir_iterator.class_indices) == num_classes) | ||
assert (len(dir_iterator.classes) == count) | ||
assert (sorted(dir_iterator.filenames) == sorted(filenames)) | ||
shutil.rmtree(tmp_folder) | ||
|
||
def test_img_utils(self): | ||
height, width = 10, 8 | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you add an argument you should document it in the docstring.