Эта публикация перенесена в раздел уроков по адресу Обработка изображений в С++. К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации. Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.
Предисловие Сидел я и программировал разнообразную ерунду, а именно графические фильтры и все что с ними связано, на 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 ничего сказать не могу,так как очень давно на нём програмировал.
А в С++ будет использоваться основа, о которой я счас расскажу.
Основа для программирования графических фильтров в С++
При программировании этой основы используется библиотека 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;
}
Понравилась публикация? Сохраните ее, чтобы вернуться к изучению материала!
0
2484
01.08.2011
25
Внимание!
Эта публикация перенесена в раздел уроков по адресу Обработка изображений в С++. К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации. Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.
Супер!!!) Я как то подумывал, хм..), хотя честно говоря хочу, написать кросс платформенную прогу которая будет использовать OGL и DevIL для просмотра изображении и некоторых преобразовании(с возможностью сохранения), но все никак руки не дотянуться. Да еще жесткий посыпался, и вся литература, либы, проекты, заготовки тож «посыпались»((( Ща сижу на Ubuntu с флехи(4 гига) памяти в обрез, работает с подвисонами.(( эх мне б жесткий))) А ща тут классный код. Буду теперь брать за основу, если руки все же дотянуться писать. Если ты не против.
как я тебя понимаю, я примерно часа 3 назад случано форматнул хард, представляешь какая херь была? это пиздец просто (сори за мат) у меня всего то это доки проекты серв и игра 50 метров, все удалилось меньше чем за 5 секунд, я даже ничего не успел сделать, это была порсто жопа какая то)) и не восстановилось обыдно что ничего)) придется все заново писать
Слушай на лине полное форматирование идет или быстрое(ну как на винде)? Просто у меня была проблема, я линь ставил, потом удалил, поставил Виндовс. И один раздел не видело(в Ntfs).Я его удалил, потом форматнул(быстрое форматирование). А потом через какую-то прогу(вроде Partion manager) восстановил все файлы на другой раздел. Она вроде и раздел с после Linux(ext4) видела и могла восстанавливать. Может типо такого есть на Linux?.. хотя может уже поздно((
Нет способа восстановить?! ппц. Сочувствую. Мне хотя и очень обидно за свой жесткий, но он мне верно прослужил около 5-6 лет. Вот — вот собирался новый брать и тут бац, и ппц.
Урок супер! Кстати вроде тут C, а на C++, так не одна из возможностей с++ не использована. А если переделать твою основу в класс, это могло увеличить производительность, так как у тебя инициализация DevIL при каждом вызове функции, а вызовов может быть много. Это можно сделать один раз в конструкторе класса, и еще не плохо было бы сделать, чтоб класс использовался как контейнер для данных изображения.
использована: new, delete — выделение\удаление памяти в стиле С++. malloc(), free() — выделение\удаление памяти в стиле C. а на счёт классов ты предлагаешь сделать как в шарпе Bitmap?.. хотя реализация классов в с++ мне не очень нравится.
Блин, что то я, переоценил свои знания, и включил поучилку.( Извините. И, блин, меня учили, что new delete это C, меня ввели в заблуждение(. Все буду помалкивать.
И́МХО или IMHO (англ. IMHO), также имхо или imho (строчными буквами) — известное выражение, означающее «по моему́ скромному мнению» (англ. In My Humble Opinion или In My Honest Opinion).
Но я ж не просил, сделать это!!! И тем более за меня! Я просто высказал, мнение, хоть и неверное. Просто не прислушивайтесь, скажите че я не прав. Че так сразу нападать?!
Стоит ли переходить на Windows 10?Windows 10 установлена на каждый 3-й компьютер. Какие плюсы от перехода? DirectX 12 работает только в Windows 10?
Как установить Windows 10?Как бесплатно и легально скачать? Как записать и установить с последними обновлениями?
Сохраните страницу!
Регистрация
Регистрируясь, вы принимаете правила сайта. Если вы не получили код подтв. регистрации - не
забудьте проверить папку спам.
×
Восстановление пароля
Пожалуйста, заполните поля, после чего вы получите код подтверждения на ваш Email. Если код не пришел в течении нескольких минут - проверьте папку спам.
×
Авторизация
Авторизуйтесь с помощью соц. сети или с помощью аккаунта на сайте:
топик на всю страницу, УЖОС ПРОСТО!!!
а топик супер)
топик на всю страницу, УЖОС ПРОСТО!!!
а топик супер)
Я как то подумывал, хм..), хотя честно говоря хочу, написать кросс платформенную прогу которая будет использовать OGL и DevIL для просмотра изображении и некоторых преобразовании(с возможностью сохранения), но все никак руки не дотянуться.
Да еще жесткий посыпался, и вся литература, либы, проекты, заготовки тож «посыпались»((( Ща сижу на Ubuntu с флехи(4 гига) памяти в обрез, работает с подвисонами.(( эх мне б жесткий)))
А ща тут классный код. Буду теперь брать за основу, если руки все же дотянуться писать. Если ты не против.
Просто у меня была проблема, я линь ставил, потом удалил, поставил Виндовс. И один раздел не видело(в Ntfs).Я его удалил, потом форматнул(быстрое форматирование). А потом через какую-то прогу(вроде Partion manager) восстановил все файлы на другой раздел.
Она вроде и раздел с после Linux(ext4) видела и могла восстанавливать.
Может типо такого есть на Linux?.. хотя может уже поздно((
Сочувствую.
Мне хотя и очень обидно за свой жесткий, но он мне верно прослужил около 5-6 лет. Вот — вот собирался новый брать и тут бац, и ппц.
Кстати вроде тут C, а на C++, так не одна из возможностей с++ не использована. А если переделать твою основу в класс, это могло увеличить производительность, так как у тебя инициализация DevIL при каждом вызове функции, а вызовов может быть много.
Это можно сделать один раз в конструкторе класса, и еще не плохо было бы сделать, чтоб класс использовался как контейнер для данных изображения.
new, delete — выделение\удаление памяти в стиле С++.
malloc(), free() — выделение\удаление памяти в стиле C.
а на счёт классов ты предлагаешь сделать как в шарпе Bitmap?.. хотя реализация классов в с++ мне не очень нравится.
Извините.
И, блин, меня учили, что new delete это C, меня ввели в заблуждение(.
Все буду помалкивать.
Я просто высказал, мнение, хоть и неверное.
Просто не прислушивайтесь, скажите че я не прав.
Че так сразу нападать?!