Обработка изображений в С++
Что прочитать перед этим материалом
Основа для программирования графических фильтров в С++
При программировании этой основы используется библиотека DevIL, о которой подробно рассказываетcя в этой статье.
Создайте у себя в проекте следующие файлы:
- Image.h - header file;
- Image.cpp - cpp code file;
В файле Image.h будут храниться описания функций, а в файле Image.cpp - реализации функций.
В названиях функций и констант вы встретите приставку “ag”, вместо неё вы можете поставить любую другую. Это сделано для того чтобы не было дубликатов функций или констант в разных API.
Файл Image.h
Код:
#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.cpp и включите в него Image.h:
Код:
#include "Image.h"
Теперь разберём каждую функцию по отдельности.
- Функция agLoadImage
Код:
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;
}
- Функция agSaveImage
Код:
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;
}
- Функция agNewImage
Код:
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;
}
- Функция agCloneImage
Код:
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;
}
Вот собственно и все!