- •Литература
- •Введение
- •Введение в Image Processing Library
- •Основные понятия теории сигналов
- •Переход от непрерывных сигналов и преобразований к дискретным
- •Преобразование Фурье
- •Быстрое преобразование Фурье
- •Ввод изображений
- •Кодирование цветных изображений
- •Глубина и квантование цвета
- •Сжатие изображений
- •Форматы графических файлов
- •Работа с файлами в формате bmp
- •Зеркальные отражения изображений
- •Повороты изображений
- •Препарирование изображений
- •Бинаризация
- •Яркостный срез
- •Линейное контрастирование
- •Пилообразное контрастирование
- •Соляризация
- •Эквализация
- •Выполнение логических и арифметических операций над изображенями
- •Выполнение логических операций
- •Выполнение арифметических операций
- •Общие понятия фильтрации изображений
- •Масочная фильтрация
- •Генерация шума
- •Восстановления изображений на основе обратной фильтрации
- •Фильтрация Винера
- •Итерационные методы восстановления изображений
- •Алгебраические методы восстановления изображений
Работа с файлами в формате 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. Сообщение об ошибке в функции не выполняющей преобразование по месту
Для организации выполнения преобразований по месту необходимо выполнить следующие действия:
Создать временное изображение требуемого размера.
Выполнить преобразование исходного изображения и записать результат во временное изображение.
Удалить исходное изображение.
Скопировать временное изображение на место исходного.
Удалить временное изображение.
Для примера эти действия реализованы в функции 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);
}