48629 (588583), страница 3
Текст из файла (страница 3)
{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; yGetBMHeight(); 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(; xGetBMWidth(); 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)
{BYTE *pDPix=NULL, *pSPix=NULL;
// Источник необходим
if(m_pSourceBM==NULL)
return FALSE;
//Если приёмник не задан, то преобразование помещаем в источник
if(m_pDestBM==NULL)
m_pDestBM=m_pSourceBM;
// Получаем указатели на пикселы в источнике и приёмнике
if((pDPix=m_pDestBM->GetPixPtr(x, y))==NULL ||
(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 for(mx=x_start; 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. После преобразования яркости работа по коррекции контрастности происходит со значениями таблицы преобразования, полагая при этом, что они являются индексами в таблице, полученной после коррекции яркости. Листинг 3.6.2 – Метод CBrightCont::Init().Файл Filter.cpp // "Серая середина" –уровень 159 #define CONTRAST_MEDIAN 159 BOOL CBrightCont::Init(int b_offset, int c_offset) {int i=0,//Индекс цвета в таблице преобразований t=0,//Индекс таблицы //Индекс цвета, соответствующего нижней границе яркости t_index=0, // Индекс цвета, соответствующего верхней границе яркости b_index=0, value_offset; //Смещение значения цвета double value=0.; //Новое значение цвета //Изменяем яркость for(i, t=0; t<3; t++) for(i=0; i<256; i++) {if(i+b_offset>255) BGRTransTable[t][i]=255; else if(i+b_offset<0) BGRTransTable[t][i]=0; else BGRTransTable[t][i]=i+b_offset; } // Изменяем контрастность if(c_offset<0)// Уменьшаем контрастность {for(i=0, t=0; t<3; t++) for(i=0; i<256; i++) if(BGRTransTable[t][i] { //Рассчитываем смещение в зависимости от удалённости цвета от “серой середины” value_offset=(CONTRAST_MEDIAN-BGRTransTable[t][i])*c_offset/128; if(BGRTransTable[t][i]-value_offset>CONTRAST_MEDIAN) BGRTransTable[t][i]=CONTRAST_MEDIAN; else BGRTransTable[t][i]-=value_offset; } else { // Рассчитываем смещение в зависимости от удалённости цвета от “серой середины” value_offset=(BGRTransTable[t][i]-CONTRAST_MEDIAN)*c_offset/128; if(BGRTransTable[t][i]+value_offset else BGRTransTable[t][i]+=value_offset; } } elseif(c_offset>0) //Увеличиваем контрастность { //Расчёт нижней границы цвета int offset_b=c_offset*CONTRAST_MEDIAN/128; //Все значения в таблице ниже нижней границы получат значения 0 for(t=0; t<3; t++) for(b_index=0; b_index<256; b_index++) { if(BGRTransTable[t][b_index] BGRTransTable[t][b_index]=0; else break; } // Расчёт верхней границы цвета int offset_t=c_offset*128/CONTRAST_MEDIAN; // Все значения в таблице ниже нижней границы получат значения 255 for(t=0; t<3; t++) for(t_index=255; t_index>=0; t_index--) { if(BGRTransTable[t][t_index]+offset_t>255) BGRTransTable[t][t_index]=255; else break; } //Расчёт шага изменения интенсивности цвета double step=256./(256-(offset_b+offset_t)); // "Растягиваем" интенсивность цветов между нижней и верхней //границами до диапазона 0-255 for(t=0; t<3; t++) { value=0.; for(i=b_index; i<=t_index; i++) { if(BGRTransTable[t][i]>=offset_b || BGRTransTable[t][i]<256- offset_t) {value=(int)((BGRTransTable[t][i]-offset_b)*step+0.5); if(value>255) value=255; BGRTransTable[t][i]=(int)(value); }}}} return TRUE; }; 3.7 Фильтр “Инверсия” Этот фильтр реализуется с помощью таблицы преобразований. Для его реализации добавим в программу класс CInvertColors (листинг 3.7.1), производный от класса CDotFilter. Листинг 3.7.1 – Интерфейс класса CInvertColors.Файл Filter.h //Инверсия цветов class CInvertColors: public CDotFilter { public: CInvertColors(); }; Операция инверсии цветов не требует никаких настроечных параметров, поэтому инициализация таблиц преобразования выполняется в конструкторе класса (листинг 3.7.1). Листинг 3.7.1 – Конструктор класса CInvertColors .Файл Filter.cpp CInvertColors::CInvertColors() {for(int i=0, t=0; t<3; t++) for(i=0; i<256; i++) {BGRTransTable[t][i]=255-i; } }; 3.8 Фильтр “Размытие” Фильтр "Размытие" - это уже пространственное преобразование. Применение этого фильтра оказывает эффект сглаживания деталей изображения. Фильтр реализуется классом CВlur (листинг 3.8.1) Листинг 3.8.1 – Интерфейс класса CBlur. Файл Filter.h class CBlur: public CMatrixFilter {public: CBlur(); }; Листинг 3.8.2 – Конструктор класса CBlur.Файл Filter.cpp const int BlurMatrix[25]= {1,1,1, 1,1, 1,1,1, 1,1, 1,1,1, 1,1, 1,1,1, 1,1, 1,1,1, 1,1 }; CBlur::CBlur() {m_pMatrix=BlurMatrix; m_rangX=5; m_rangY=5; }; Матрица BlurMatrix задаёт преобразование “Размытие”, а в конструкторе CBlur() запоминается её адрес и размер. 3.9 Фильтр “Резкость” Для повышения четкости изображения в фильтре используется матрица "Размытие". Задача повышения четкости изображения заключается в том, чтобы выделить высокочастотные детали изображения. Светлые детали сделать ярче, темные - темнее. Для этого изображение сначала размывается, а затем определяется разность между размытым изображением и оригиналом. На величину этой разницы изменяется яркость оригинала. Таким образом, однородные участки изображения не подвергнутся изменениям, а те места картинки, где присутствуют высокочастотные детали, станут конрастнее. Фильтр реализуется классом CSharp (листинг 3.9.1). Листинг 3.9.1 – Интерфейс класса CSharp. Файл Filter.h class CSharp: public CMatrixFilter { public: CSharp(); BOOL TransformPix(LONG x, LONG y); }; В классе CSharp переопределён метод TransformPix(), реализация метода приведена в листинге 3.9.1. Листинге 3.9.1 – Методы класса CSharp. Файл Filter.cpp. CSharp::CSharp() { m_pMatrix=BlurMatrix; m_rangX=5; m_rangY=5; }; // коэффициент увеличения резкости #define SHARP_COEFF 3 BOOL CSharp::TransformPix(LONG x, LONG y) { //Размыли пиксел if(!CMatrixFilter::TransformPix(x, y)) return FALSE; BYTE *pDPix=NULL, *pSPix=NULL; pSPix=m_pSourceBM->GetPixPtr(x,y); pDPix=m_pDestBM->GetPixPtr(x, y); int d=0; for(int c=0; c<3; c++) { // Нашли разницу d=*(pSPix+c)-*(pDPix+c); // Усилили разницу d*=SHARP_COEFF;