Вы находитесь на странице: 1из 31

Разработка интерактивных систем

на OpenFrameworks

3. Создание коллажей.
Детектор движения

лекции и объявления:
www.uralvision.blogspot.com twitter.com/uralvision

вопросы по проектам и программированию:


perevalovds@gmail.com УрГУ / ИММ весна 2010
Создание коллажей
Коллаж
Коллаж (от фр. collage — наклеивание) — технический приём в изобразительном искусстве,
заключающийся в наклеивании на подложку предметов и материалов, отличающихся от основы по
цвету и фактуре. Коллажем также называется произведение, целиком выполненное в этой технике.
(Википедия)

А здесь мы будем под коллажом понимать размещение различных картинок на экране.

Для коллажа нужно:


- загрузить набор
картинок,
осуществить их
- вращение,
- перенос,
- изменение размера,
- прозрачность.

http://www.chinesecontemporary.com/hong_hao_5.htm
Загрузка и вывод изображения

объявление картинки
ofImage image;

в setup()
image.loadImage( "texture.jpg" ); //загрузка с диска
//файл должен лежать в bin/data

в draw()
image.draw ( 100.0, 50.0 ); //вывод на экран
//левый верхний угол будет в (100, 50)
Это изображение можно скачать с
http://uralvision.blogspot.com/2010/03/4.html
Исходное изображение было взято с
http://ayesha5.files.wordpress.com/2008/06/sun-flower2.jpg
Поворот изображения
в draw()

ofPushMatrix(); //запомнить матрица преобразования


ofRotate( 10.0 ); //поворот в градусах левого верх. угла
image.draw( 0.0, 0.0 ); //рисуем
ofPopMatrix(); //восстановить матрицу
Вращение около своего центра
в draw()
//Рисуем повернутым, и чтоб центр был в ( 200.0, 100.0 )
ofPushMatrix();
ofTranslate( 200.0, 100.0 ); //центр картинки
ofRotate( 20.0 ); //поворот
//рисование со сдвигом:
image.draw( -image.width / 2, -image.height / 2
); ofPopMatrix();
Прозрачность для пикселов
Чтобы сделать хороший коллаж из нескольких картинок,
нужно убрать черный фон. Это делается путем
использования прозрачности для пикселов изображения.
Прозрачность всего изображения
Также для коллажей часто используют прозрачность для всего
изображения. Сегодня мы это обсуждать не будем.

На картинке показан коллаж, где некоторые подсолнухи наложены с


прозрачностью. А у двух подсолнухов к тому же сделана полная
прозрачность соответственно Red, Blue и Green, Blue каналов.
Прозрачность пикселов
Применение прозрачности для
пикселов изображения
Если к каналам Red, Green, Blue добавить канал Alpha, то
можно задавать прозрачность пикселов.

Alpha = 0 - пиксел прозрачен и невидим,


Alpha = 255 - пиксел полностью непрозрачен.
То есть можно просто вырезать фон.
Схема смешивания цветов при
прозрачности
Обычно данные о прозрачности хранятся в виде параметра "alpha". Это
"непрозрачность".

Если значение alpha фрагмента в [0, 1] (то есть alpha = Alpha / 255.0)
то старый цвет C0 смешивается с цветом фрагмента C1 по формуле

R = (1-alpha) * R0 + alpha * R1
G = (1-alpha) * G0 + alpha * G1
B = (1-alpha) * B0 + alpha * B1

Если alpha = 0 - новый цвет равен просто старому C0.


Если alpha = 1 - новый цвет равен цвету фрагмента C1.
При промежуточных значениях - происходит смешивание цветов.

Если несколько объектов накладываются - процедура выполняется


последовательно, от дальнего к ближнему.
Схема смешивания цветов при
прозрачности
Красный квадрат накладываем на черный, белый и синий
цвет.
Накладываем три раза, с alpha: 0.3, 0.6, 1.0.
Способы получения изображений с
вырезанным фоном
1. из векторного редактора
2. "умным" выделением краев в Photoshop или Gimp
3. вручную - плохое качество (алиасинг-рваные края)!
Форматы картинок, хранящие
прозрачность
1. Форматы, позволяющие хранить прозрачность
png-24 - наилучший по качеству / размеру / скорости
распаковки
bmp-32, tiff, tga.
2. Форматы, которые хранят прозрачность 1-битовую
(рваные края):
gif, png-8.

3. Не хранит прозрачность в принципе:


jpg.
Пример: вращающиеся подсолнухи
//Объявление переменных
ofImage image; //картинка
float angle = 0.0; //угол вращения

//Инициализация
void testApp::setup(){
image.loadImage( "texture.png" ); //png - с прозрачностью
angle = 0.0;
}

//Обновление состояния
void testApp::update(){
angle += 0.1; //поворот
}
Пример: вращающиеся подсолнухи
//Рисование
void testApp::draw(){
//нужно включить режим прозрачности
glEnable(GL_BLEND);
//и функцию, которую использовать для прозрачности
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

for (int i=0; i<5; i++) {


ofPushMatrix();
ofTranslate( 300.0 + i * 100.0, 300.0 ); //перенос
ofRotate( angle ); //вращение
ofScale( 1.0 + 0.2 * i, 1.0 + 0.2 * i ); //увеличение размера
image.draw( -image.width / 2, -image.height / 2 );
ofPopMatrix();
}
glDisable(GL_BLEND); //выключение режима прозрачности
}
Проект "Поле подсолнухов"
- первое слово
Хотим сделать поле подсолнухов, которые реагируют на
проходящих людей.

Для этого нужно сначала научиться делать

Детектор Движения
Детектор движения
Что делает детектор движения

Детектор движения (ДД) на основе видеокамеры


обнаруживает области на изображении, в которых имеется
движение некоторых объектов.

Мы реализуем простейший ДД, который анализирует два


подряд идущих кадра с камеры.

Более продвинутые ДД анализируют не два, а большее количество


кадров.

В охранных системах сейчас в основном используются ДД не на основе


видеокамер, а автономные датчики, работающими на основе анализа
теплового излучения.
Проект Детектор Движения
//Объявляем переменные

//видео-граббер для "захвата" видеокадров


ofVideoGrabber grabber;
int w, h; //ширина и высота кадра

unsigned char * outImage; //обработанные байты изображения


ofTexture outImageTexture; //текстура для вывода на экран
изображения

//байты предыдущей картинки для анализа движения в кадре


unsigned char * lastImage;
//флажок, что начало обработки
bool isFirstFrame;
Проект Детектор Движения
//Инициализация

void testApp::setup(){
w = 320;
h = 240;
grabber.initGrabber(w, h); //подключение камеры
ofBackground(255,255,255); //задаем цвет фона

//выделение памяти для выходного изображения


outImage = new unsigned char[w * h * 3];
//создание текстуры для вывода результата на экран
outImageTexture.allocate( w, h, GL_RGB );

//выделение памяти для картинки для анализа движения в кадре


lastImage = new unsigned char[w * h * 3];
isFirstFrame = true; //помечаем, что будет только первый кадр
}
Проект Детектор Движения
//Обновление состояния стр. 1 / 3

void testApp::update() {

grabber.grabFrame(); //захват кадра


if (grabber.isFrameNew()){ //если пришел новый кадр
//Берем пикселы пришедшего кадра
unsigned char * input = grabber.getPixels();

//если первый кадр - запоминаем его в буфер,


//чтоб не было мусора в начале работы
if ( isFirstFrame ) {
isFirstFrame = false; //сбрасываем флажок
memcpy( lastImage, input, w * h * 3 ); //копируем input -> lastImage
}
Проект Детектор Движения
//Обновление состояния стр. 2 / 3

//Анализ движения в кадре


for (int y=0; y<h; y++) {
for (int x=0; x<w; x++) {
//входной пиксел (x, y):
int r = input[ 3 * (x + w * y) + 0 ];
int g = input[ 3 * (x + w * y) + 1 ];
int b = input[ 3 * (x + w * y) + 2 ];
//пиксел с предыдущего кадра (x, y)
int r0 = lastImage[ 3 * (x + w * y) + 0 ];
int g0 = lastImage[ 3 * (x + w * y) + 1 ];
int b0 = lastImage[ 3 * (x + w * y) + 2 ];
//разность яркостей
int delta = abs( r-r0 ) + abs( g-g0 ) + abs( b-b0 );
//результат
int result = ( delta >= 100 ) ? 255 : 0;
outImage[ 3 * (x + w * y) + 0 ] = result;
outImage[ 3 * (x + w * y) + 1 ] = result;
outImage[ 3 * (x + w * y) + 2 ] = result;
}
}
Проект Детектор Движения
//Обновление состояния стр. 3 / 3

//запись в текстуру для последующего вывода ее на экран


outImageTexture.loadData(outImage, w, h, GL_RGB);

//копируем картинку текущего кадра для будущего сравнения


memcpy( lastImage, input, w * h * 3 );
}
}

//Рисование
void testApp::draw(){
grabber.draw( 0.0, 0.0 ); //вывод кадра
outImageTexture.draw( w, 0.0 ); //вывод результата обработки
}
Проект Детектор Движения
Проект "Поле подсолнухов"
Хотим сделать поле подсолнухов, которые реагируют на
проходящих людей.
Трава: http://madhousemama.files.wordpress.com/2009/04/grass.jpg
Проект "Поле подсолнухов"
Сценарий
- нарисована трава как фоновая картинка.
- в узлах регулярной сетки нарисованы подсолнухи, маленькие.
- когда в камере есть движение - в соответствующих областях подсолнухи
начинают расти и крутиться. Затем постепенно они возвращаются в
исходный размер и останавливают вращение.

Реализация
- Анализ видео
Разбить изображение камеры на прямоугольные фрагменты.
В каждом фрагменте считать число пикселов, которые находит детектор
движения - то есть для которых ( outImage[ 3 * (x + w * y) + 0 ] == 255 ).
Проект "Поле подсолнухов"
- Физика

Подсолнухи заданы массивом: для каждого подсолнуха хранится:


- положение его центра на экране
- координаты фрагмента для ДД
- текущий угол вращения и размер,
- текущая скорость изменения угла и скорость изменения размера.

На шаге обработки во фрагменте подсчитывается число пикселов движения. Это


число интерпретируется как величина силы, которая увеличивает значение скорости
вращения и увеличения.

Что подсолнухи останавливали вращение - считаем что есть сила трения, влияющая
на скорость вращения:
Vangle = Vangle * k_trenie, k_trenie - коэффициент трения, например, 0.9 или 0.95.

Чтобы подсолнухи после увеличения размера обратно уменьшались - считаем, что


на размер size действует сила Гука, стремящаяся его вернуть в некоторое
положение size0, аналогично примеру с маятником:
double F_guka= k_guka * (size0 - size), k_guka - коэффициент, например, 0.05
Приложение:
Запись видео работающей
программы и публикация
Захват видео с экрана

Программа CamStudio - бесплатная программа для захвата


изображений с экрана и записи в видеофайл.
http://camstudio.org

Замечание: при больших размерах захватываемой области


скорость захвата может весьма низкой. Не забывайте при съемке
вашего проекта установить режим Release, а не Debug.
Публикация видеоролика
Где публиковать: Youtube или Vimeo.
Youtube - более знаком.
Vimeo - профессиональные работы по интерактивным инсталляциям
публикуют обычно тут, так как качество видео очень хорошее.

Пример: RubberMirror - резиновое зеркало


http://www.youtube.com/watch?v=tk-g-sYb7-I

Оценить