Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

GMSAPR

.pdf
Скачиваний:
11
Добавлен:
16.03.2016
Размер:
9.01 Mб
Скачать

101

return FALSE;

// Новую битмап выберем в контексте вирт. экрана m_VirtScreenDC.SelectObject(&m_VirtScreenBitmap);

}

// Очистим виртуальный экран

CBrush FonBrush(GRAY); // кисть для заливки фона m_VirtScreenBitmap.GetBitmap(&BMStruct);

// узнаем размеры экрана m_VirtScreenDC.FillRect(&CRect(0,0, BMStruct.bmWidth, BMStruct.bmHeight), &FonBrush);

// Выведем на виртуальный экран картинку pCurBM->DrawBitmap(&m_VirtScreenDC, 0, 0, m_dScale,

m_nStretchMode);

// Обновим изображение на экране

Invalidate(); return TRUE;

};

void CBMView::OnDraw(CDC* pDC)

{

CBMDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// Получим размер клиентской части окна

CRect ClientRect;

GetClientRect(&ClientRect);

//Копируем содержимое виртуального экрана

//с учетом позиции прокрукрутки

CPoint ScrollPos=GetScrollPosition(); pDC->BitBlt(ScrollPos.x, ScrollPos.y, ClientRect.Width(), ClientRect.Height(), &m_VirtScreenDC, ScrollPos.x, ScrollPos.y, SRCCOPY);

}

102

void CBMView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)

{

CBMDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// Обновим изображение на виртуальном экране if(UpdateVirtualScreen())

{

CSize ScrollSize; // Размер области прокрутки

//Область прокрутки –

//весь размер картинки с учетом масштаба

ScrollSize=pDoc->GetCurrentBMSize();

ScrollSize.cx=static_cast<LONG> (ScrollSize.cx*m_dScale);

ScrollSize.cy=static_cast<LONG> (ScrollSize.cy*m_dScale);

SetScrollSizes(MM_TEXT, ScrollSize);

}

else

AfxMessageBox("Ошибка при выводе на виртуальный экран");

// Вызываем метод базового класса

CScrollView::OnUpdate(pSender, lHint, pHint);

}

Рассмотрим, как же все это работает. Когда метод OnOpenDocument()

объекта-документа возвращает TRUE, каркас приложения создает объект-облик и посылает ему уведомление о необходимости отобразить содержимое документа. Это уведомление в нашем случае обрабатывается методом CBMView::OnUpdate(), который, в свою очередь, вызывает

CBMView::UpdateVirtualScreen(). Метод

UpdateVirtualScreen() создает растр нужного размера, присоединяет его к дескриптору виртуального экрана, запрашивает у объекта-документа

103

текущее изображение и выводит его на виртуальный экран. Вывод осуществляется с использованием переменных m_dScale, m_nStretchMode, которые задают масштаб и режим масштабирования. Эти переменные мы добавим в класс CBMView как раз для того, чтобы поэкспериментировать с масштабированием. В интерфейсе класса данные переменные описаны следующим образом:

double m_dScale;

int m_nStretchMode;

В конструкторе же эти переменные инициализируются начальными значениями:

CBMView::CBMView()

{

m_dScale=1.0;

m_nStretchMode=HALFTONE;

}

После того, как картинка была отрисована на виртуальный экран, вызывается метод Invalidate(), который сообщает облику, что ему следует обновить изображение на экране. Облик, получив это сообщение, вызывает свой метод OnDraw(), в котором изображение с виртуального экрана копируется на реальный экран.

Уже, кажется, можно загрузить картинки и посмотреть, как они выглядят. Ниже приведен снимок вида программы (так называемый скриншот, screenshot), с загруженными четырьмя рисунками (ведь это у нас многодокументное приложение).

104

Рис. 1. Четвертая картинка показана сразу в двух окнах

Более того, в программах с MDI-интерфейсом у одного документа может быть несколько объектов-обликов. Создать еще один облик для документа можно командой Window | New window. Изменение данных объекта-документа будет отражаться во всех окнах (всеми объектами-обликами). Такую возможность можно использовать, например, для показа одного и того же изображения в разных окнах в разных масштабах, далее мы посмотрим, как это выглядит на практике.

Чтобы можно было изменять масштаб вывода изображения, добавим в меню View программы команды Zoom In и Zoom Out, а для установки режима масштабирования — команды Stretch HALFTONE и Stretch COLORONCOLOR (рис. 3). Можно также назначить этим командам горячие клавиши. Обработчики этих команд добавим в класс облика. Эти функции изменяют состояние переменных m_dScale и m_nStretchMode. Для команд установки режимов масштабирования также добавлены методы

OnUpdateViewStretchhalftone() и OnUpdateViewStretchcoloroncolor() для обработки сообщения

UPDATE_COMMAND_UI. В этих функциях можно управлять состоянием соответствующих команд в интерфейсе программы (например, можно делать

105

недоступными команды в зависимости от состояния программы). В данном случае мы просто маркируем соответствующий режим масштабирования.

void CBMView::OnViewZoomin()

{

m_dScale*=2; OnUpdate(NULL, 0, NULL);

}

void CBMView::OnViewZoomout()

{

m_dScale/=2; OnUpdate(NULL, 0, NULL);

}

void CBMView::OnViewStretchhalftone()

{

m_nStretchMode=HALFTONE; OnUpdate(NULL, 0, NULL);

}

void CBMView::OnUpdateViewStretchhalftone(CCmdUI* pCmdUI)

{

pCmdUI->SetCheck(m_nStretchMode==HALFTONE);

}

void CBMView::OnViewStretchcoloroncolor()

{

m_nStretchMode=COLORONCOLOR; OnUpdate(NULL, 0, NULL);

}

void CBMView::OnUpdateViewStretchcoloroncolor (CCmdUI* pCmdUI)

{

pCmdUI->SetCheck (m_nStretchMode==COLORONCOLOR);

}

В приложениях с MDI-интерфейсом возможно также использовать механизм, называемый Splitted window, который позволяет показать несколько обликов в одном окне-рамке. Для этого потребуется лишь научить рамку

106

дочернего окна нашего приложения работать с несколькими обликами. Зайдем в интерфейс класса CChildFrame и добавим переменную-объект класса

CSplitterWnd:

// Attributes

protected:

CSplitterWnd m_wndSplitter;

Затем, переопределим в классе CChildFrame виртуальный метод

OnCreateClient и немного модифицируем его код, как показано ниже:

BOOL CChildFrame::OnCreateClient (LPCREATESTRUCT lpcs, CCreateContext* pContext)

{

//return CMDIChildWnd::OnCreateClient(lpcs,pContext);

return

m_wndSplitter.Create( this,

 

2, 2, //

Максимальное

количество строк и столбцов

CSize( 10, 10 ),

// Минимальный

размер окна

pContext

);

 

 

}

 

 

 

В результате — чудо! В одном окне-рамке мы можем увидеть четыре различных окна-облика с нашей картинкой в разных масштабах и разных режимах масштабирования. Для того чтобы получить такой эффект, надо ухватиться мышью за ползунок в левой (верхней) части нижней (правой) полосы прокрутки и перетащить его в нужную позицию.

Хотя метод CSplitterWnd::Create() имеет параметры для задания максимального количества строк и столбцов, в документации сказано, что эти значения почему-то не должны превышать 2. Хотя, если попробовать увеличить это значение на свой страх и риск, то можно разбить рамку и на 9 различных обликов.

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

107

показаны увеличенные фрагменты изображения: слева в режиме HALFTONE,

справа — COLORONCOLOR. Видно, что режим COLORONCOLOR не пытается бороться с «лестничным эффектом».

Рис. 2. В одном окне-рамке отображаются четыре различных облика одного объектадокумента

5.3Заключение

Вданной главе мы рассмотрели особенности вывода изображений на экран с использованием виртуального экрана, устранения мерцания (путём тривиальной модификации метода OnEraseBckg), обеспечили масштабирование при выводе на экран, а так же научились использовать элемент управления CSplitterWnd.

Все эти методы делают работу с графической информацией гораздо более удобной.

108

6 Редактирование изображений

Ну вот, наконец-то мы добрались до самого интересного. Магия преобразований — вот главная радость, которую дает цифровая обработка изображений. Используя возможности редактирования в таких программах как

Adobe Photoshop или Ulead Photoimpact, человек, даже с заурядными художественными способностями, может превратить самую скучную фотографию в нечто более привлекательное.

Конечно, за всем этим стоит прочная математическая база и кропотливый труд программистов.

6.1 Преобразования

Далее мы рассмотрим два вида преобразований:

Точечные — новое значение элемента изображения (пиксела) рассчитывается только на основе его старого значения;

Пространственные (матричные) — при расчете нового значения пиксела учитывается не только его старое значение, но также

значения некоторой области пикселов вокруг него.

Точечные преобразования удобно выполнять с помощью таблиц преобразования, которые будут подробно рассмотрены далее.

Обычно пространственное преобразование заключается в нахождении свертки значений группы пикселов. Свертка вычисляется как сумма пиксельных значений, попавших в зону преобразования, помноженных на весовые коэффициенты. В качестве весовых коэффициентов выступают элементы матрицы преобразования. Значения элементов матрицы преобразования и определяют тип преобразования. Размер матрицы преобразования соответствует области пикселов, которые будут участвовать в преобразовании. Центральный элемент матрицы — весовой коэффициент преобразуемого пиксела (x, y). Поэтому матрицы преобразования, обычно

109

имеют нечетный размер (например, 3 × 3 или 5 × 5 элементов). Часто свертки заключают в себе сложный и глубокий математический смысл, который, к счастью, имеет простую и понятную практическую интерпретацию. Рассмотрим, например, свертку с помощью следующей матрицы M:

M =

1

1

1

 

 

1

1

1

 

 

 

1

1

1

 

.

 

 

 

 

Новое значение пиксела P (x, y) может быть рассчитано с использованием следующего псевдокода:

MX=3; // размер матрицы преобразования по x

MY=3; // размер матрицы преобразования по y

CountCoeffSumm=0; // счетчик суммы коэффициентов матрицы

//преобразования

NewP=0; // новое значение пиксела for(j=-MY/2; j<= MY/2; j++)

for(i=-MX/2; i<=MX/2; i++)

{

NewP = NewP + P(x+i, y+j)*M(i, j);

CountCoeffSumm = CountCoeffSumm + M(i, j);

}

P(x, y)=NewP/CountCoeffSumm;

Здесь предполагается целочисленное деление, то есть результат MX/2 — значение 1.

В процессе преобразования выполняется подсчет коэффициентов матрицы преобразования, а после расчета нового значения оно делится на сумму коэффициентов. Это необходимо для того, чтобы привести результат к диапазону значений суммированных пикселов.

Описанная выше свертка с помощью единичной матрицы соответствует преобразованию «размытие» (то есть понижение четкости) изображения. Достигается такой эффект за счет усреднения значений группы пикселов, охваченной матрицей преобразования. Если значение пиксела (x, y) было выше

110

среднего, оно уменьшится, если было ниже среднего, то увеличится. Однако это не означает, что все изображение станет монотонным, так как матрица преобразования движется по изображению вместе с координатами (x, y), средний уровень тоже изменяется. Далее будет рассмотрено применение различных матриц преобразования.

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

Само название процесса «геометрический» говорит о том, что суть преобразования заключается в изменении положения или других геометрических характеристик изображения. Примерами геометрических преобразований являются поворот, сдвиг, интерполяция (масштабирование) изображения.

6.2 Таблица преобразования

Таблица преобразования — это просто массив, заполненный какими-то значениями. Размер массива равен максимальному значению, которое может принимать преобразуемая величина. С помощью такой таблицы удобно и быстро можно заменять одно значение другим. Таблицы преобразований применимы, когда новое значение формируется из единственного старого значения, то есть при точечном преобразовании.

Например, нам надо преобразовать яркость пиксела. В этом случае старое значение яркости играет роль индекса элемента в таблице, а значение этого элемента является новым значением. Формула преобразования может выглядеть так:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]