Как с помощью Delphi записать огромный JPEG, превышающий физическую оперативную память?

Вот в чем проблема. У меня есть большой набор плиток JPEG размером 512x512 пикселей в виде обычных файлов jpg.

Я написал программу, которая делает кучу вещей и в конце должна сшить все эти файлы в один огромный JPEG.

Прежде всего, я НЕ хочу использовать ImageMagick для этого, а выполняю это в моем программном обеспечении!

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

Проблема возникает, когда размеры результирующего файла слишком велики (например, 20 000 x 20 000 пикселей). Когда я вызываю TBitmap.SetSize, я, естественно, получаю сообщение об ошибке (нехватка памяти или что-то в этом роде).

Я провел несколько тестов с помощью Photoshop на том же компьютере и смог создать сложный (непустой) файл размером 30 000 x 30 000 и сохранить его в формате JPEG.

Итак, вопрос в том, как я мог сделать то же самое? Ищете способ сшить все эти JPEG-файлы, записав результат прямо на диск или используя какой-то другой трюк? ...

Несмотря на то, что 20k x 20k пикселей кажутся достаточно большими, это значение применимо только к моей машине (4 ГБ оперативной памяти), поэтому меньшее количество оперативной памяти будет еще более ограничивать программное обеспечение!

Спасибо

Изменить: уточнить:

Я бы хотел найти способ сшить эти маленькие изображения JPG и записать большое, не сохраняя большое изображение в ОЗУ. Очевидно, чтение / запись потока растровых изображений возможно непосредственно на диск (не уверен), но это приведет к ОЧЕНЬ большому файлу. Итак, если формат JPG не позволяет этого сделать, подойдет любой другой сжатый формат, например TIFF или PNG. Я также хотел бы избежать чрезмерного повторного сжатия, чтобы не потерять (уже сжатое) исходное качество JPG.

Следовательно, идеальным решением будет способ напрямую читать маленькие файлы и каким-то образом записывать в большой. Размеры плиток - 256x256 или 512x512 на тот случай, если это поможет для некоторого выравнивания при сжатии JPEG.


person Alexander    schedule 27.06.2010    source источник


Ответы (8)


Спасибо всем !

Фактически, ответ и возможное решение - действовать так же, как Photoshop, то есть записывать поток растровых изображений из плиток в большой файл BMP на диске (например, файл размером 20 000 x 30 000 будет 2,4 ГБ), а затем использовать Библиотека NativeJpg для преобразования этого большого растрового изображения в jpg путем подачи полосы данных растрового изображения полосой за полосой, каждая из которых имеет высоту 8 пикселей.

Также можно было бы сшить одну линию плиток (высотой 512 пикселей), а затем передать ее в библиотеку NativeJpg 8 на 8, а затем перейти к следующей строке плиток!

Пример кода от Эрика Тернера:

procedure GetBitmapTile(BM: TBitmap; Y, X: Integer);
var JpegImage: TJpegImage;
begin
  JpegImage := NIL; // Replace with tile lookup //
  BM.PixelFormat := pf32bit;
  BM.Width := JpegImage.Width;
  BM.Height := JpegImage.Height;
  BM.Canvas.Draw(0, 0, JpegImage);
end;

procedure WriteBitmapFile(TileCountY, TileCountX: Integer; BM_Stm: TStream);
var
  BM: TBitmap;
  TileY: Integer;
  TileX: Integer;
  PixelY: Integer;
begin
  BM := TBitmap.Create;
  for TileY := 0 to TileCountY-1 do
    for TileX := 0 to TileCountX-1 do
    begin
      GetBitmapTile(BM, TileY, TileX);
      for PixelY := 0 to 511 do
        BM_Stm.Write(BM.ScanLine[PixelY]^, 512 * SizeOf(TRGBQuad));
    end;
    BM.Free;
end;

Библиотека NativeJpg: http://www.simdesign.nl/nativejpg.html

person Alexander    schedule 27.06.2010

Это довольно сложная область, и, как уже сказал @Peter, ребята из Photoshop были в с тех пор, как 1990 год. Вы, вероятно, не сможете работать со встроенными библиотеками декодирования JPG вашего языка программирования, поскольку они, скорее всего, загрузят (и распакуют) все изображение в ОЗУ.

Я бы порекомендовал поискать внешние библиотеки, которые справляются с этим - я не знаю, есть ли достойные, доступные бесплатно, но это вполне может быть так.

person Pekka    schedule 27.06.2010
comment
Спасибо, Пекка. Мне не нужно распаковывать большие изображения, читать легко. Вопрос пишется! ;) - person Alexander; 27.06.2010
comment
@Alexander, это все еще будет сложным из-за характера формата JPG: вы не можете просто сшить байтовые потоки вместе, как это было бы (вроде) возможно с необработанным форматом, который не использует сжатие. Если вам не нужен эффект обучения, я предлагаю использовать для этого внешнюю библиотеку :) - person Pekka; 27.06.2010
comment
Что ж, сейчас я делю изображение пополам и записываю два отдельных файла, а затем перешиваю их в Photoshop, но я бы наверняка получил эффект обучения, потому что это личный проект;) В противном случае я думаю, что я вообще не задавал бы вопрос! Если есть другой формат, который подходит для этого, например PNG или что-то еще, кроме raw bmp (что приводит к смехотворно большому файлу) ... - person Alexander; 27.06.2010

Максимальный размер изображений в Delphi (по крайней мере, в предыдущих версиях) больше зависел от графических драйверов Windows, чем от объема системной памяти.

Некоторые эксперименты по этому поводу: компьютерная лаборатория EFG

person Jan Oosting    schedule 27.06.2010
comment
Спасибо, Ян, я уже читал об этом веб-сайте, но на самом деле я больше думал о какой-то технике, которая позволила бы читать и записывать данные в формате JPEG без необходимости хранить в памяти все полученное изображение. Я знаю, что пакет netpbm выполняет это в своих собственных форматах и ​​даже может конвертировать между некоторыми форматами, не загружая все данные в ОЗУ ... - person Alexander; 27.06.2010

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

В видеопамяти изображение (или экранный буфер) представляет собой линейный массив. Горизонтальные строки сохраняются последовательно, и каждый пиксель соответствует смещению массива в (y * width + x) -1.

Таким образом, в изображении размером 320x200 пиксель 5,2 будет иметь индекс массива 5 * 320 + 2-1, или 1601. В те дни, когда еще не было аппаратного ускорения, вы должны были бы использовать malloc () в буфере размером с ваш экран. и математически выполнять такие операции, как рисование текстур, форм, световых эффектов и т. д., а затем записывать буфер в видеопамять.

В вашем случае вы можете использовать встроенные классы растровых изображений и изображений для работы с меньшими изображениями, которые помещаются в памяти, а затем копировать их пиксельные данные в большой массив или серию массивов (я забываю, позволяет ли виртуальная память создавать буферы> размер физической RAM). Затем, используя библиотеку JPEG, которая работает непосредственно с этим массивом (которая не зависит от размера ОЗУ, установленного на машине), вы сможете передать массив в библиотеку и сохранить ее содержимое на диск. Сжатие LZW довольно просто, и я ожидал бы, что в них будет много материала о том, как вручную реализовать сжатие JPEG в Интернете.

Одно предостережение: если вы используете 32-разрядную ОС, ваше адресное пространство должно быть ограничено до 4 ГБ. Единственный способ, который я могу придумать, чтобы обойти это, - это создать буферы меньшего размера (скажем, по одной строке за раз), бросить и заполнить данные той частью данных пикселей, которой соответствует строка в изображения, которые нужно сшить, сохраните и зацикливайте, пока не покроете всю область изображения.

Надеюсь, это понятно. Удачи!

person 3Dave    schedule 27.06.2010
comment
Мне просто нравятся отрицательные голоса, которые появляются через шесть месяцев после публикации ответа. - person 3Dave; 10.11.2010

Photoshop, как и многие другие медиа-ориентированные программы, долгое время сталкивался с проблемой работы с файлами, размер которых превышал размер основной памяти. Для больших фотографий одна из техник - мозаика, позволяющая обработать только часть изображения.

На практике это более запутано (каламбур), чем просто вырезать и вставить.

Я не программист на Delphi, но меня беспокоит то, что когда у вас заканчивается память для создания образа, не произойдет ли то же самое, когда вы попытаетесь использовать это изображение?

person Peter Tillemans    schedule 27.06.2010
comment
Я знал, что это произойдет, но нет, как было сказано ранее, я могу открыть изображение намного большего размера и поработать над ним в Photoshop;) - person Alexander; 27.06.2010

Для такой тяжелой работы я прибегаю к http://www.graphicsmagick.org/

Здесь также есть набор единиц Паскаля http://graphics32.org/, но они довольно математичны и сложные (и поэтому я не заставил их работать), но также созданы для тяжелой работы.

person Stijn Sanders    schedule 28.06.2010
comment
Спасибо, но что касается graphicsmagick, пожалуйста, прочтите мой вопрос: Прежде всего, я НЕ хочу использовать ImageMagick для этого, а выполняю это в моем программном обеспечении! - person Alexander; 29.06.2010
comment
GraphicsMagick имеет библиотеку C ++, которую вы можете использовать при сборке, и интерфейс OLE: graphicsmagick.org/programming.html (опять же, я должен признать, использование библиотеки C ++ из Delphi - это совсем другая история) - person Stijn Sanders; 01.07.2010

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

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

Они утверждают, что большие изображения и матрицы данных легко обрабатываются и размещены на странице их снимков экрана, они показывают изображение 5200 x 5200 (26 МБ) и говорят, что их тесты также проводились с размером изображения до 220 МБ.

Если вам действительно нужна обработка больших изображений непосредственно в приложении, этот пакет может вам подойти за 50 долларов. Если он близок, но не совсем правильно (я не знаю, присоединится ли он к jpeg), вы можете подумать о покупке исходного кода за 299 долларов, посмотреть, что он делает, и расширить его.

Отказ от ответственности: я не имею отношения к этой компании.

person lkessler    schedule 27.06.2010
comment
Образ 220 Мб (‹2 Гб) и образ 2,4 Гб (› 2 Гб) - это совершенно разные лиги. Вы можете загрузить первое изображение в память. Хорошо, он большой, но не НАСТОЛЬКО большой, так что это возможно. Но для второго изображения этого сделать нельзя. Поскольку его размер больше вашего адресного пространства. - person Alex; 28.06.2010

А как насчет частичного внешнего программного обеспечения / решения для внутреннего вызова? IJG (Independent JPEG Group) имеет отличный инструмент командной строки jpegtran, который я использовал в моем средстве просмотра для вращения без потерь. Нет проблем использовать CreateProcess, WaitForsingleObject внутри вашего собственного кода, чтобы он выглядел как ваш собственный код. Вы даже можете упаковать исполняемые файлы внутри своего ресурса и извлечь его временно.

Так что у них также есть утилита jpegjoin (ее можно найти на http://jpegclub.org/jpegtran/), которая может использоваться таким же образом. ОБНОВЛЕНИЕ: эта утилита предназначена для соединения без потерь, поэтому требуется гораздо меньший объем памяти / диска

person Maksee    schedule 08.07.2010