Извлечение интересующей области из файла изображения без чтения всего изображения

Я ищу библиотеку (на любом языке), которая способна читать область файла изображения (любого формата) без необходимости первоначального чтения всего этого файла изображения.

Я столкнулся с несколькими вариантами, такими как vips, которые действительно не сохраняют все изображение в памяти, но все же, похоже, для начала нужно прочитать его полностью.

Я понимаю, что это может быть недоступно для сжатых форматов, таких как jpeg, но теоретически похоже, что bmp или tiff должны допускать такой тип чтения.


person andrei    schedule 16.01.2018    source источник
comment
Вы должны быть более конкретными. Большинство форматов изображений сжаты. В противном случае изображения будут огромными, и их хранение будет пустой тратой места. Вы всегда можете написать настраиваемое сопоставление файлов для чтения в определенные места, хотя это может быть не быстрее или меньше потреблять памяти, чем чтение большей части изображения хотя бы один раз.   -  person Adi Shavit    schedule 16.01.2018
comment
Образы хранятся на NAS в другом месте, поэтому проблем с местом не будет. Проблема в том, что из ›100 мегапикселей, которые может иметь изображение, будет использоваться только его часть (для которой мы уже знаем координаты), и дополнительная полоса пропускания, необходимая для чтения неиспользуемой части файла, становится производительностью. узкое место. AFAIK bmp несжатый, и tiff МОЖЕТ быть несжатым, поэтому мне любопытно, существуют ли какие-либо существующие реализации этого метода чтения.   -  person andrei    schedule 16.01.2018
comment
Разрешает ли NAS произвольное чтение файлов?   -  person Yves Daoust    schedule 16.01.2018
comment
Доступ к субрегиону в необработанных массивах довольно тривиален. начните чтение в начале каждой строки ROI. Вам придется сбалансировать это с уменьшенной пропускной способностью сжатых изображений.   -  person Adi Shavit    schedule 16.01.2018
comment
NAS будет основан на NFS, поэтому мы ожидаем, что теоретически он будет разрешать произвольный доступ.   -  person andrei    schedule 16.01.2018
comment
@Adi: Это звучит тривиально, я знаю, поэтому я хотел бы знать, есть ли уже реализованная библиотека, которая выполняет это, прежде чем я начну изучать реализацию своей собственной.   -  person andrei    schedule 16.01.2018
comment
Не в обиду собакам, но NFS, как правило, очень медлительна. Вы используете стандартный сервер с NFS или какое-то высокоскоростное устройство? Можете ли вы, например, ssh войти на сервер NFS и использовать vips для извлечения оттуда?   -  person Mark Setchell    schedule 16.01.2018
comment
Если вы уже знаете координаты, вы, по-видимому, также должны знать имена файлов, которые вам понадобятся, поэтому, возможно, вы могли бы кэшировать их на свой локальный компьютер заранее, чтобы пропускная способность не была проблемой.   -  person Mark Setchell    schedule 16.01.2018
comment
Вы отказались от этого вопроса? Похоже, вы не комментируете чьи-либо ответы, не благодарите их и не разъясняете многое.   -  person Mark Setchell    schedule 18.01.2018
comment
этот вопрос не оставлен :) В настоящее время я изучаю несколько решений, я обязательно обновлю это своими выводами.   -  person andrei    schedule 19.01.2018
comment
libvips делает то, что вам нужно, я думаю. Я добавил ответ с некоторыми таймингами.   -  person jcupitt    schedule 22.01.2018
comment
Вы не можете (на практике) прочитать случайную часть обычного изображения TIFF, даже если они внутренне организованы в виде набора полос. Две большие проблемы заключаются в том, что 1) полосы могут быть любого размера вплоть до (и больше) самого изображения, поэтому кэширование очень сложно, и 2) произвольный доступ через полосы будет иметь катастрофическую производительность для таких операций, как поворот на 90 градусов — для напишите одну полосу вывода, вам нужно будет прочитать каждую полосу ввода! Чтобы повернуть весь файл, вам пришлось бы читать его много-много раз.   -  person jcupitt    schedule 22.01.2018


Ответы (5)


libvips будет читать только ту часть, которая вам нужна, когда это возможно. Например, если вы обрежете 100 x 100 пикселей из верхнего левого угла большого PNG-файла, это будет быстро:

$ time vips crop wtc.png x.jpg 0 0 100 100
real    0m0.063s
user    0m0.041s
sys 0m0.023s

(четыре числа слева, сверху, ширина, высота области, которая будет обрезана из wtc.png и записана в x.jpg)

Но область 100x100 пикселей снизу довольно медленная, так как она должна считывать и распаковывать пиксели перед пикселями, которые вы хотите получить в нужной точке файла:

$ time vips crop wtc.png x.jpg 0 9000 100 100
real    0m3.063s
user    0m2.884s
sys 0m0.181s

JPG и форматированный TIFF работают одинаково, хотя это менее очевидно, поскольку они являются гораздо более быстрыми форматами.

Некоторые форматы поддерживают настоящее чтение с произвольным доступом. Например, тайловый TIFF работает быстро везде, так как libvips может использовать libtiff для чтения только нужных тайлов:

$ vips copy wtc.png wtc.tif[tile]
$ time vips crop wtc.tif x.jpg 0 0 100 100
real    0m0.033s
user    0m0.013s
sys 0m0.021s
$ time vips crop wtc.tif x.jpg 0 9000 100 100
real    0m0.037s
user    0m0.021s
sys 0m0.017s

OpenSlide, vips, тайловый OpenEXR, FITS, двоичный PPM/PGM/PBM, HDR, RAW, Analyze, Matlab и, возможно, некоторые другие поддерживают настоящий произвольный доступ, подобный этому.

Если вас интересуют подробности, в документации по API есть глава, описывающая, как libvips открывает файл:

http://libvips.github.io/libvips/API/current/How-it-opens-files.md.html

Вот обрезка и сохранение в Python с использованием pyvips:

import pyvips

image = pyvips.Image.new_from_file(input_filename, access='sequential')
tile = image.crop(left, top, width, height)
tile.write_to_file(output_filename)

access= — это флаг, который намекает libvips, что это нормально для потоковой передачи этого изображения, если базовый формат файла не поддерживает произвольный доступ. Вам не нужно это для форматов, которые поддерживают произвольный доступ, таких как мозаичный TIFF.

Вам не нужно писать в файл. Например, это создаст буферный объект, содержащий файл, закодированный как JPG:

buffer = tile.write_to_buffer('.jpg', Q=85)

Или это будет писать напрямую в stdout:

target = pyvips.Target.new_from_descriptor(0)
tile.write_to_target('.jpg', Q=85)

Q=85 — необязательный аргумент для установки Q-фактора JPG. Вы можете установить любой из параметров сохранения файла.

person jcupitt    schedule 22.01.2018
comment
Кажется, это пока лучший вариант. Ответ от Cris Luengo с извлечением определенных фрагментов из tiff без чтения всего файла был полезен, однако это касается извлечения действительно индивидуальной рентабельности инвестиций при минимальных затратах на кодирование к проблеме! - person andrei; 24.01.2018
comment
Я бы использовал интерфейс Python pypi.python.org/pypi/pyvips, у вас должно получиться хорошо производительность, и это также делает кодирование очень простым. Спросите в системе отслеживания проблем pyvips, если у вас есть вопросы github.com/jcupitt/pyvips/issues - person jcupitt; 24.01.2018
comment
Есть ли способ заставить vips записывать в stdout, а не в файл на диске, такой как x.jpg выше? - person Mark Setchell; 26.01.2018
comment
Эй, Марк, есть оператор для записи в stdout как тип MIME: vips jpegsave_mime x.tif, но вы не можете комбинировать его с кадрированием в командной строке. Вам нужно будет запустить две команды (обрезать в файл, затем записать файл как mime) или использовать что-то вроде Python. - person jcupitt; 26.01.2018
comment
Я добавил пример Python. Спасибо за предложение! - person jcupitt; 26.01.2018

ITK может делать это с некоторыми форматами. Существует метод CanStreamRead, который возвращает true для форматов, поддерживающих потоковую передачу. , например MetaImageIO. Пример можно найти здесь. Вы можете задать более подробные вопросы на форуме ITK.

person Dženan    schedule 16.01.2018

Если у вас есть контроль над форматом файла, я бы посоветовал вам использовать мозаичные файлы TIFF. Обычно они используются в изображениях целых слайдов цифровой патологии со средним размером 100kx30k пикселей или около того.

LibTiff упрощает чтение плиток, соответствующих выбранной области интереса. Тайлы можно сжимать, не снижая эффективности чтения небольшой области (нет необходимости декодировать целые строки сканирования).

person Cris Luengo    schedule 18.01.2018
comment
Использование libtiff для извлечения тайлов было протестировано и, похоже, работает так, как предполагалось (т. е. хорошая производительность при извлечении тайлов из большого tiff через соединение с низкой пропускной способностью). Я все еще немного смотрю на другие решения, но это определенно решает проблему :) - person andrei; 23.01.2018
comment
@andrei, действительно, именно для этого и был разработан плиточный формат TIFF! - person Cris Luengo; 23.01.2018

Формат BMP (несжатый) достаточно прост, чтобы вы могли сами написать функцию.

TIFF немного сложнее, так как существует очень много подформатов. Но библиотека TIFF (TIFFlib) поддерживает режим ввода-вывода, ориентированный на тайлы. http://www.libtiff.org/libtiff.html#Tiles

person Yves Daoust    schedule 16.01.2018
comment
Это устаревший сайт для LibTiff. Вместо этого используйте simplesystems.org/libtiff. - person Cris Luengo; 18.01.2018

Я не знаю такого библиотечного решения.
Низкоуровневый доступ для чтения файлов зависит от формата и, в частности, сопоставление файлов зависит от ОС.

Если у вас есть доступ к необработанным байтам, то при условии, что вы знаете ширину, высоту, глубину, количество каналов и т. Д., Тогда вычисление смещения файла тривиально, поэтому просто сверните свое собственное.

Если вы передаете извлеченные данные по сети, вы можете рассмотреть возможность сжатия извлеченной области интереса в памяти, если она относительно велика, перед отправкой по сети.

person Adi Shavit    schedule 16.01.2018