Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
кгизображение.doc
Скачиваний:
13
Добавлен:
11.11.2019
Размер:
2.45 Mб
Скачать

Работа с файлами в формате bmp

Как уже говорилось, в рамках данного курса обработка изображений будет проводиться средствами библиотеки IPL. При этом, в качестве формата графических файлов для хранения исходных изображений, предназначенных для проведения дальнейшей обработки, предлагается использовать BMP. Причинами такого выбора является возможность хранений полноцветных изображений без потерь и хорошо известная, простая структура. К сожалению, библиотека IPL не располагает возможностями по работе с какими бы то ни было графическими файлами. Поэтому, прежде чем переходить к рассмотрению процедур обработки изображения, рассмотрим структуру формата BMP, и приведем пример чтения графического файла в этом формате.

Структура данных файла в формате BMP имеет следующий вид:

BITMAPFILEHEADER

BITMAPINFOHEADER

RGBQUAD array

Color-index array

В начале идет структура заголовка файла (BITMAPFILEHEADER), имеющая следующий вид:

typedef struct tagBITMAPFILEHEADER {     WORD bfType;     DWORD bfSize;     WORD bfReserved1;     WORD bfReserved2;     DWORD bfOffBits; } BITMAPFILEHEADER, *PBITMAPFILEHEADER;

bfType

Тип файла. Должен быть "BM".

bfSize

Размер файла в байтах.

bfReserved1, bfReserved2

Зарезервированные поля.

bfOffBits

Смещение битового массива относительно начала файла.

Далее следует структура информационного заголовка (BITMAPINFOHEADER), имеющая следующий вид:

typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER, *PBITMAPINFOHEADER;

biSize

Размер структуры.

biWidth, biHeight

Ширина и высота изображения в пикселах соответственно.

biPlanes

Количество плоскостей. Использовалось ранее при небольшой глубине фвета. Сейчас, при числе цветов 256 и больше, оно всегда равно 1. Сохранено для совместимости.

biBitCount

Глубина цвета в битах на пиксель.

biCompression

Тип сжатия. Если компрессия не используется, то флаг имеет значенине BI_RGB. Возможны варианты BI_RLE8, BI_RLE4, BI_BITFIELDS и BI_JPEG.

biSizeImage

Размер изображения в байтах. Если изображение не сжато (то есть поле biCompression установлено в BI_RGB), то здесь может быть записан 0.

biXPelsPerMeter, biYPelsPerMeter

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

biClrUsed

Количество используемых цветов кодовой таблицы. Если значение поля равно 0, то используется максимально возможное количество цветов, которые разрешены значением поля biBitCount.

biClrImportant

Количество основных цветов. Определяет число цветов, необходимых для отображения изображения. Если значение поля равно 0, то используются все цвета.

За информационным заголовком следует таблица цветов, представляющая собой массив структур RGBQUAD (4-байтовых полей). Каждое поле соответствует своему цвету в палитре, а три байта из четырех – синей, зеленой и красной компонентам этого цвета. Последний байт каждого поля зарезервирован и должен быть равен 0.

После таблицы цветов находятся данные изображения, которое по строкам растра записано снизу вверх, а внутри строки – слева направо. Длина каждой строки выровнена на границу в 4 байта (при длине строки, некратной четырем, она дополняется нулями).

Теперь рассмотрим пример реализации чтения графических файлов в формате BMP приложением, построенным на основе MFC с использованием библиотеки IPL:

//------------------------------------------------------------------------------ // Чтение файла в формате BMP BITMAPINFOHEADER* ipLoad(const char* fname) {     if(!fname)         return NULL;     BITMAPINFOHEADER* infohdr = NULL;     ifstream fsrc;

    try     {         BITMAPFILEHEADER filehdr;         BITMAPINFOHEADER bmphdr;     // Открыть файл         fsrc.open(fname, ios::in | ios::binary);         if(fsrc.fail())             throw runtime_error("Ошибка чтения графического файла");     // Чтение структуры BITMAPFILEHEADER         fsrc.read((char*)&filehdr, sizeof(BITMAPFILEHEADER) );         if(fsrc.fail())             throw runtime_error("Ошибка чтения заголовка файла BMP");     // Проверка типа файла         if(filehdr.bfType != 0x4d42)             throw runtime_error("Неверный тип исходного файла");     // Чтение структуры BITMAPINFOHEADER         fsrc.read((char*)&bmphdr, sizeof(BITMAPINFOHEADER));         if(fsrc.fail())             throw runtime_error("Ошибка чтения информационного заголовка файла BMP");     // Проверка типа файла         if(bmphdr.biSize != 0x28)             throw runtime_error("Неверный тип исходного файла");     // Определение размера массива цветов         int colorbytes = filehdr.bfOffBits - sizeof(BITMAPFILEHEADER) - sizeof(BITMAPINFOHEADER);     // Определение размера BITMAPINFOHEADER         int totalbytes = filehdr.bfSize - sizeof(BITMAPFILEHEADER);     // Размещение BITMAPINFOHEADER         infohdr = (BITMAPINFOHEADER*) new unsigned char[totalbytes];         if(!infohdr)             throw runtime_error("Ошибка выделения памяти для размещения изображения");     // Скопировать BITMAPINFOHEADER         memcpy(infohdr, &bmphdr, sizeof(bmphdr) );     // Определение смещения массива цветов         char* quads = (char*)infohdr + sizeof(BITMAPINFOHEADER);     // Определение смещения массива изображения         char* pixels = (char*)quads + colorbytes;     // Чтение массива цветов         fsrc.read((char*)quads, colorbytes);     // Чтение массива изображения         fsrc.read((char*)pixels, totalbytes - colorbytes - sizeof(BITMAPINFOHEADER));     // Закрыть файл         fsrc.close();     }     catch(runtime_error e)     {         TRACE0(e.what());         if(infohdr)         {             free( infohdr );             infohdr = 0;         }     }     return infohdr; } //------------------------------------------------------------------------------ // Чтение и размещение изображения void CTestDoc::Serialize(CArchive& ar) {     if(ar.IsStoring())     {     }     else     {     // Если в памяти уже имеется изображение, то ее необходимо освободить         if(m_img != NULL)         {             iplDeallocate(m_img, IPL_IMAGE_ALL);         }     // Чтение структуры данных файла BMP         BITMAPINFOHEADER* bmphdr = ipLoad(ar.GetFile()->GetFilePath());

    // Размещение изображения в памяти         if(bmphdr)         {         // Создание структуры заголовка изображения             m_img = iplCreateImageHeader(3, 0, IPL_DEPTH_8U, "RGB", "BGR",                        IPL_DATA_ORDER_PIXEL, IPL_ORIGIN_BL, IPL_ALIGN_DWORD, bmphdr->biWidth,                        bmphdr->biHeight, NULL, NULL, NULL, NULL);         // Преобразование прочитанного из файла изображения             iplConvertFromDIB(bmphdr, m_img);         // Обновление отображения             UpdateAllViews(NULL);         }         else         {             TRACE0("Ошибка при чтении файла BMP");         }     } }Изменение размеров изображения

Для изменения размеров изображения можно воспользоваться следующими функциями библиотеки IPL:

    void iplZoom(IplImage* srcImage, IplImage* dstImage, int xDst, int xSrc, int yDst, int ySrc, int interpolate); // растягивает или увеличивает изображение     void iplDecimate(IplImage* srcImage, IplImage* dstImage, int xDst, int xSrc, int yDst, int ySrc, int interpolate); // сжимает или уменьшает изображение     void iplResize(IplImage* srcImage, IplImage* dstImage, int xDst, int xSrc, int yDst, int ySrc, int interpolate); // изменяет размеры изображения     void iplDecimateBlur (IplImage* srcImage, IplImage* dstImage, int xDst, int xSrc, int yDst, int ySrc, int interpolate, int xMaskSize, int yMaskSize); // уменьшает и размывает изображение

Как видите, эти функции имеют почти одинаковые наборы аргументов, где:     IplImage* srcImage; // указатель на исходное изображение     IplImage* dstImage; // указатель на изображение, в котором функция должна сохранить результат

Положительные целочисленные аргументы xDst и xSrc определяют коэффициент масштабирования по x, который равен отношению xDst/xSrc, а yDst и ySrc – коэффициент масштабирования по y, равный отношению yDst/ySrc.

Для увеличения изображения в два раза в каждом направлении можно воспользоваться функцией iplZoom. Аргументы xDst, xSrc, yDst и ySrc при вызове этой функции должны иметь следующие значения: xDst=2, xSrc=1, yDst=2 и ySrc=1 (Рис. 1). В этой функции отношения xDst/xSrc и yDst/ySrc должны быть больше либо равны 1, иначе во время выполнения библиотека выдаст сообщение об ошибке (Рис. 2).

iplZoom

Рис. 1. Действие функции iplZoom с аргументами xDst=2, xSrc=1, yDst=2 и ySrc=1

Рис. 2. Сообщение об ошибке в функции iplZoom.

Для уменьшения изображения в два раза в каждом направлении можно воспользоваться функцией iplDecimate или функцией iplDecimateBlur. Аргументы xDst, xSrc, yDst и ySrc при вызове этой функции должны иметь следующие значения: xDst=1, xSrc=2, yDst=1 и ySrc=2 (Рис. 3). В этих функциях отношения xDst/xSrc и yDst/ySrc должны быть меньше либо равны 1, иначе во время выполнения библиотека выдаст сообщение об ошибке (Рис. 4).

iplDecimate

Рис. 3. Действие функции iplDecimate с аргументами xDst=1, xSrc=2, yDst=1 и ySrc=2

Рис. 4. Сообщение об ошибке в функции iplDecimate

Функция iplResize позволяет увеличивать изображение в одном направлении и уменьшать в другом. Например, для увеличения изображения в полтора раза в направлении x и одновременного уменьшения в полтора раза в направлении y аргументам: xDst, xSrc, yDst и ySrc необходимо присвоить следующие значения: xDst=3, xSrc=2, yDst=2 и ySrc=3 (Рис. 5). Таким образом, коэффициенты масштабирования по x и по y могут принимать любые значения, которые выражаются дробями xDst/xSrc и yDst/ySrc.

iplResize

Рис. 5. Действие функции iplResize с аргументами xDst=3, xSrc=2, yDst=2 и ySrc=3.

Последний целочисленный аргумент interpolate определяет метод интерполяции, который будет использоваться при вычислении неизвестных значений пикселов изображения в процессе изменения его размеров. Этот аргумент задаётся с помощью одной из трех констант:     IPL_INTER_NN; // определение неизвестных значений по ближайшему соседнему пикселу     IPL_INTER_LINEAR; // определение неизвестных значений с использованием линейной интерполяции     IPL_INTER_CUBIC; // определение неизвестных значений с использованием кубической интерполяции

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

iplZoom

IPL_INTER_NN

IPL_INTER_LINEAR

IPL_INTER_CUBIC

Рис. 6. Действие функции iplZoom в различных режимах интерполирования

Функция iplDecimateBlur требует задания двух дополнительных целочисленных аргументов xMaskSize и yMaskSize, которые определяют размер маски размытия. На рисунке 7 для сравнения представлены результаты уменьшения изображения выполненные с использованием функций iplDecimate и iplDecimateBlur. Изображение, полученное с помощью функции iplDecimateBlur, – визуально более "гладкое", но заметна тёмная рамка, которая является результатом процедуры фильтрации.

iplDecimate

iplDecimateBlur

Рис. 7. Действие функции iplDecimate с аргументами xDst=1, xSrc=2, yDst=1 и ySrc=2

При выполнении изменений размеров изображения можно воспользоваться макроопределениями iplZoomFit, iplDecimateFit и iplResizeFit, предоставляемыми библиотекой IPL. Макроопределения при нимяют всего три аргумента: указатель на исходное изображение srcImage, указатель на изображение dstImage, в котором будет сохранен результат преобразований, и целочисленный аргумент interpolate, определяющий метод интерполирования. Коэффициенты масштабирования в этих макроопределениях вычисляются исходя из размеров изображений srcImage и dstImage.

В прикладных программах обработки изображений результат преобразований часто необходимо поместить на место исходного изображения. Если в функции геометрических преобразований попытаться передать в качестве указателя на результат указатель на исходное изображение, то во время выполнения библиотека выдаст сообщение об ошибке (Рис. 8).

Рис. 8. Сообщение об ошибке в функции не выполняющей преобразование по месту

Для организации выполнения преобразований по месту необходимо выполнить следующие действия:

  1. Создать временное изображение требуемого размера.

  2. Выполнить преобразование исходного изображения и записать результат во временное изображение.

  3. Удалить исходное изображение.

  4. Скопировать временное изображение на место исходного.

  5. Удалить временное изображение.

Для примера эти действия реализованы в функции ZoomImage, которая предназначена для пропорционального увеличения или уменьшения изображения и принимает два аргумента: указатель на преобразуемое изображение img и коэффициент масштабирования scale. Для преобразования изображений в функции ZoomImage используются макроопределения iplZoomFit и iplDecimateFit.

void ZoomImage(IplImage* img, double scale)

{

// Создать временное изображения с измененным размером

IplImage* tmp = iplCreateImageHeader(3, 0, IPL_DEPTH_8U, "RGB", "BGR",

IPL_DATA_ORDER_PIXEL, IPL_ORIGIN_BL, IPL_ALIGN_DWORD,

img->width*scale, img->height*scale,

NULL, NULL, NULL, NULL);

// Занять память под временное изображение

iplAllocateImage(tmp, 0, 0);

if(scale > 1.)

{

// Выполнить увеличение изображения

iplZoomFit(img, tmp, IPL_INTER_LINEAR);

}

else

{

// Выполнить уменьшение изображения

iplDecimateFit(img, tmp, IPL_INTER_LINEAR);

}

// Освободить память исходного изображения

iplDeallocate(img, IPL_IMAGE_ALL);

// Скопировать результат в исходное изображение

img=iplCloneImage(tmp);

// Освободить память временного изображения

iplDeallocate(tmp, IPL_IMAGE_ALL);

}