Эффективное манипулирование изображениями в С#

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

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

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

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

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

Любая мысль или идеи?

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

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

using( Bitmap b = new Bitmap( 175, 175 ) )
using( Graphics g = Graphics.FromBitmap( b ) )
{
   ...
}

Ответ 3

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

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

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

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

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

Ответ 4

Я просто собираюсь выбросить это случайно, но если вы хотите получить краткое "руководство" по лучшим методам работы с изображениями, посмотрите на проект Paint.NET. Для бесплатных высокопрофессиональных инструментов для манипуляций с изображениями посмотрите AForge.NET.

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