harveyslash
harveyslash

Reputation: 6012

Keras showing images from data generator

I am using image generator for keras like this:

val_generator = datagen.flow_from_directory(
        path+'/valid',
        target_size=(224, 224),
        batch_size=batch_size,)

x,y = val_generator.next()
for i in range(0,1):
    image = x[i]
    plt.imshow(image.transpose(2,1,0))
    plt.show()

This shows wrong colors: enter image description here

I have two questions.

  1. How to fix the problem

  2. How to get file names of the files (so that I can read it myself from something like matplotlib)

Edit : this is what my datagen looks like

datagen = ImageDataGenerator(
    rotation_range=3,
#     featurewise_std_normalization=True,
    fill_mode='nearest',
    width_shift_range=0.2,
    height_shift_range=0.2,
    horizontal_flip=True
)

Edit 2 :

After following Marcin's answer :

image = 255 - image

I get normal colors , but there are still some weird colors:

enter image description here

Upvotes: 15

Views: 25687

Answers (6)

Gerry P
Gerry P

Reputation: 8102

Just a bit of advice if you are using test_batches=Imagedatagenerator().flow from directory. If you use this to feed a predict generator make sure you set shuffle=false to maintain a correlation between the file and the associated prediction. If you have files numerically labelled in the directory for example as 1.jpg, 2.jpg etc. The images are not fetched as you might think. They are fetched in the order: 1.jpg, 10.jpg, 2.jpg, 20.jpg etc. This makes it hard to match a prediction to a specific file. You can get around this by using 0's padding for example 01.jpg, 02.jpg etc. On the second part of the question "how can I get the files the generator produces you can get these files as follows:

for file in datagen.filenames:
        file_names.append(file)

Upvotes: 1

Nico
Nico

Reputation: 11

from skimage import io

def imshow(image_RGB):
  io.imshow(image_RGB)
  io.show()

x,y = train_generator.next()

for i in range(0,11):
    image = x[i]
    imshow(image)

It works for me.

Upvotes: 1

in3omnia
in3omnia

Reputation: 91

The dtype of your image array is 'float32', just convert it into 'uint8':

plt.imshow(image.astype('uint8'))

Upvotes: 9

Marin
Marin

Reputation: 41

I had the same problem as OP and solved it by rescaling the pixels from 0-255 to 0-1.

Keras' ImageDataGenerator takes a 'rescale' parameter, which I set to (1/255). This produced images with expected colors

image_gen = ImageDataGenerator(rescale=(1/255))

Upvotes: 4

lhk
lhk

Reputation: 30086

The color problem is rather strange. I'll try to reproduce it once I have access to my linux machine.

For the filename part of the question, I would like to propose a small change to the Keras sourcecode:

You might want to take a look at this file: https://github.com/fchollet/keras/blob/master/keras/preprocessing/image.py It contains the image preprocessing routines.

Look at line 820, the next() function of the DirectoryIterator: this is called to get new images from the directory.

Inside of that function, look at line 838, if save_to_dir has been set to a path, the generator will output the augmented images to this path, for debugging purposes. The name of the augmented image is a mixture of an index and a hash. Not useful for you.

But you can change the code quite easily:

filenames=[] #<-------------------------------------------- new code
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 = self.image_data_generator.random_transform(x)
    x = self.image_data_generator.standardize(x)

    filenames.append(fname) # <-----------------------------store the used image's name
    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)
        #fname = '{prefix}_{index}_{hash}.{format}'.format(prefix=self.save_prefix,
        #                                                  index=current_index + i,
        #                                                  hash=np.random.randint(1e4),
        #                                                  format=self.save_format)
        fname=filenames[i] # <------------------------------ use the stored code instead
        img.save(os.path.join(self.save_to_dir, fname))

Now the augmented image is saved with the original filename.

This should allow you to save the images under their original filenames. Ok, how do you actually inject this into the Keras souce ?

Do it like this:

  1. clone Keras: git clone https://github.com/fchollet/keras
  2. go to the sourcefile I linked above. Make the change.
  3. Trick your python code to import the changed code instead of the version installed by pip.

.

# this is the path to the cloned repository
# if you cloned it next to your script
# then just use keras/
# if it's one folder above
# then use ../keras/
sys.path.insert(0, os.getcwd() + "/path/to/keras/")

import keras

Now the DirectoryIterator is your patched version.

I hope that this works, I'm currently on windows. My python stack is only on the linux machine. There might be a small syntax error.

Upvotes: 1

Marcin Możejko
Marcin Możejko

Reputation: 40516

  1. There are at least three ways to have this twisted colors. So:

    • one option is that you need to switch a color ordering like in this question.
    • second is that you might have your pictures made to be a negative (every channels gets transformed by 255 - x transformation) this sometimes happens when it comes to using some GIS libraries.
    • you could also use a score/255 transformation.

    You need to check which options happens in your case.

  2. In order to get the images on your own I usually use (when your folder has a format suitable for a Keras flow_from_directory) I usually use the mix of os.listdir and os.path.join by :

    list_of_labels = os.listdir(path_to_dir_with_label_dirs)
    for label in list_of_labels:
        current_label_dir_path = os.path.join(path_to_dir_with_label_dirs, label
        list_of_images = os.listdir(current_label_dir_path)
        for image in list_of_images:
            current_image_path = os.path.join(current_label_dir_path, image)
            image = open(current_image_path) # use the function which you want.
    

Upvotes: 3

Related Questions