Эффективное создание нескольких миниатюр из одного потока изображений - PullRequest
1 голос
/ 04 декабря 2011

У меня есть сервлет загрузки изображений, который получает загруженные изображения через HTTP POST и представляют собой изображения с высоким разрешением, размер которых варьируется от 5 МБ до 75 МБ. Данные изображения считываются из потока ввода запроса и сохраняются на локальном диске. Я ищу эффективный механизм для создания миниатюр параллельно (или частично последовательно, если не полностью параллельно) различных размеров (4-5 различных размеров, самый большой из которых - веб-изображение - 1024x768) из входного потока запроса вместе с сохранением потока в диск как оригинальный загруженный файл.

То, о чем я мог думать до сих пор, -

  1. Сохранить исходный поток как файл изображения на диск.
  2. Создание веб-изображения (1024x768), которое является самым большим среди множества миниатюр.
  3. Затем используйте это, чтобы генерировать последующие меньшие изображения, поскольку это будет быстрее.

Может кто-нибудь предложить более эффективный способ? Наиболее желательным подходом было бы сделать это синхронно, но асинхронность также подойдет, если она очень эффективна.

Любая помощь в этом отношении будет высоко цениться желательно на Java.

Ответы [ 2 ]

0 голосов
/ 08 декабря 2011

Дайте мне посмотреть, правильно ли я понял,

У вас есть одно большое изображение и вы хотите одновременно выполнять с ним различные операции.Некоторые операции связаны с дисковым вводом-выводом.

Вариант 1, Запуск 1 потока, чтобы сохранить исходный образ высокого разрешения на диск.Это займет много времени по сравнению с другими операциями, потому что запись на диск идет медленно.Запустите другие темы, чтобы создать миниатюры нужного размера.Вам необходимо изменить исходное изображение.Я считаю, что это может быть сделано путем клонирования байтов исходного изображения (в Java я предполагаю BufferedImage).Затем вы можете изменить размер клонов в соответствии с желаемыми размерами.Операция изменения размера быстрее по сравнению с записью на диск.

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

Примечание: используйте ExecutorService, у которого есть пул потоков, вместо отдельных потоков.В моем примере я использовал 1 поток на миниатюру, потому что это облегчает объяснение.

Вариант 2, Еще один способ сделать это - назначить один поток для записи на диск и несколькодругие рабочие потоки, чтобы сделать изменение размера.Кэшируйте все thmubnails в список, из которого поток, который записывает на диск, возьмет их один за другим и запишет их.

Вариант 3, Наконец, если у вас есть несколько дисков, вы можетедать каждому потоку диск для записи, тогда все записи будут выполняться параллельно (более или менее).

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

0 голосов
/ 05 декабря 2011

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

Ваша идея о создании меньшего изображения, а затем о создании миниатюр, вероятно, хорошая, но первое, что я хотел бы сказать, если у вас есть изображение размером 75 МБ, тогда оно явно намного больше, чем 1024x768 - скорее всего, несколько его кратных. , в этом случае вы хотите убедиться, что вы масштабируете изображение, используя SCALE_FAST ( Image ). Вы хотите добиться того, чтобы при масштабировании изображение уменьшалось до меньшего размера, отбрасывая пиксели, а не пытаясь сделать что-то более привлекательное (и гораздо более дорогое), например, усреднение по площади. Вы можете даже заставить его работать быстрее, взяв int [] для изображения и выбрав каждый N-й элемент, чтобы создать новый int [] для нового изображения, уменьшенного по некоторому коэффициенту.

В этот момент у вас будет уменьшенное изображение, скажем, примерно 2000 к 2000 году. Затем вы можете взять это изображение и масштабировать его, используя что-то более приятное, отыскивая реальные эскизы, такие как SCALE_SMOOTH.

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

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

Производитель Потребительское решение на Java

Таким образом, вы распараллеливаете, где это полезно (операции с процессором), и выполняете запись в файл последовательно (избегая побоев).

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

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

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

...