Эффективная обработка изображений в C # - PullRequest
11 голосов
/ 28 мая 2009

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

У предыдущего парня были статические методы, которые получали растровое изображение в качестве параметра, а также возвращали его, внутренне выделяя и удаляя объект Graphics. Насколько я понимаю, экземпляр Bitmap содержит все изображение в памяти, в то время как Graphics в основном представляет собой очередь операций рисования, и что он идемпотентен.

В настоящее время процесс работает следующим образом:

  • Получить изображение и сохранить его во временном файле.
  • Получение координат обрезки.
  • Загрузить исходное растровое изображение в память.
  • Создание нового растрового изображения из оригинала с применением обрезки.
  • Сделайте некоторую сумасшедшую регулировку яркости на новом растровом изображении, возможно (?) Вернув новое растровое изображение (я бы не стал трогать это; арифметика указателей предостаточно!), Давайте назовем это A.
  • Создайте еще одно растровое изображение из полученного, применяя водяной знак (давайте назовем это B1)
  • Создание растрового изображения в виде пиктограммы 175x175 из A.
  • Создание растрового изображения миниатюры 45x45 из A.

Это похоже на большое распределение памяти; у меня такой вопрос: это хорошая идея переписать части кода и повторно использовать экземпляры Graphics, фактически создавая конвейер? По сути, мне нужно только 1 изображение в памяти (исходная загрузка), а остальные могут быть записаны непосредственно на диск. Для всех сгенерированных изображений потребуются преобразования обрезки и яркости, а также одно преобразование, уникальное для этой версии, для эффективного создания дерева операций.

Есть мысли или идеи?

О, и я, наверное, должен упомянуть, что я впервые работаю с .NET, поэтому, если что-то, что я говорю, кажется смущенным, пожалуйста, потерпите меня и дайте мне несколько советов.

Ответы [ 4 ]

3 голосов
/ 03 июня 2009

Повторное использование графических объектов, вероятно, не приведет к значительному увеличению производительности.

Основной код GDI просто создает контекст устройства для растрового изображения, загруженного в ОЗУ (DC памяти).

Узким местом вашей операции является загрузка образа с диска.

Зачем перезагружать образ с диска? Если он уже находится в байтовом массиве в ОЗУ, что и должно быть при загрузке - вы можете просто создать поток памяти в байтовом массиве, а затем создать растровое изображение из этого потока памяти.

Другими словами, сохраните его на диск, но не перезагружайте, просто работайте с ним из ОЗУ.

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

Вы должны профилировать операцию, чтобы увидеть, где она нуждается в улучшении (или даже если она нуждается в улучшении.)

3 голосов
/ 28 мая 2009

Процесс кажется разумным. Каждое изображение должно существовать в памяти, прежде чем оно будет сохранено на диск - поэтому каждая версия ваших миниатюр будет в памяти в первую очередь. Ключом к тому, чтобы убедиться, что это работает эффективно, является удаление объектов Graphics и Bitmap. Самый простой способ сделать это с помощью оператора using.

using( Bitmap b = new Bitmap( 175, 175 ) )
using( Graphics g = Graphics.FromBitmap( b ) )
{
   ...
}
2 голосов
/ 03 июня 2009

Я завершил аналогичный проект некоторое время назад и провел некоторое практическое тестирование, чтобы выяснить, есть ли разница в производительности, если я повторно использую объект Graphics, а не раскручиваю новый объект для каждого изображения. В моем случае я работал над постоянным потоком большого количества изображений (> 10 000 в «пакете»). Я обнаружил, что получил небольшое увеличение производительности за счет повторного использования объекта Graphics.

Я также обнаружил, что получил небольшое увеличение, используя GraphicsContainers в объекте Graphics, чтобы легко заменять различные состояния в / из объекта, так как он использовался для выполнения различных действий. (В частности, мне пришлось применить обрезку и нарисовать текст и прямоугольник на каждом изображении.) Я не знаю, имеет ли это смысл для того, что вам нужно сделать. Возможно, вы захотите взглянуть на методы BeginContainer и EndContainer в объекте Graphics.

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

Некоторые ссылки, которые могут вам пригодиться:

Использование вложенных графических контейнеров
Класс GraphicsContainer

0 голосов
/ 28 мая 2009

Я расскажу об этом только случайно, но если вам нужно краткое «руководство» по наилучшим методам работы с изображениями, посмотрите проект Paint.NET. Чтобы получить бесплатные высокопроизводительные инструменты для работы с изображениями, смотрите AForge.NET .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...