Внимание!

Эта публикация перенесена в раздел уроков по адресу Обработка изображений в С++.
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

Обработка изображений в С++

Предисловие
Сидел я и программировал разнообразную ерунду, а именно графические фильтры и все что с ними связано, на C#. И потихоньку учил С++. Потом решил я сравнить производительность C# и C++.В тесте будут принимать участие самые распространённые графические фильтры : негатив, сепия, корректировка яркости и контрастности. Написал основу на С++ для загрузки/сохранения изображений с использованием DevIL, и перевел коды фильтров с шарпа на C++. Хотя эта основа наверное далека от идеала.
[spoiler]
Вот тест производительности :


Теперь можно посчитать во сколько раз увеличилась производительность :
Пусть


- функция подсчёта во сколько раз различается производительность C# и С++ кода, тогда
f(Negative) = 515 / 16 = 32,1875
f(Sepia) = 562 / 15 = 37,4(6)
f(Brightness) = 749 / 5 = 149,5
f(Contrast) = 780 / 63 = 12,38


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


C этого момента я буду писать уроки только на С++, но и поддержу пользователя с ником darkx,который предложил создавать кросс лэнг уроки в этом комменте. В каждом уроке будут исходники на языках VB.NET, C#.NET, C++, про Delphi ничего сказать не могу,так как очень давно на нём програмировал.

В VB.NET/C#.NET будет использоваться основа с названием Filter, об которой я расказывал в предыдущих уроках :
1) Графические фильтры на основе попиксельной обработки изображений (Часть 1)
2) Графические фильтры на основе попиксельной обработки изображений (Часть 2)
3) Графические фильтры на основе попиксельной обработки изображений (Часть 3)

А в С++ будет использоваться основа, о которой я счас расскажу.

Основа для программирования графических фильтров в С++

При программировании этой основы используется библиотека DevIL, о которой подробно рассказываетcя в этой статье.
Создайте у себя в проекте файлы :
1) Image.h - header file
2) Image.cpp - cpp code file

В файле Image.h будут храниться описания функций, а в файле Image.cpp - реализации функций. В названиях функций, констант вы встретите приставку "ag", вместо неё вы можете поставить любую другую.Это сделано для того чтобы не было дубликатов функций или констант в разных API.

Файл Image.h
/*http://esate.ru, Alexei16*/


#pragma once

#include <windows.h>
#include <il.h>             //|
                       // Подключаем DevIL
#pragma comment(lib, "DevIL.lib") //|


enum Pixelformat
{
  AG_BGR,  //Формат пикселей B|G|R
      AG_BGRA, //Формат пикселей B|G|R|A
      AG_RGB,  //Формат пикселей R|G|B
      AG_RGBA  //Формат пикселей R|G|B|A
};

enum ImageType
{
  AG_BMP = 0x0420,  //!< Microsoft Windows Bitmap - .bmp extension
  AG_JPG = 0x0425,  //!< JPEG - .jpg, .jpe and .jpeg extensions
  AG_PNG = 0x042A,  //!< Portable Network Graphics - .png extension
  AG_TGA = 0x042D,  //!< TrueVision Targa File - .tga, .vda, .icb and .vst extensions
  AG_TIF = 0x042E,  //!< Tagged Image File Format - .tif and .tiff extensions
  AG_JP2 = 0x0441   //!< Jpeg 2000 - .jp2 extension
};

typedef struct BaseIMGInfo
{
  int Depth;   //Глубина изобаржения(нужно только для 3D текстур), по умолчанию = 1
  int Format;  //Формат пикселей
  int Type;   //Тип данных в памяти, в основном = IL_UNSIGNED_BYTE
};

typedef struct ImageData
{
  unsigned char * Data; //Указатель на данные изобржения

  int Width;        //Ширина изображения
  int Height;        //Высота изображения
  int Bpp;          //Количество байт на пиксель
  int Stride;        //Количество байт в одной строке пикселей

  Pixelformat PixFormat;//Формат пикселей
  BaseIMGInfo BaseInfo; //Основные параметры изображения, нужные для сохранения изображений в DevIL

  int R_idx;        //Позиция красного в пикселе
  int G_idx;        //Позиция зелёного в пикселе
  int B_idx;        //Позиция синего в пикселе
  int A_idx;        //Позиция альфа-канала в пикселе, только для AG_RGBA или AG_BGRA
};

ImageData * agLoadImage(char * FileName); //Функция для загрузки изображения
bool agSaveImage(ImageData * IMG, char * FileName, ImageType IType); //Функция для сохранения изображения
ImageData * agNewImage(int Width, int Height,  Pixelformat PFormat); //Функция для создания нового,пустого, изображения
ImageData * agCloneImage(ImageData * Src); //Функция для создания копии изображения


Директива препроцесора #pragma once нужна, для контроля за тем, чтоб конкретный файл подключался при компиляции только один раз.Думаю больше никаких обьяснений к файлу Image.h не надо.Теперь перейдём к реализации.

Откройте файл Image.cpp, и включите в него Image.h :
/*http://esate.ru, Alexei16*/


#include "Image.h"

Теперь разберём каждую функцию;Объяснения к коду будут в комментариях.
1)Функция agLoadImage
/*http://esate.ru, Alexei16*/


ImageData * agLoadImage(char * FileName) //FileName - указатель на строку с именем файла
{
  ILuint * Texture = new ILuint; //Указатель на текстуру
  ImageData * Result = NULL; //Изображение

  ilInit();           //И и и л з ц я D v L
  ilEnable(IL_ORIGIN_SET); // н ц а и а и   е I
  ilGenImages(1, Texture); //Генерируем текстуру
  ilBindImage(*Texture);   //Делаем текстуру текущей

  if(ilLoadImage(FileName) == true) //Пытаемся загрузить изображение
  { // если да, то
    Result = new ImageData(); //Создаём изображение
    
    Result -> Width = ilGetInteger(IL_IMAGE_WIDTH); //Получаем ширину изображения
    Result -> Height = ilGetInteger(IL_IMAGE_HEIGHT);//Получаем высоту изображения
    Result -> Bpp = ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL);//Получаем количество байт на пиксель
    Result -> Stride = Result -> Width * Result -> Bpp;//Вычислем количество байт в одной строке пикселей

    Result -> Data = new unsigned char[Result -> Width * Result -> Height * Result -> Bpp]; //Создаём указатель на данные изображения

    

    memcpy(Result -> Data, ilGetData(), ilGetInteger(IL_IMAGE_SIZE_OF_DATA)); //Копируем данные из памяти в наше изображение

    Result -> BaseInfo.Type = ilGetInteger(IL_IMAGE_TYPE);//Получаем тип данных в памяти
    Result -> BaseInfo.Format = ilGetInteger(IL_IMAGE_FORMAT);//Получаем формат пикселей
    Result -> BaseInfo.Depth = ilGetInteger(IL_IMAGE_DEPTH);//Получаем глубину изображения

    if(Result -> BaseInfo.Format == IL_RGB) //Если формат пикселей BGR, то
    {                           //данные располагаются в памяти
      Result -> R_idx = 0;        //так ->
      Result -> G_idx = 1;        //0|1|2|0|1|2|0|1|2|0|1
      Result -> B_idx = 2;        //R|G|B|R|G|B|R|G|B|R|G
    }
    else if(Result -> BaseInfo.Format == IL_RGBA) //Если формат пикселей RGBA,то
    {                            //данные располагаются в памяти
      Result -> R_idx = 0;           //так ->
      Result -> G_idx = 1;           //0|1|2|3|0|1|2|3|0|1|2|3|0
      Result -> B_idx = 2;           //R|G|B|A|R|G|B|A|R|G|B|A|R
      Result -> A_idx = 3;
    }
    else if(Result -> BaseInfo.Format == IL_BGR) //Если формат пикселей BGR,то
    {                              //данные располагаются в памяти
      Result -> R_idx = 2;            //так ->
      Result -> G_idx = 1;            //0|1|2|0|1|2|0|1|2|0|1|2
      Result -> B_idx = 0;            //B|G|R|B|G|R|B|G|R|B|G|R
    }
    else if(Result -> BaseInfo.Format == IL_BGRA) //Если формат пикселей BGRA, то
    {                            //данные располагаются в памяти
      Result -> R_idx = 2;           //так ->
      Result -> G_idx = 1;           //0|1|2|3|0|1|2|3|0|1|2|3|0
      Result -> B_idx = 0;           //B|G|R|A|B|G|R|A|B|G|R|A|B
      Result -> A_idx = 3;
    }
    else
    {
      MessageBoxA(0, "Unsuitable pixel format", "Error", MB_OK | MB_ICONERROR);
    }
  }
  else
  { //Если нет, то выдаём сообщение об ошибке
    MessageBoxA(0, "Could not load image", "Error", MB_OK | MB_ICONEXCLAMATION);
  }
  ilDeleteImages(1, Texture); //Удаляем текстуру ->
  delete Texture;         //->|

  return Result;
}


2)Функция agSaveImage
/*http://esate.ru, Alexei16*/


bool agSaveImage(ImageData * IMG, char * FileName, ImageType IType) //IMG - изображение, FileName - имя сохраняемого файла, IType - расширение файла
{
  bool Res;//Результат функции
  if(IMG != NULL && strlen(FileName) > 0)//Если изображение не пустое и длинна имени файла больше нуля,то
  {
    ILuint * Texture = new ILuint;//Указатель на текстуру

    ilInit(); //Инициализация DevIL
    ilGenImages(1, Texture);//Генерируем текстуру
    ilBindImage(*Texture);  //Делаем её текущей

    ilTexImage(IMG -> Width,IMG -> Height,
           IMG -> BaseInfo.Depth,IMG -> Bpp,IMG -> BaseInfo.Format,IMG -> BaseInfo.Type,
           IMG -> Data);//Закидываем данные в память
    Res = ilSave(IType, FileName);//И сохраняем изображение

    ilDeleteImages(1, Texture); //Удаляем текстуру ->
    delete Texture;         //->|
  }
  else
  { //Иначе,выдаем сообщене об ошибке
    MessageBoxA(0, "Pointer to the image is empty or the wrong name of the saved file", "Error", MB_OK | MB_ICONERROR);
  }
  return Res;
}


3)Функция agNewImage
/*http://esate.ru, Alexei16*/


ImageData * agNewImage(int Width, int Height,  Pixelformat PFormat)
{
  ImageData * Result = NULL;//Результат функции
  if(Width > 0 && Height > 0) //Если длинна и ширина - положительные величины, то
  {
    Result = new ImageData();//Создаем изображение

    Result -> Width = Width; // Присваиваем длинну
    Result -> Height = Height; // Присваиваем ширину
    Result -> PixFormat = PFormat; // Присваиваем формат пикселей

    Result -> BaseInfo.Depth = 1;
    Result -> BaseInfo.Type = IL_UNSIGNED_BYTE;

    if(Result -> PixFormat == AG_BGR)
    {
      Result -> R_idx = 2;
      Result -> G_idx = 1;
      Result -> B_idx = 0;
      Result -> BaseInfo.Format = IL_BGR; 
      Result -> Bpp = 3; //3 байта на пиксель, потомучто B(1 byte) + G(1 byte) + R(1 byte) = 3 bytes 
    }
    else if(Result -> PixFormat == AG_BGRA)
    {
      Result -> R_idx = 2;
      Result -> G_idx = 1;
      Result -> B_idx = 0;
      Result -> A_idx = 3;
      Result -> BaseInfo.Format = IL_BGRA;
      Result -> Bpp = 4; //4 байта на пиксель, потомучто B(1 byte) + G(1 byte) + R(1 byte) + A(1 byte) = 3 bytes
    }
    else if(Result -> PixFormat == AG_RGB)
    {
      Result -> R_idx = 0;
      Result -> G_idx = 1;
      Result -> B_idx = 2;
      Result -> BaseInfo.Format = IL_RGB;
      Result -> Bpp = 3;
    }
    else if(Result -> PixFormat == AG_RGBA)
    {
      Result -> R_idx = 0;
      Result -> G_idx = 1;
      Result -> B_idx = 2;
      Result -> A_idx = 3;
      Result -> BaseInfo.Format = IL_RGBA;
      Result -> Bpp = 4;
    }

    Result -> Data = new unsigned char[Result -> Width * Result -> Height * Result -> Bpp]; //Выделяем память под данные изображения
    Result -> Stride = Result -> Width * Result -> Bpp; //Вычисляем количество байт на строку пикселей
  }
  else
  { //Иначе выдаем сообщение об ошибке
    MessageBoxA(0, "Invalid input parameters", "Error", MB_OK | MB_ICONERROR);
  }
  return Result;
}


4) Функция agCloneImage
/*http://esate.ru, Alexei16*/


ImageData * agCloneImage(ImageData * Src) //Src - исходное изображение
{
  ImageData * Dst = NULL; //Новое изображение
  if(Src != NULL) // Если исходное изображение не пустое,то
  {
    Dst = new ImageData();//Создаем новое изображение

    Dst -> B_idx = Src -> B_idx;// далее все просто копируем
    Dst -> G_idx = Src -> G_idx;
    Dst -> R_idx = Src -> R_idx;
    Dst -> A_idx = Src -> A_idx;

    Dst -> Width = Src -> Width;
    Dst -> Height = Src -> Height;
    Dst -> Stride = Src -> Stride;
    Dst -> Bpp = Src -> Bpp;
    Dst -> PixFormat = Src -> PixFormat;

    Dst -> BaseInfo.Depth = Src -> BaseInfo.Depth;
    Dst -> BaseInfo.Format = Src -> BaseInfo.Format;
    Dst -> BaseInfo.Type = Src -> BaseInfo.Type;
    try
    {
      Dst -> Data = new unsigned char[Dst -> Width * Dst -> Height * Dst -> Bpp];
      memcpy(Dst -> Data, Src -> Data, Dst -> Width * Dst -> Height * Dst -> Bpp);
    }
    catch(...)
    {
      MessageBoxA(0, "Can not allocate memory", "Error", MB_OK | MB_ICONERROR);
      delete Dst;
    }
  }
  else
  { //иначе, выдаем сообщение об ошибке
    MessageBoxA(0, "Invalid input parameters", "Error", MB_OK | MB_ICONERROR);
  }
  return Dst;
}


Вот собственно и все!
</cut>
0      2376        01.08.2011        25

Внимание!

Эта публикация перенесена в раздел уроков по адресу Обработка изображений в С++.
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

0 
01.08.2011 00:00:00
когда научитесь использовать

топик на всю страницу, УЖОС ПРОСТО!!!
а топик супер)
0 
01.08.2011 00:00:00
уже исправлено, вставлен
0 
01.08.2011 00:00:00
когда научитесь использовать cut>

топик на всю страницу, УЖОС ПРОСТО!!!
а топик супер)
0 
02.08.2011 00:00:00
Супер!!!)
Я как то подумывал, хм..), хотя честно говоря хочу, написать кросс платформенную прогу которая будет использовать OGL и DevIL для просмотра изображении и некоторых преобразовании(с возможностью сохранения), но все никак руки не дотянуться.
Да еще жесткий посыпался, и вся литература, либы, проекты, заготовки тож «посыпались»((( Ща сижу на Ubuntu с флехи(4 гига) памяти в обрез, работает с подвисонами.(( эх мне б жесткий)))
А ща тут классный код. Буду теперь брать за основу, если руки все же дотянуться писать. Если ты не против.
0 
02.08.2011 00:00:00
как я тебя понимаю, я примерно часа 3 назад случано форматнул хард, представляешь какая херь была? это пиздец просто (сори за мат) у меня всего то это доки проекты серв и игра 50 метров, все удалилось меньше чем за 5 секунд, я даже ничего не успел сделать, это была порсто жопа какая то)) и не восстановилось обыдно что ничего)) придется все заново писать
0 
02.08.2011 00:00:00
Слушай на лине полное форматирование идет или быстрое(ну как на винде)?
Просто у меня была проблема, я линь ставил, потом удалил, поставил Виндовс. И один раздел не видело(в Ntfs).Я его удалил, потом форматнул(быстрое форматирование). А потом через какую-то прогу(вроде Partion manager) восстановил все файлы на другой раздел.
Она вроде и раздел с после Linux(ext4) видела и могла восстанавливать.
Может типо такого есть на Linux?.. хотя может уже поздно((
0 
02.08.2011 00:00:00
не знаю, я не совсем форматнул, удялял файл и случайно выделил все файлы, и по привычке shift del и все, ничего нету(
0 
03.08.2011 00:00:00
Нет способа восстановить?! ппц.
Сочувствую.
Мне хотя и очень обидно за свой жесткий, но он мне верно прослужил около 5-6 лет. Вот — вот собирался новый брать и тут бац, и ппц.
0 
02.08.2011 00:00:00
конечно я не против
0 
03.08.2011 00:00:00
Протести плиз
0 
02.08.2011 00:00:00
Урок супер!
Кстати вроде тут C, а на C++, так не одна из возможностей с++ не использована. А если переделать твою основу в класс, это могло увеличить производительность, так как у тебя инициализация DevIL при каждом вызове функции, а вызовов может быть много.
Это можно сделать один раз в конструкторе класса, и еще не плохо было бы сделать, чтоб класс использовался как контейнер для данных изображения.
0 
02.08.2011 00:00:00
использована:
new, delete — выделение\удаление памяти в стиле С++.
malloc(), free() — выделение\удаление памяти в стиле C.
а на счёт классов ты предлагаешь сделать как в шарпе Bitmap?.. хотя реализация классов в с++ мне не очень нравится.
0 
02.08.2011 00:00:00
А насчет инициализации, это не плохая идея, но может просто функции init()… ИМх0)
0 
02.08.2011 00:00:00
Блин, что то я, переоценил свои знания, и включил поучилку.(
Извините.
И, блин, меня учили, что new delete это C, меня ввели в заблуждение(.
Все буду помалкивать.
0 
02.08.2011 00:00:00
помойму кому это надо будет то сделает, уроки должны учить а не делать за других!!!
0 
02.08.2011 00:00:00
полностью согласен.
0 
02.08.2011 00:00:00
ИМХО alt >(
0 
02.08.2011 00:00:00
сории за оч тупой вопрос, что такое ИМХО =)
0 
03.08.2011 00:00:00
И́МХО или IMHO (англ. IMHO), также имхо или imho (строчными буквами) — известное выражение, означающее «по моему́ скромному мнению» (англ. In My Humble Opinion или In My Honest Opinion).
0 
03.08.2011 00:00:00
ок спс, терь знаю что эт такое))
0 
03.08.2011 00:00:00
Протести плиз
0 
02.08.2011 00:00:00
Но я ж не просил, сделать это!!! И тем более за меня!
Я просто высказал, мнение, хоть и неверное.
Просто не прислушивайтесь, скажите че я не прав.
Че так сразу нападать?!
0 
02.08.2011 00:00:00
никто ни на кого не наподает.
0 
02.08.2011 00:00:00
угу, просто вырозил мнение, и всо)
0 
21.01.2013 00:00:00
Основа есть, а а как использовать ее, урока нет(
^
Регистрация
Регистрируясь, вы принимаете правила сайта. Если вы не получили код подтв. регистрации - не забудьте проверить папку спам.
Логин*
Email*
Пароль*
Подтверждение пароля*
 
Логин*
Код*
 
×
Восстановление пароля
Пожалуйста, заполните поля, после чего вы получите код подтверждения на ваш Email. Если код не пришел в течении нескольких минут - проверьте папку спам.
Логин

или Email
Логин*
Код подтверждения*
Новый пароль*
Подтверждение пароля*
×
Авторизация
  • Используйте вашу учетную запись на Facebook.com для входа на сайт.
  • Используйте вашу учетную запись VKontakte для входа на сайт.
  • Используйте вашу учетную запись Google для входа на сайт.
Авторизуйтесь с помощью соц. сети или с помощью аккаунта на сайте:
×