web-dev-qa-db-fra.com

Keras flowFromDirectory récupère les noms de fichiers lors de leur génération

Est-il possible d'obtenir les noms de fichiers qui ont été chargés en utilisant flow_from_directory? J'ai :

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
)

train_generator = datagen.flow_from_directory(
        path+'/train',
        target_size=(224, 224),
        batch_size=batch_size,)

J'ai un générateur personnalisé pour mon modèle à sorties multiples comme:

a = np.arange(8).reshape(2, 4)
# print(a)

print(train_generator.filenames)

def generate():
    while 1:
        x,y = train_generator.next()
        yield [x] ,[a,y]

Nœud qu'en ce moment je génère des nombres aléatoires pour a mais pour une vraie formation, je souhaite charger un fichier json qui contient les coordonnées du cadre de délimitation pour mes images. Pour cela, je devrai obtenir les noms de fichiers qui ont été générés à l'aide de la méthode train_generator.next(). Après cela, je peux charger le fichier, analyser le json et le passer au lieu de a. Il est également nécessaire que l'ordre de la variable x et la liste des noms de fichiers que j'obtiens soient les mêmes.

18
harveyslash

Oui, est-ce possible, au moins avec la version 2.0.4 (je ne connais pas la version antérieure).

L'instance de ImageDataGenerator().flow_from_directory(...) a un attribut avec filenames qui est une liste de tous les fichiers dans l'ordre dans lequel le générateur les renvoie et aussi un attribut batch_index. Vous pouvez donc le faire comme ceci:

datagen = ImageDataGenerator()
gen = datagen.flow_from_directory(...)

Et à chaque itération sur le générateur, vous pouvez obtenir les noms de fichiers correspondants comme ceci:

for i in gen:
    idx = (gen.batch_index - 1) * gen.batch_size
    print(gen.filenames[idx : idx + gen.batch_size])

Cela vous donnera les noms de fichiers des images dans le lot actuel.

22
Pepe

Vous pouvez créer une sous-classe assez minimale qui renvoie le tuple image, file_path En héritant du DirectoryIterator :

import numpy as np
from keras.preprocessing.image import ImageDataGenerator, DirectoryIterator

class ImageWithNames(DirectoryIterator):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.filenames_np = np.array(self.filepaths)
        self.class_mode = None # so that we only get the images back

    def _get_batches_of_transformed_samples(self, index_array):
        return (super()._get_batches_of_transformed_samples(index_array),
                self.filenames_np[index_array])

Dans l'init, j'ai ajouté un attribut qui est la version numpy de self.filepaths Afin que nous puissions facilement indexer dans ce tableau pour obtenir les chemins sur chaque génération de lots.

La seule autre modification apportée à la classe de base consiste à renvoyer un Tuple qui est le lot d'images super()._get_batches_of_transformed_samples(index_array) et les chemins de fichier self.filenames_np[index_array].

Avec cela, vous pouvez faire votre générateur comme suit:

imagegen = ImageDataGenerator()
datagen = ImageWithNames('/data/path', imagegen, target_size=(224,224))

Et puis vérifiez avec

next(datagen)
3
Bob Baxley

Voici un exemple qui fonctionne avec shuffle=True ainsi que. Et gère également correctement le dernier lot. Pour faire un passage:

datagen = ImageDataGenerator().flow_from_directory(...)    
batches_per_Epoch = datagen.samples // datagen.batch_size + (datagen.samples % datagen.batch_size > 0)
for i in range(batches_per_Epoch):
    batch = next(datagen)
    current_index = ((datagen.batch_index-1) * datagen.batch_size)
    if current_index < 0:
        if datagen.samples % datagen.batch_size > 0:
            current_index = max(0,datagen.samples - datagen.samples % datagen.batch_size)
        else:
            current_index = max(0,datagen.samples - datagen.batch_size)
    index_array = datagen.index_array[current_index:current_index + datagen.batch_size].tolist()
    img_paths = [datagen.filepaths[idx] for idx in index_array]
    #batch[0] - x, batch[1] - y, img_paths - absolute path
2
apatsekin

au moins avec la version 2.2.4, vous pouvez le faire comme ceci

datagen = ImageDataGenerator()
gen = datagen.flow_from_directory(...)
for file in gen.filenames:
    print(file)

ou obtenez le chemin du fichier

for filepath in gen.filepaths:
    print(filepath)
0
Xinstein