p align="left">Реализуем на практике второй способ организации "фильтрации". При этом сам процесс преобразования изображения вынесем в отдельный поток (назовем его "рабочим" потоком) выполнения программы. Это даст нам возможность контролировать не только область применения фильтра, но и продолжительность выполнения операции, т.е. возможность остановить выполнение "фильтрации". Общая схема преобразования в этом случае будет выглядеть следующим образом: Пришла команда выполнить преобразование - создаем рабочий поток. Уведомляем объекты-облики о том, что начали преобразование. Приэтом облик запускает таймер и начинает периодически интересоваться,сколько процентов работы выполнено, показывая пользователю процентвыполнения. В рабочем потоке выполняется преобразование и увеличивается процент выполнения. По окончании преобразования (или если пользователь прервал выполнение) в объекты-облики посылаются сообщения о завершении работы ипоказывается преобразованная картинка. Поскольку данными в программе ВМ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; for(my=y_start; my<y_finish; my++) for(mx=x_start; mx<x_finish; mx++) {if((pSPix=m_pSourceBM->GetPixPtr(x+(mx-dx), y+(my-dy)))!=NULL) {NewBGR[c]+=(m_pMatrix[my*m_rangX+mx]*(*(pSPix+c))); count+=m_pMatrix[my*m_rangX+mx]; }}} // Адрес пиксела в изображении-приёмнике pDPix=m_pDestBM->GetPixPtr(x, y); //Установка нового значения в приёмное изображение for(c=0; c<3; c++) { // Приведение значения к допустимому диапазону if(count!=0) NewBGR[c]=NewBGR[c]/count; if(NewBGR[c]<0) NewBGR[c]=0; else if(NewBGR[c]>255) NewBGR[c]=255; *(pDPix+c)=NewBGR[c]; } return TRUE; }; В методе CmatrixFilter:: TransformPix() сначала определяется область перекрытия изображения и матрицы преобразования. Этот шаг необходим в связи с тем, что на границах изображения пиксел может не иметь соседей. Новое значение пиксела формируется с учетом значений всех пикселов и коэффициентов матрицы преобразования, попавших в область перекрытия изображения и матрицы преобразования. 3.6 Фильтр “Яркость/Контраст” Изменение яркости заключается в изменении интенсивности цвета всех пикселов на заданное значение. Данное преобразование является точечным. Для его реализации добавим в программу класс CBrightCont, производный от класса CDotFilter. Интерфейс класса приведён в листинге 3.6.1 Листинг 3.6.1 - Интерфейс класса CBrightCont. Файл Filter.h // Яркость/контраст class CBrightCont: public CDotFilter { public: BOOL Init(int b_offset, int c_offset); }; Переменные b_offset, c_offset - это объекты, связанные с ползунками, могут принимать положительные и отрицательные значения, что соответствует увеличению или уменьшению яркости/контрастности изображения. Реализация метода CBrightCont::Init() приведена в листинге 3.6.2 Этот метод инициализирует таблицы преобразования. Сначала выполняется смещение яркости на заданную величину, а затем либо "сжатие", либо "растяжение" диапазона яркости. Причем при сжатии значения яркости изменяются не равномерно, а пропорционально их удаленности от "серой середины", определенной константой CONTRAST_MEDIAN. После преобразования яркости работа по коррекции контрастности происходит со значениями таблицы преобразования, полагая при этом, что они являются индексами в таблице, полученной после коррекции яркости.
Страницы: 1, 2, 3, 4, 5, 6
|