Использование вебкамер
План лекции
1.Вебкамеры
Получение изображения
Пороговая обработка
Калибровка
Игра “Трамплин”
2. Камеры глубины
Создание интерактивных поверхностей
Динамический мэппинг
Растровое изображение
- это прямоугольный массив пикселей.
1-канальное пиксель = яркость 0..255
3-канальное пиксель = Red, Green, Blue 0..255
4-канальное пиксель = Red, Green, Blue, Alpha 0..255
Она называются:
“полутоновое”, “цветное”, “цветное с прозрачностью”
Получение изображения с камеры в openFrameworks
ofVideoGrabber camera;
void ofApp::setup(){
camera.setDeviceID\( 0 \); //Выбор камеры \(если несколько\)
camera.initGrabber\( 640, 480 \);
}
void ofApp::update(){
camera.update\(\);
}
void ofApp::draw(){
ofSetColor\( 255 \);
camera.draw\( 0, 0 \);
}
Конвертация в полутоновое изображение
объявить
ofPixels pix;
добавить в update():
if \( camera.isFrameNew\(\) \) {
pix = camera.getPixelsRef\(\);
pix.setImageType\( OF\_IMAGE\_GRAYSCALE \);
//Тут можно обрабатывать пиксели
}
добавить в draw():
ofImage img;
img.setFromPixels\( pix \);
img.draw\( 0, 0 \);
Доступ к пикселям
pix.getColor( x, y ).getBrightness() - возвращает яркость пикселя (x,y)
pix.setColor( x, y, ofColor( value ) ) - ставит яркость пикселю
Уменьшение яркости изображения:
добавить после “Тут можно обрабатывать ...”
for \(int y=0; y<480; y++\) {
for \(int x=0; x<640; x++\) {
int value = pix.getColor\( x, y \).getBrightness\(\);
value = value / 2;
pix.setColor\( x, y, value \);
}
}
Пороговая обработка
объявить
int T = 100; //порог
изменить value = … на
value = \( value > T \) ? 255 : 0;
добавить в draw() вывод на экран строки со значением T:
ofSetColor\( 255, 0, 0 \);
ofDrawBitmapString\( "T = " + ofToString\( T \), 20, 20 \);
добавить в keyPressed() изменение порога по нажатию z и x:
if \( key == 'z' \) T -= 10;
if \( key == 'x' \) T += 10;
Определение роста человека
В условных единицах. Для перевода в метры - нужно просто калибровать эти значения.
int Y = 0; //координата самой верхней точки
в update(), после пороговой обработки
//вычисление высоты - ищем самую верхнюю белую точку
Y = -1;
for \(int y=0; y<480 && Y == -1; y++\) {
for \(int x=0; x<640; x++\) {
int value = pix.getColor\( x, y \).getBrightness\(\);
if \( value == 255 \) {
Y = y;
break;
}
}
}
в draw()
ofSetColor\( 255, 0, 0 \);
ofSetLineWidth\( 3 \);
ofLine\( 0, Y, 640, Y \);
ofSetLineWidth\( 1 \);
ofDrawBitmapString\( "H = " + ofToString\( 480-Y \), 20, 40 \);
Оцифровка графика функции
Используя этот же алгоритм, но для
X=0, 10, 20, ...
можно оцифровать график функции. Будем искать черные точки (графика) на белом фоне (бумаге)
int stepX = 10;
int Y[200];
вычисление трамплина, после пороговой обр.
for (int x=0; x<640/stepX; x++) {
int x1 = x \* stepX;
for \(int y=0; y<480; y++\) {
Y\[ x \] = 480;
int value = pix.getColor\( x1, y \).getBrightness\(\);
if \( value == 0 \) {
Y\[ x \] = y;
break;
}
}
}
в draw() рисуем трамплин:
ofPushMatrix();
ofTranslate( 10, 260 );
ofSetColor( 255 );
ofNoFill();
ofSetLineWidth( 2 );
ofRect( 0, 0, 639, 479 );
ofSetLineWidth( 1 );
ofSetColor( 255, 0, 0 );
for (int x=0; x<640 / stepX - 1; x++) {
ofLine\( x \* stepX, Y\[x\],
\(x+1\) \* stepX, Y\[x+1\] \);
ofLine\( x \* stepX, 480,
x \* stepX, Y\[x\] \);
}
ofPopMatrix();
Оцифровка графика функции
Используя этот же алгоритм, но для
X=0, 10, 20, ...
можно оцифровать график функции. Будем искать черные точки (графика) на белом фоне (бумаге)
Калибровка камеры и проектора
1)Если пороговую обработку делать не для яркости, а для зелёной компоненты цвета (или, красной или синей),
то можно выводить картинку проектора красными и синими цветами, и камера не будет их видеть.
int value = pix.getColor\( x, y \).getBrightness\(\);
меняем на
int value = pix.getColor\( x, y \).g;
зелёная компонента цвета (а r, b - синие)
Калибровка камеры и проектора
1)Если пороговую обработку делать не для яркости, а для зелёной компоненты цвета (или, красной или синей),
то можно выводить картинку проектора красными и синими цветами, и камера не будет их видеть.
int value = pix.getColor\( x, y \).getBrightness\(\);
меняем на
int value = pix.getColor\( x, y \).g;
зелёная компонента цвета (а r, b - синие)
2) После этого, можно на изображении с камеры задать 4 точки - углы соответствующей картинки проектора;
и осуществить трансформацию изображения камеры.
(Используя перспективную трансформацию, или, для простоты, билинейную интерполяцией)
После этого, камера и проектор будут видеть и освещать часть стены в одинаковых координатах
Калибровка камеры и проектора
Билинейная трансформация:
for \(int y=0; y<480; y++\) {
for \(int x=0; x<640; x++\) {
float xu = x / 640.0;
float yu = y / 480.0;
ofPoint P = \(1-xu\) \* \(1-yu\) \* p\[0\] + \(xu\) \* \(1-yu\) \* p\[1\]
+ \(xu\) \* \(yu\) \* p\[2\] + \(1-xu\) \* \(yu\) \* p\[3\];
int X = P.x;
int Y = P.y;
int value = 255;
if \( X >= 0 && X < 640 && Y >= 0 && Y < 480 \) {
value = pixCam.getColor\( X, Y \).g;
value = \(value > T \) ? 255 : 0;
}
pix.setColor\( x, y, value \);
}
}
Игра трамплин
Добавим к проекту мячик, прыгающий по графику.
Получим игру “Трамплин”
Реализация мяча
В update(), после вычисления точек графика
//мячик
ofPoint force;
force.y += 9.8 \* m \* scale;
//вычисление реакции опоры \(трамплина\)
int x = int\(ball.x / stepX\);
if \( x >= 0 && x < 640 / stepX - 1 \) {
float y1 = Y\[x\];
float y2 = Y\[x+1\];
//высота трамплина в этой точке
float y = ofMap\( ball.x / stepX, x, x+1, y1, y2 \);
//направление
ofPoint norm\( y1-y2, stepX \);
norm.normalize\(\); //делаем единичной длины
if \( ball.y + rad > y \) {
ball.y = y - rad;
float proj = norm.x \* vel.x + norm.y \* vel.y;
vel = vel - norm\*2\*proj;
}
}
ofPoint a = force / m; //ускорение
vel += a \* dt;
ball += vel \* dt;v
ofPoint ball; //координаты центра мяча
ofPoint vel; //вектор скорости мяча
float dt = 1.0 / 30.0;
float m = 0.5;
float rad = 20;
float scale = 50;
новая функция:
void start() {
ball.x = 40;
ball.y = 0;
vel.x = vel.y = 0;
}
в setup():
start\(\);
в draw() рисуем мяч:
ofSetColor\( 255, 0, 255 \);
ofFill\(\);
ofCircle\( ball, rad \);
Примеры того, что еще можно делать с камерой
1)Оптический поток
2) Петля камера -> экран -> камера ...