Разработка программного обеспечения для фильтрации растровых изображений
Разработка программного обеспечения для фильтрации растровых изображений
РЕФЕРАТ Розробка програмного забезпечення для фільтрації растрових зображень. Дипломний проект з дисципліни «Технології програмування ГКС». Пояснювальна записка - Харків: ХНУРЕ. - 2009. - 62 с. Предмет дослiдження: растровi зображення. Мета проекту - систематизацiя, поглиблення i застосування знань по технологiям программування, отриманих на лекцiйних та лабораторних заняттях. Метод дослiдження - вивчення лiтератури, написання i тестування програми на комп'ютерi. Програма призначена для фільтрації растрових зображень. В роботi розглядаються питання розробки програмного забезпечення для фільтрації растрових зображень. ПЗ виконує корекцію кольорів відкритих растрових зображень за допомогою фiльтрiв. Графiчнi фiльтри реалiзованi на основi точечних i просторових (матричних) перетворень. Програмне забезпечення реалізовано у системі програмування Microsoft Visual С++ 6.0. Ключові слова: Растрове зображення, DDB, DIB, BMP, матриці перетворень, MDI-інтерфейс, графічні фільтри The abstract Development of the program maintenance for filtration of raster's images. The course project on discipline “The Technology of Programming the FKS”. An explanatory note - Kharkiv:KNURE.-2009.- 62 р. Object of research: raster images. The purpose of the course project - the ordering, deepening and application of the knowledge on technologies of the programming, received on lectures and laboratory researches. The method of research - studying the literature, writing and testing the program on the computer. The program is assigned for filtration of raster's images. In this work, the questions of development of a program maintenance for filtration of raster's images are considered. The PM realizes the correction of a color of opened raster images, with the help of a filters. Graphic filters are realized on the basis of a dot and spatial (matrix) transformations. The program maintenance is realized in the system of programming Microsoft Visual C ++ 6.0. Key words: The raster image, DDB, DIB, BMP, matrixes of transformations, the MDI-interface, graphic filters. ВВЕДЕНИЕ Компьютерная графика объединяет довольно широкий круг операций по обработке графической информации с помощью компьютера. Компьютерной графике отводится огромная роль в современном мире. В компьютерной графике можно выделить несколько основных направлений: - Визуализация научных (расчетных или экспериментальных) данных. Большинство современных математических программных пакетов (например, Мар1е, Маtlab, МаthCAD) имеют средства для отображения графиков, поверхностей и трехмерных тел, построенных на основе каких-либо расчетов. Кроме того, графическая информация может активно использоваться в самом процессе вычислений. - Геометрическое проектирование и моделирование. Это направление компьютерной графики связано с решением задач начертательной геометрии - построением чертежей, эскизов, объемных изображений с помощью программных систем, получивших название САD-системы, например АutoCAD. Существует большое количество специализированных САD-систем в машиностроении, архитектуре и т. д. - Распознавание образов. Способность распознавать абстрактные образы считают одним из важнейших факторов, определившим развитие мыслительных способностей человека. Задача распознавания и классификации графической информации является одной из ключевых и при создании искусственного интеллекта. Уже в наши дни компьютеры распознают образы повсеместно (анализ аэро- и космических фотоснимков; системы сортировки, наведения и т. д.). Возможно, самый известный пример распознавания образов - сканирование и перевод "фотографии" текста в набор отдельных символов, формирующих слова. Такую операцию позволяет выполнить программное обеспечение многих современных сканеров. - Изобразительное искусство. К этому направлению можно отнести разнообразную графическую рекламу: от текстовых транспарантов и фирменных знаков до компьютерных видеофильмов, обработку фотографий, создание рисунков, мультипликацию и т. д. В качестве примера популярных и довольно востребованных программ из этой области компьютерной графики можно назвать АdobePhotoshop (обработка растровых изображений), СогеlDraw (создание векторной графики), 3DS Мах (трехмерное моделирование). - Виртуальная реальность. Реальность, даже виртуальная, подразумевает воздействия на всю совокупность органов чувств человека, в первую очередь на его зрение. К компьютерной графике можно отнести задачи моделирования внешнего мира в различных приложениях: от компьютерных игр до тренажеров. - Цифровое видео. Все более широкое распространение получают аними-рованные изображения, записанные в цифровом формате. Это фильмы, передаваемые через компьютерные сети, цифровое, кабельное и спутниковое телевидение. 1. АНАЛИЗ ТЕХНИЧЕСКОГО ЗАДАНИЯВ данной работе разрабатывается ПО для фильтрации растровых изображений. Графический формат BMP является аппаратно-независимым (devise-independent bitmap DIB) и наиболее удобным для данного вопроса. Для разработки программ работы с битовыми изображениями необходимо разработать свой собственный класс, поддерживающий файлы *.bmp. Кроме открытия растровых изображений программа должна выполнять цветокоррекцию открытых изображений. Цветокоррекция выполняется при помощи графических фильтров.Для реализации поставленной задачи в проекте целесообразно создать MDI приложение (с многодокументным интерфейсом). В разрабатываемом ПО реализуется возможность масштабирования открытого файла.Для обработки изображений при помощи фильтров необходимо предусмотреть, как они будут уживаться между собой и взаимодействовать с остальными модулями программы. Фильтры можно реализовать в виде классов, производных от какого-то одного базового класса. В базовом классе следует определить набор методов, общих для всех фильтров. Процесс преобразования изображения выносится в отдельный поток выполнения программы. Это дает возможность контролировать не только область применения фильтра, но и продолжительность выполнения операции, т.е. возможность остановить выполнение преобразования.2. ОПИСАНИЕ ГРАФИЧЕСКОГО ФОРМАТА2.1 Общая информация о графическом форматеГрафический формат - порядок (структура), согласно которому данные, описывающие изображение, записаны в файле.Графические данные обычно разделяются на два класса: векторные и растровые. Векторные данные используются для представления прямых, многоугольников, кривых и т. п., с помощью определенных в числовом виде базовых точек. Растровые данные представляют собой набор числовых значений, определяющих яркость и цвет отдельных пикселов. Пикселами (или пикселями - от английского pixel) называются минимальные элементы (цветные точки), из которых формируется растровое изображение. Далее под растром будем понимать массив пикселов (массив числовых значений). Для обозначения массива пикселов часто используется термин bitmap (битовая карта). В bitmap каждому пикселу отводится определённое число битов (одинаковое для всех пикселов изображения). Это число называется битовой глубиной пиксела или цветовой глубиной изображения, т.к. от количества битов, отводимых на один пиксел, зависит количество цветов изображения. Наиболее часто используется цветовая глубина 1, 2, 4, 8, 15, 16, 24 и 32 бита.Растровый формат используется для хранения растровых данных. Файлы такого типа особенно хорошо подходят для хранения изображений реального мира, например оцифрованных фотографий. Растровые файлы содержат битовую карту изображения и ее спецификацию. Наиболее распространенные растровые форматы: ВМР, ТIFF, GIF, РСХ, JРЕС.В данном проекте будет использоваться ВМР формат.2.2 Общее описание формата ВМРMicrosoft Windows Bitmap (ВМР) - собственный растровый формат операционной системы Windows. Формат основан на внутренних структурах представления растровых данных Windows. Формат совершенствовался и развивался по мере появления новых версий Windows. Первоначально был очень простым, содержал лишь растровые данные и не поддерживал сжатие. Растровые данные представляли собой индексы в цветовой палитре, которая была фиксированной и определялась графической платой. Поэтому этот формат называют аппаратно-зависимым (Device Dependent Bitmap, DDB), он был ориентирован на графические платы для IВМ РС (СGА,EGA, НERCULES) и другие.Развитием формата ВМР стало введение в него поддержки изменяемой цветовой палитры. Это позволило хранить информацию о цветах вместе с растровыми данными. Такое изменение формата позволило сделать хранимые изображения аппаратно-независимыми (Devise Independent Bitmap, DIB). Иногда аббревиатуру DIВ используют как синоним ВМР. 2.3 Структура файла ВМРФайлы ОБВ исходного формата ВМР содержали два раздела: заголовок файла и растровые данные (рис. 2.3.1).|
Заголовок файла | | Растровые данные | | |
Рисунок 2.3.1 - Структура файла DDВ исходного формата ВМР Файлы более поздних версий содержат четыре раздела: заголовок файла, информационный заголовок растра, палитру цветов и растровые данные (рис. 2.3.2). |
Заголовок файла | | Заголовок растра | | Палитра цветов | | Растровые данные | | |
Рисунок 2.3.2 - Структура ВМР-файла Рассмотрим в деталях структуру данных файла формата ВМР версии 3.x, появившегося с операционной системой Microsoft Windows 3.x. Этот формат поддерживается большинством существующих в настоящее время приложений. Все версии формата ВМР начинаются с 14-байтового заголовка-структуры. Листинг 2.3.1 - Структура данных файла формата ВМР версии 3.x BITMAPFILEHEADER : Typedef struct tagBITMAPHEADER { WORD bfType; //тип файла, должен быть 4d42h («ВМ») DWORD bfSise //размер файла в байтах WORD bfReserved1; //зарезервировано, должен быть 0 WORD bfReserved2; // зарезервировано, должен быть 0 DWORD bfOffBits; //смещение в байтах до начала растровых данных } BITMAPFILEHEADER; // За заголовком файла следует заголовок растра. Его длина составляет 40 байтов.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//За заголовком растра может следовать палитра цветов, состоящая из //последовательности 4-байтовых структур RGBQUARDTypedef struct _RGBQUARD{BYTE rgbBlue; //синяя составляющаяBYTE rgbGreen; // зелёная составляющаяBYTE rgbRed; //красная составляющаяBYTE rgReserved; //заполнитель(всегда 0)}RGBQUARD;//Структура BITMAPINFOHEADER и структуры RGBQUARD собираются в структуре BITMAPINFO: Typedef struct tagBITMAPINFO {BITMAPINFOHEADER bmiHeader; RGBQUARD bmiColors[1]; } BITMAPINFO; После структуры BITMAPINFO на расстоянии bfOffBits (поле структуры BITMAPFILEHEADER) от начала файла начинаются растровые данные. Растровые данные представляют собой индексы в палитре цветов (в случае если bibitCount равно 1, 4, 8) или реальные значения цветов пикселов (в случае если bibitCount равно 24). Если bibitCount равно 24, то каждый пиксел представляется тремя байтами: первый байт - интенсивность синего цвета, затем по байту на зеленый и красный цвет. Этот формат цвета называется RGB888 или RGB24. Растровые данные, соответствующие одной строке пикселов изображения, вне зависимости от формата цвета должны быть выровнены на границу двойного слова DWORD, т. е. каждая строка пикселов должна описываться целым числом двойных слов. Например, строка из 5 пикселов по 24 бита (3 байта) на пиксел может быть описана 15 байтами, но длина строки растровых данных в формате ВМР должна быть 16 байтов. Последний байт будет служить лишь для целей выравнивания. Формат ВМР версии 3.x имеет разновидность (для Windows NT), предназначенную для хранения растровых данных с пиксельной глубиной 16 и 32 битов. Этот формат имеет точно такую же структуру заголовка растра BITMAPINFOHEADER. Его длина составляет 40 байтов. Отличие заключается в том, что поле bibitCount может принимать значения 16 и 32. При пиксельной глубине 16 битов для хранения цвета пиксела отводится два байта (слово - тип WORD), каждому компоненту цвета пиксела отводится по 5 битов (формат цвета RGВ555). Младшие 5 битов задают интенсивность синего цвета, затем по 5 битов на зеленый и красный цвет, старший бит в слове не используется. При пиксельной глубине 32 бита для хранения цвета пиксела отводится 4 байта (двойное слово - тип DWORD). При этом на каждый компонент цвета отводится по 8 бит, так же как и при 24-битной глубине, а старший байт в DWORD не используется (формат цвета RGВ888). Дополнительные возможности этой разновидности формата проявляются, если указать значение поля biCompression, равное 3. В этом случае вслед за структурой BITMAPINFOHEADER (на месте палитры цвета) следуют три поля DWORD: RedMask, GreenMask, BlueMask, которые задают битовые маски для компонентов цвета пиксела. Биты в этих масках обязательно должны быть смежными и не содержать перекрывающихся полей. Для 16-битовых растровых данных часто применяют формат RGВ565, который задается следующей маской. RedMask = 0хF8000000; // 1111 1000 0000 0000 0000 0000 0000 0000 GгееnМаsk = 0х07Е00000; // 0000 0111 1110 0000 0000 0000 0000 0000 В1uеМаsk = 0х001F0000; // 0000 0000 0001 1111 0000 0000 0000 0000 С помощью этой маски из значения WORD, задающего цвет пиксела, извлекается значение каждого цветового компонента. В формате RGВ565 красному и синему цветам отводится по 5 битов, а зеленому - 6 битов. Такое неравноправие обосновывают тем, что человеческий глаз более восприимчив к зеленому цвету, поэтому тщательная запись его градаций позволяет повысить качество изображения. Для 32-битовых растровых данных используют формат RGB101010, определяющий по 10 битов на каждый цвет, который задается следующей маской. RedMask = 0ХFFC00000; // 1111 1111 1100 0000 0000 0000 0000 0000 GгееnМаsk = 0х00ЗFF000; // 0000 0000 0011 1111 1111 0000 0000 0000 В1uеМаsk = 0х00000FFС; // 0000 0000 0000 0000 0000 1111 1111 1100 По сравнению с форматом RGВ888 такое представление позволяет описать большее количество цветов. 2.4 Преобразования графических файлов Рассмотрим два вида преобразований: - точечные - новое значение элемента изображения (пиксела) рассчитывается только на основе его старого значения; - пространственные (матричные) - при расчете нового значения пиксела учитывается не только его старое значение, но также значения некоторой области пикселов вокруг него. Точечные преобразования удобно выполнять с помощью таблиц преобразования. Таблица преобразования - это просто массив, заполненный какими-то значениями. Размер массива равен максимальному значению, которое может принимать преобразуемая величина. С помощью такой таблицы удобно и быстро заменять одно значение другим. Таблицы преобразований применимы, когда новое значение формируется из единственного старого значения, т. е. при точечном преобразовании. Рассмотрим, например, как можно инвертировать цвет картинки. Диапазон значений каждого 8-битного компонента цвета находится в пределах от 0 до 255. Создадим таблицу преобразования из 256 элементов и заполним ее значениями от 255 до 0 (рис. 2.4.1). |
индекс | 0 | 1 | 2 | 3 | … | 253 | 254 | 255 | | значение | 255 | 254 | 253 | 252 | … | 2 | 1 | 0 | | |
Рисунок 2.4.1 - Таблица преобразования "инверсия" После преобразования по формуле (V'= 255 - V) с использованием таблицы интенсивность 255 будет заменена на 0, 254 - на 1 и т. д. В случае такого простого преобразования, как инверсия цвета, использование таблицы может и не дать особого выигрыша по скорости, но если новое значение пиксела должно рассчитываться по более сложной формуле, чем в нашем случае, то выигрыш будет весьма заметен. Кроме того, использование таблиц позволяет использовать единообразный подход к осуществлению различных преобразований. Пространственное преобразование заключается в нахождении свертки значений группы пикселов. Свертка вычисляется как сумма пиксельных значений, попавших в зону преобразования, помноженных на весовые коэффициенты. В качестве весовых коэффициентов выступают элементы матрицы преобразования. Значения элементов матрицы преобразования и определяют тип преобразования. Размер матрицы преобразования соответствует области пикселов, которые будут участвовать в преобразовании. Центральный элемент матрицы - весовой коэффициент преобразуемого пиксела (х, у). Поэтому матрицы преобразования, обычно имеют нечетный размер (например, 3x3 или 5x5 элементов). Например, свертка с помощью единичной матрицы соответствует преобразованию "размытие" (понижение четкости) изображения. Достигается такой эффект за счет усреднения значений группы пикселов, охваченной матрицей преобразования. Если значение пиксела (х, у) было выше среднего, оно уменьшится, если было ниже среднего, то увеличится. Однако это не означает, что все изображение станет монотонным, так как матрица преобразования движется по изображению вместе с координатами (х, у), средний уровень тоже изменяется. 3. ПРОСМОТР И РЕДАКТИРОВАНИЕ РАСТРОВЫХ ИЗОБРАЖЕНИЙ 3.1 Создание многодокументного приложения С помощью генератора приложений АррWizard создаём каркас приложения для просмотра и редактирования рисунков. - С помощью команды File/New/Projects/MFC AppWizard (exe) начнем создание приложения. Назовем проект ВМViewer. - На первом шаге выберем тип приложения Multiple documents. - Далее можно принять все установки по умолчанию. 3.2 Класс CRaster для работы с растровыми изображениями Создаем в программе класс CRaster, отвечающий за загрузку и осуществляющий поддержку операций по обработке растрового изображения. Класс CRaster содержит два метода DrawBitmap, выполняющих вывод изображения на контекст устройства. Аргументы одного из методов позволяют задать положение и размеры выводимой области исходного изображения и определить область назначения. По умолчанию изображение выводится полностью в масштабе 1:1, однако с помощью аргументов этой функции можно и изменить масштаб. Второй метод позволяет просто указать позицию начала вывода и масштаб, в котором должно быть нарисовано изображение. Оба метода внутри используют мощную АРI-функцию StretchDIBits(). Режим масштабирования выбирается CDC-методом SetStretchBltMode(): int SetStretchBltMode(int nStretchMode); Аргумент функции - nStretchMode - режим масштабирования. Поддерживаются следующие режимы масштабирования: - BLACKONWHITE - выполняет булеву операцию AND между цветом существующих и удаленных пикселов (при уменьшении размера изображения). Этот режим используется, если масштабируется рисунок "черным по белому", т. е. алгоритм масштабирования будет стараться сохранить черные пикселы; - COLORNCOLOR - этот режим удаляет (добавляет) строки (столбцы) пикселов без каких-либо попыток сохранить содержащуюся в них информацию. Наиболее быстрый режим. Используется, когда необходимо сохранить цвета изображения неизменными; - WHITEONBLACK - выполняет булеву операцию OR. Этот режим используется, если масштабируется рисунок "белым по черному"; - HALFTONE - преобразует изображение к заданному размеру и при этом трансформирует цвета так, чтобы средний цвет полученной картинки приближался к исходному цвету. Наиболее медленный режим. Однако масштабированная картинка выглядит лучше за счет сглаживания "лестничного эффекта". Этот режим не работает в Windows 95/98, и, похоже, заменяется режимом COLORNCOLOR. При масштабировании фотографий и цветных рисунков в большинстве случаев наиболее подходящим является режимы COLORNCOLOR и HALFTONE . Чтобы можно было изменять масштаб вывода изображений, добавим в меню View программы команды Zoom In, Zoom Out, а для установки режима масштабирования - команды Stretch HALFTONE, Stretch COLORNCOLOR. Обработчики этих команд добавим в класс облика (листинг 3.2.1). Эти функции изменяют состояние переменных m_dScale и m_nStretchModeстановки режимов масштабирования также добавлены методы OnUpdateViewStretchhalftone() и OnUpdateViewStretchcoloroncolor() для обработки сообщения UPDATE_COMMAND_UI. В этих функциях можно управлять состоянием соответствующих команд в интерфейсе программы (например, можно делать недоступными команды в зависимости от состояния программы). В данном случае мы просто маркируем соответствующий режим масштабирования. Листинг 3.2.1 - Обработка команд масштабирования. Файл BMView.cpp void CBMView::OnViewZoomin() {// TODO: Add your command handler code here m_dScale*=2; OnUpdate(NULL, 0, NULL); } void CBMView::OnViewZoomout() {// TODO: Add your command handler code here m_dScale/=2; OnUpdate(NULL, 0, NULL); } void CBMView::OnViewStretchhalftone() {// TODO: Add your command handler code here m_nStretchMode=HALFTONE; OnUpdate(NULL, 0, NULL); } void CBMView::OnUpdateViewStretchhalftone(CCmdUI* pCmdUI) {// TODO: Add your command update UI handler code here pCmdUI->SetCheck(m_nStretchMode==HALFTONE); } void CBMView::OnViewStretchcoloroncolor() {// TODO: Add your command handler code here m_nStretchMode=COLORONCOLOR; OnUpdate(NULL, 0, NULL); } void CBMView::OnUpdateViewStretchcoloroncolor(CCmdUI* pCmdUI) {// TODO: Add your command update UI handler code here pCmdUI->SetCheck(m_nStretchMode==COLORONCOLOR);} 3.3 Модификация класса документа для обеспечения работы с изображениями Поскольку данными в нашей программе будут изображения, модифицируем класс документа так, чтобы он умел работать с изображениями. В проект приложения добавим файлы Raster.h, Raster.cpp. В классе документа надо определить данные как объекты класса СRaster. В принципе, для целей показа картинки на экране хватит и одного объекта СRaster. Чтобы наделить программу некоторыми возможностями по редактированию изображений потребуется не один, а, как минимум, два объекта: один для хранения исходной картинки, второй - буфер для приема преобразованной картинки. Порядок работы с двумя объектами СRaster в этом случае будет выглядеть следующим образом: - Загружаем изображение в первый объект СRaster и показываем его наэкране до тех пор, пока пользователь не даст команду выполнить какие-нибудь изменения изображения. - Помещаем измененное изображение во второй объект СRaster и начинаем показывать второй объект-картинку. - Может случиться так, что пользователю не понравится то, как мы изменили его картинку, тогда он отдает команду "Отменить преобразования".При этом меняются объекты местами. 3.4 Программная схема выполнения преобразований. Графические фильтры Поскольку следует реализовать целый ряд процедур преобразования изображений, необходимо предусмотреть, как они будут уживаться между собой и взаимодействовать с остальными модулями программы. В мультимедийном программировании широко распространена концепция фильтров. Фильтр - это некоторая программа, которая, пропуская через себя данные, преобразует их некоторым образом. В нашем случае данными являются значения цветов пикселов изображения. Представим, что у нас имеется набор фильтров, пропуская через которые данные изображения мы можем добиваться различных эффектов (рис. 3.6.1). Для того чтобы получить нужный эффект, достаточно просто указать программе, какой фильтр считать активным. В программе должна существовать "фильтрация" - процедура, в которой будет выполняться само пропускание данных через фильтр. Фильтры можно реализовать в виде классов, производных от какого-то одного базового класса. В базовом классе следует определить набор методов, общих для всех фильтров. В программе заведем переменную - указатель на активный фильтр. Используя этот указатель, "фильтрация" будет обращаться к нужному фильтру. Рисунок 3.6.1 - Схема использования фильтров для преобразования изображений Саму фильтрацию изображения можно выполнять по-разному. Например, можно передать фильтру всю исходную картинку и ожидать от него уже полностью преобразованного изображения. А можно пропускать через фильтр исходное изображение по одному пикселу. В последнем случае не придется дублировать цикл обработки всего изображения в каждом фильтре, и вызывающая фильтр процедура получит полный контроль над областью изображения, к которой будет применено преобразование. Реализуем на практике второй способ организации "фильтрации". При этом сам процесс преобразования изображения вынесем в отдельный поток (назовем его "рабочим" потоком) выполнения программы. Это даст нам возможность контролировать не только область применения фильтра, но и продолжительность выполнения операции, т.е. возможность остановить выполнение "фильтрации". Общая схема преобразования в этом случае будет выглядеть следующим образом: Пришла команда выполнить преобразование - создаем рабочий поток. Уведомляем объекты-облики о том, что начали преобразование. Приэтом облик запускает таймер и начинает периодически интересоваться,сколько процентов работы выполнено, показывая пользователю процентвыполнения. В рабочем потоке выполняется преобразование и увеличивается процент выполнения. По окончании преобразования (или если пользователь прервал выполнение) в объекты-облики посылаются сообщения о завершении работы ипоказывается преобразованная картинка. Поскольку данными в программе ВМViewer заведует класс CBMDoc, именно в него и поместим "фильтрацию". Для создания рабочего потока потребуется добавить в класс CBMDoc несколько методов: Transform() - создает рабочий поток; ThreadProc () - функция потока, запускает "фильтрацию" для конкретного объекта-документа; TransformLoop() - сама "фильтрация"; InformAllViews() - передает сообщения всем обликам документа; Рассмотрим метод TransformLoop() (Листинг 3.6.1). Листинг 3.6.1 - Метод CBMDoc::TransformLoop(). Файл BMDoc.cpp void CBMDoc::TransformLoop() {if(m_pCurFilter==NULL) return; if(!CreateCompatibleBuffer()) return; m_EventDoTransform.SetEvent(); m_bEditable=FALSE; InformAllViews(UM_STARTTRANSFORM); CRaster*pSBM=GetCurrentBMPtr(),//источник *pDBM=GetBufferBMPtr();// приёмник // Установили в фильтр источник и приёмник преобразований m_pCurFilter->SetBuffers(pSBM, pDBM); for(LONG y=0; y<pSBM->GetBMHeight(); y++) {// Процент выполнения InterlockedExchange(&m_lExecutedPercent, 100*y/pSBM->GetBMHeight()); //Проверка не решили ли прервать преобразование if(!m_EventDoTransform.Lock(0)) {InformAllViews(UM_ENDOFTRANSFORM, FALSE, 0); m_bEditable=TRUE; return; } LONG x=0; // Преобразование с использованием текущего фильтра for(; x<pSBM->GetBMWidth(); x++) m_pCurFilter->TransformPix(x, y); } m_EventDoTransform.ResetEvent(); m_bEditable=TRUE; SwapBM();//Сделать буфер текущим изображением SetModifiedFlag(); //флаг “данные изменились” InformAllViews(UM_ENDOFTRANSFORM, TRUE, 0); return; }; В методе TransformLoop() мы сначала "зажигаем" событие "Выполняется преобразование" - объект m_EventDoTransform класса CEvent. Затем сообщаем текущему фильтру, какое изображение будет исходным, и какое - приемным (адреса объектов CRaster). Далее в цикле прогоняем через фильтр пикселы изображения. На текущий фильтр указывает переменная m_pCurFilter, которую мы завели в классе CBMDoc специально для этих целей. Тип этой переменной - указатель на объект класса CFilter. Преобразование же данных выполняется с помощью метода Cfilter::TransformPix(), Класс СFilter как раз и является базовым для всех фильтров. В процессе преобразования перед обработкой очередной строки пикселов вычисляется процент выполнения как процент уже обработанных строк изображения. Вычисленное значение записывается в переменную m_lExecutedPercent с помощью API-функции InterlockedExchange() - эта функция позволяет предотвратить одновременное обращение к переменной из разных потоков. Далее проверяется, по-прежнему ли установлено событие m_EventDoTransform. И только затем обрабатываются пикселы строки. Причем в нашей программе в иллюстрационных целях мы позволяем пользователю посмотреть эффект преобразования на половине изображения. Если установлен флаг m_bEditHalf, первая половина строки копируется в неизменном виде. После того как все пикселы изображения были обработаны, скидывается флаг m_EventDoTransform, буферное изображение становится активным и во все облики направляется сообщение UM_ENDOFTRANSFORM с параметром TRUE, который говорит о том, что преобразование завершилось и надо обновить изображение в окне облика. Для контроля количества выполненной работы фильтра в класс CBMView с помощью ClassWizard добавим метод OnTimer(). В этом методе будет выполняться запрос процента выполнения операции и обновляться информация о выполнении. Процент выполнения операции отображается в заголовке окна облика. Приход сообщения UM_ENDOFTRANSFORM обрабатывается методом OnEndTransform(), который зависит от значения аргумента wParam: - TRUE - преобразование успешно закончено - выполняет обновление экрана; - FALSE - пользователь прервал операцию - не выполняет обновление экрана. Далее им вызывается функция OnStopTimer(), которая разрушает таймер. Выделение операций обработки данных, которые могут выполняться длительный отрезок времени, в отдельный поток позволяет пользователю сохранить контроль над выполнением программы. В нашем приложении пользователь, запустив фильтрацию на одном из открытых изображений, может переключиться на просмотр и редактирование другого изображения. При необходимости пользователь может остановить выполнение преобразования, для этого в программе предусмотрим команду, которая бы сбрасывала флаг m_EventDoTransform. При сбросе этого флага цикл выполнения преобразования СВМDос::ТгаnsformLoop() прерывается, потоковая функция завершается и рабочий поток прекращает свое существование. 3.5 Класс “Фильтр” Выполнение задачи подразумевает существование в программе некоторого объекта-фильтра. Фильтры выполняют разные преобразования, но с точки зрения "фильтрации" они все одинаковы и обращаться с ними она будет единообразно. Поэтому нам надо определить базовый класс CFilter для фильтра с минимальным, но основным набором методов, с помощью которых будет происходить общение. Данные класса - два указателя на объекты-картинки класса Craster: - m_рSourseBM - адрес объекта "исходная картинка", откуда берутся данные для преобразования; - m_рDestBM - адрес объекта "приемная картинка", куда помещаются преобразованные данные. Методы класса: - SetBuffers () - сообщает фильтру адреса исходного и приемного изображения; - TransformPix() - преобразует данные одного пиксела с координатами (x,y). Переменная-указатель на этот класс m_pCurFilter заведена в классе CBMDoc. Этой переменной присваивается адрес текущего фильтра. Для реализации точечных методов преобразования создаём класс CdotFilter (Листинг 3.7.1). Листинг 3.7.1 - Базовый класс для точечных фильтров CdotFilter. Файл Filter.h //Базовый класс для точечных фильтров class CDotFilter: public CFilter { protected: //Таблицы преобразования для компонентов цвета BYTE BGRTransTable[3][256]; public: //Метод преобразования пиксела BOOL TransformPix(LONG x, LONG y);}; Данными этого класса являются три таблицы преобразования компонентов RGB цвета. Для точечного фильтра переопределён метод . Реализация метода приведена в листинге 3.7.2 Листинг 3.7.2 - Метод CDotFilter:: TransformPix (). Файл Filter.cpp BOOL CDotFilter::TransformPix(LONG x, LONG y) (pSPix=m_pSourceBM->GetPixPtr(x, y))==NULL) return FALSE; // Преобразование. Порядок BGR *pDPix=BGRTransTable[0][*pSPix]; *(pDPix+1)=BGRTransTable[1][*(pSPix+1)]; *(pDPix+2)=BGRTransTable[2][*(pSPix+2)]; return TRUE; ; Хотя формат 24-битового цвета называют RGB, в файле формата BMP компоненты цвета хранятся в обратном порядке (Порядок BGR). В производных от CDotFilter классах останется реализовать инициализацию таблиц преобразования. Для реализации пространственных (матричных) методов преобразования создаём класс CMatrixFilter. Интерфейс класса приведён в листинге 3.7.3 Листинг 3.7.3 - Интерфейс базового для матричных фильтров класса CmatrixFilter. Файл Filter.h // Пространственные (матричные фильтры) // Базовый класс class CMatrixFilter: public CFilter { protected: int m_rangX; // размер матрицы по X и Y int m_rangY; const int *m_pMatrix; // указатель на матрицу public: //Методпреобразования пиксела BOOL TransformPix(LONG x, LONG y); }; Данными этого класса являются размер матрицы преобразования и указатель на матрицу. Размер мртрицы определяет зону пикселов, окружающих пиксел (x,y), которая будет вовлечена в расчёт нового значения пиксела (x,y). Указателю на матрицу преобразования m_pMatrix будет присваиваться адрес матрицы, которая будет использована в преобразовании. Реализация метода CmatrixFilter:: TransformPix() приведена в листинге3.7.4 Листинг 3.7.4 - Метод CmatrixFilter:: TransformPix(). Файл Filter.cpp // Пространственные фильтры BOOL CMatrixFilter::TransformPix(LONG x, LONG y) {BYTE *pDPix=NULL, *pSPix=NULL; // Источник и приёмник необходимы if(m_pSourceBM==NULL || m_pDestBM==NULL) return FALSE; // Определяем зону перекрытия изображения и матрицы. Это требуется для //обработки пикселов, находящихся на границах изображения int x_start=0; int dx=m_rangX/2, dy=m_rangY/2; if(x-dx<0) x_start=dx-x; int y_start=0; if(y-dy<0) y_start=dy-y; int x_finish=m_rangX; if(x+dx>m_pSourceBM->GetBMWidth()) x_finish-=(x+dx-m_pSourceBM->GetBMWidth()); int y_finish=m_rangY; if(y+dy>m_pSourceBM->GetBMHeight()) y_finish-=(y+dy-m_pSourceBM->GetBMHeight()); // Расчёт новых значений цвета пиксела с учётом соседей, попавших в зону //действия матрицы преобразования int NewBGR[3]; int count=0; for(int c=0, mx=0, my=0; c<3; c++) {NewBGR[c]=0; count=0;
Страницы: 1, 2
|