Как работать с изображениями

Общие советы по работе с изображениями

Не стоит работать на низком уровне с изображениями через системные функции

Системные типы и классы HBITMAP, Bitmap и им подобные рассчитаны, в первую очередь, на вывод на экран. Для них существует множество функций рисования геометрических примитивов, вывода текста, но при этом отсутствует прямой доступ к пикселям изображения. Функции Bitmap::GetPixel и им подобные работают непозволительно долго.

Оптимальным вариантом является работа с изображением непосредственно в памяти. Это можно сделать двумя способами:

  • Написав свои классы для работы с изображениями. Особенно полезно это будет для тех, кто не имеет достаточного опыта программирования.
  • Через DIB (device independent bitmap). Данный способ рекомендуется в случае, когда помимо прямого доступа к пискелям изображения нужно рисовать на нём с помощью GDI-функций.

Работайте с пикселями в формате float, а не byte

Экономия памяти важна при написании коммерческого кода, но не при разработке математических методов обработки изображений. Использование float имеет следующие преимущества перед byte:
  • Отсутствие ошибок, связанных с переполнением при выходе за границы диапазона [0, 255]
  • Не накапливаются ошибки округления.
  • Многие алгоритмы значительно проще реализуются при использовании типа float, например, Canny edge detection. Некоторые алгоритмы вообще не могут быть реализованы при использовании типа byte для пикселя.
Более того, в современных процессорах работа с float будет производиться быстрее, чем с byte.

Делайте обход по изображениям в правильном порядке

Двумерные изображения хранятся в памяти в виде одномерных массивов. Обычно они записываются построчно: сначала идёт 0-я строка, затем 1-я и т.д. Последовательный доступ к памяти осуществляется быстрее, чем произвольный. Поэтому обход по изображению нужно делать так, чтобы доступ к памяти был последовательный: во внешнем цикле производится обход по вертикали, а во внутреннем — по горизонтали:

for (int y = 0; y < image.Height(); y++)
  for (int x = 0; x < image.Width(); x++)
    ...

Также стоит быть аккуратным при использовании двумерных массивов в C# (а лучше вообще их не использовать, написав класс-обёртку для одномерных массивов): в них первый индекс — это строка (Y-координата), а вторая — столбец (X).

Как загружать и сохранять изображения

Вы всегда можете ознакомиться с форматами графических файлов и написать свои функции (классы, библиотеки) для чтения и сохранения изображений в желаемых форматах. Однако если для формата BMP написание подобных функций не составляет труда, то для более популярных форматов, таких как JPEG и PNG, написание своего декодера — идея не из лучших.

Существует множество сторонних библиотек для работы с изображениями в C++, которые можно использовать для загрузки и сохранения и загрузки изображений, однако в системе Windows для этих задач можно использовать встроенную библиотеку GDI+. Общий принцип работы такой: загрузить изображение в класс Gdiplus::Bitmap, получить прямой доступ к пикселям изображения и преобразовать изображение во внутреннее представление.

Ниже приложены проекты для Microsoft Visual Studio 2010, в которых реализованы функции чтения и записи изображений: