TFrecords занимают больше места, чем исходные изображения JPEG

Я пытаюсь преобразовать свой набор изображений Jpeg в TFrecords. Но файл TFrecord занимает почти в 5 раз больше места, чем набор изображений. После долгих поисков я узнал, что когда JPEG записывается в TFrecords, это уже не JPEG. Однако я не нашел понятного кода для решения этой проблемы. Скажите, пожалуйста, какие изменения нужно внести в приведенный ниже код, чтобы записать JPEG в Tfrecords.

def print_progress(count, total):
    pct_complete = float(count) / total
    msg = "\r- Progress: {0:.1%}".format(pct_complete)
    sys.stdout.write(msg)
    sys.stdout.flush()

def wrap_int64(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))

def wrap_bytes(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def convert(image_paths , labels, out_path):
    # Args:
    # image_paths   List of file-paths for the images.
    # labels        Class-labels for the images.
    # out_path      File-path for the TFRecords output file.

    print("Converting: " + out_path)

    # Number of images. Used when printing the progress.
    num_images = len(image_paths)

    # Open a TFRecordWriter for the output-file.
    with tf.python_io.TFRecordWriter(out_path) as writer:

        # Iterate over all the image-paths and class-labels.
        for i, (path, label) in enumerate(zip(image_paths, labels)):
            # Print the percentage-progress.
            print_progress(count=i, total=num_images-1)

            # Load the image-file using matplotlib's imread function.
            img = imread(path)
            # Convert the image to raw bytes.
            img_bytes = img.tostring()

            # Create a dict with the data we want to save in the
            # TFRecords file. You can add more relevant data here.
            data = \
            {
                'image': wrap_bytes(img_bytes),
                'label': wrap_int64(label)
            }

            # Wrap the data as TensorFlow Features.
            feature = tf.train.Features(feature=data)

            # Wrap again as a TensorFlow Example.
            example = tf.train.Example(features=feature)

            # Serialize the data.
            serialized = example.SerializeToString()

            # Write the serialized data to the TFRecords file.
            writer.write(serialized)

Редактировать: Может ли кто-нибудь ответить на это?!!


person Uchiha Madara    schedule 11.07.2018    source источник


Ответы (2)


Вместо преобразования изображения в массив и обратно в байты мы можем просто использовать встроенную функцию open для получения байтов. Таким образом, сжатое изображение будет записано в TFRecord.

Замените эти две строки

img = imread(path)
img_bytes = img.tostring()

с

img_bytes = open(path,'rb').read()

Справка :

https://github.com/tensorflow/tensorflow/issues/9675

person Uchiha Madara    schedule 18.07.2018
comment
Как это отвечает на вопрос? Можете ли вы предоставить некоторые подробности здесь? В обоих случаях tfrecord сохраняет информацию о байтах, почему стратегия загрузки изменяет размер файла? - person bw4sz; 29.10.2019
comment
img = imread(path) распаковывает jpeg и читает исходное изображение. Поэтому, когда вы сохраняете байты в tfrecords, они занимают много места. Пока open(path,'rb') читает jpeg как есть, без распаковки. Таким образом, размер tfrecord будет таким же, как коллекция изображений на вашем диске. - person Uchiha Madara; 30.10.2019

Вы не должны сохранять данные изображения в TFRecord, только имя файла. Затем, чтобы загрузить изображение, когда записи подаются в цикл обучения, в идеале вы должны использовать относительно новый Dataset API. Из документов:

# Reads an image from a file, decodes it into a dense tensor, and resizes it
# to a fixed shape.
def _parse_function(filename, label):
  image_string = tf.read_file(filename)
  image_decoded = tf.image.decode_jpeg(image_string)
  image_resized = tf.image.resize_images(image_decoded, [28, 28])
  return image_resized, label

# A vector of filenames.
filenames = tf.constant(["/var/data/image1.jpg", "/var/data/image2.jpg", ...])

# `labels[i]` is the label for the image in `filenames[i].
labels = tf.constant([0, 37, ...])

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(_parse_function)

Какой подход быстрее? Здесь есть ряд конкурирующих факторов, таких как:

  • Чтение одного большого непрерывного файла может быть быстрее, чем открытие и чтение множества маленьких файлов. Но это будет отличаться для твердотельных накопителей, вращающихся дисков или сетевых хранилищ.
  • Чтение большого количества небольших файлов может быть более удобным для распараллеливания.
  • Хотя чтение 1000 файлов размера x может быть медленнее, чем одного файла размером 1000x, на самом деле мы обсуждаем один большой файл размером 10 x 1000x, потому что данные изображения представляют собой необработанные пиксели, а не jpeg.
  • НО, начиная с данных пикселей, сохраняет шаг декодирования jpeg
  • Оптимизация скорости чтения, вероятно, не имеет особого смысла, если она не является вашим узким местом.

Итак, в конце концов, важно знать разные подходы. Без измерений я бы склонялся к решению с множеством маленьких файлов, потому что оно требует меньше обработки данных, с которых мы начинаем, и потому что оно вряд ли будет использоваться в документации Tensorflow, если оно будет совершенно необоснованным. Но единственный реальный ответ заключается в измерении.

person Matthias Winkelmann    schedule 13.07.2018
comment
Это действительно отличный подход. Но не является ли чтение изображения в каждом примере узким местом? Если изображения хранятся в tfrecords, то шаг read_file можно спокойно пропустить. - person Uchiha Madara; 16.07.2018