Keras flowFromDirectory получает имена файлов по мере их создания

Можно ли получить имена файлов, которые были загружены с помощью flow_from_directory? У меня есть:

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,)

У меня есть собственный генератор для моей модели с несколькими выходами, например:

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]

Node, что в настоящий момент я генерирую случайные числа для a, но для реального обучения я хочу загрузить файл json, содержащий координаты ограничивающих прямоугольников для моих изображений. Для этого мне нужно будет получить имена файлов, которые были сгенерированы с помощью метода train_generator.next(). После этого я могу загрузить файл, проанализировать json и передать его вместо a. Также необходимо, чтобы упорядочение переменной x и список имен файлов, которые я получаю, одинаковы.

Ответ 1

Да, возможно ли это, по крайней мере, с версией 2.0.4 (не знаю о более ранней версии).

Экземпляр ImageDataGenerator().flow_from_directory(...) имеет атрибут filenames, который представляет собой список всех файлов в порядке, который дает генератор, а также атрибут batch_index. Поэтому вы можете сделать это следующим образом:

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

И каждая итерация генератора вы можете получить соответствующие имена файлов следующим образом:

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

Это даст вам имена файлов в текущей партии.

Ответ 2

Вы можете создать довольно минимальный подкласс, который возвращает кортеж image, file_path, унаследовав 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])

В инициализации я добавил атрибут, представляющий собой непостоянную версию self.filepaths чтобы мы могли легко индексировать этот массив, чтобы получать пути для каждого поколения self.filepaths.

Единственное другое изменение в базовом классе - это возврат кортежа, который представляет собой пакетное изображение super()._get_batches_of_transformed_samples(index_array) и пути к файлам self.filenames_np[index_array].

С этим вы можете сделать свой генератор так:

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

А потом посоветуйтесь с

next(datagen)

Ответ 3

Вот пример, который также работает с shuffle=True. А также правильно обрабатывает последнюю партию. Чтобы сделать один проход:

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

Ответ 4

по крайней мере, с версией 2.2.4, вы можете сделать это следующим образом

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

или получите путь к файлу

for filepath in gen.filepaths:
    print(filepath)