Upload
christopher-cerqueira
View
118
Download
4
Embed Size (px)
Citation preview
RA on-demand Criando interfaces em tempo de execução.
08
:38
fb
.co
m/R
VA
.BR
2
Construção de interfaces on-demand baseadas em Realidade Aumentada Projetiva para Controle de Hardware (Arduino)
Drª. Ana Maria Ambrosio
MSc. Christopher Cerqueira
aluno doutorado
Dr. Claudio Kirner
Objetivo do Capítulo
SketchSynth
BackStage
08
:38
fb
.co
m/R
VA
.BR
4
Objetivo da Apresentação
08
:38
fb
.co
m/R
VA
.BR
5
Objetivo da apresentação
• Devido as restrições, vamos montar uma versão simplificada:
• Ausência de projetores para todos os alunos
• Ausência de ARDUINO para todos os alunos
• Mostrar um framework para construção de apps de RA / RC.
• openFrameworks
• Mostrar os recursos de controle de hardware (Arduino)
• Mostrar os recursos de CV (openCV): rastreio de contorno (infraestrutura e controles) e processamento de imagem (mão).
• Mostrar tipos comuns para criação de interface.
• openFrameworks
• Aplicações tipo interação em mesa. (Projeção)
08
:38
fb
.co
m/R
VA
.BR
6
Agenda:
• Conceito e openFramework
• Criando o primeiro programa em openFrameworks
• Conectando com o Arduino
• Estudos de caso / Construção do ambiente (exemplos):
• 1. Detectar infraestrutura (Apenas rastreio do papel)
• 2. Rastreio do atuador (rastreio da mão)
• 3. Interação com estrutura virtual
• 4. Detectar estrutura física on-demand (sem reprojeção)
• Wrap-Up
3 horas
1,5h
1,5h
08
:38
fb
.co
m/R
VA
.BR
7
Conteúdo está em
•http://www.4shared.com/folder/XkaUh0KM/Resources.html
• Link tá muito difícil??? Vai em
cscerqueira.com.br
08
:38
fb
.co
m/R
VA
.BR
8
CONCEITOS
Básico - Experiências
08
:38
fb
.co
m/R
VA
.BR
9
“The product is no longer the basis of value. The
experience is.”
Venkat Ramaswamy
The Future of Competition
08
:38
fb
.co
m/R
VA
.BR
10
Valor de uma boa experiência
08
:38
fb
.co
m/R
VA
.BR
11
Interação
Texto, som, cores, visual, mecânico ou
físico.
Interface
Mensagens
Usuário Sistema 08
:38
fb
.co
m/R
VA
.BR
12
Usabilidade
08
:38
fb
.co
m/R
VA
.BR
13
Exemplo:
• Usabilidade:
1. Facilidade de aprendizado
2. Eficiência
3. Facilidade de memorização
4. Erros
5. Satisfação subjetiva
Meta-Mensagens
Usuário Sistema
08
:38
fb
.co
m/R
VA
.BR
14
3 níveis
Lógico: Resolvem, solucionam, facilitam.
Emocional: Satisfazem necessidades e desejos afetivos.
Visceral: resolvem questões fundamentais, sem consciência. Impulso.
08
:38
fb
.co
m/R
VA
.BR
15
Espera.... Visceral?
08
:38
fb
.co
m/R
VA
.BR
16
08
:38
fb
.co
m/R
VA
.BR
17
Realidades – Realidade Virtual
interface que permite ao
usuário interagir, em tempo real,
com um mundo tridimensional gerado por
computador, usando seus
sentidos através de
equipamentos especiais.
SOURCE: NASA (2013a)
08
:38
18
fb.c
om
/RV
A.B
R
Billinghurst Vision
08
:38
fb
.co
m/R
VA
.BR
19
Realidades – Realidade Aumentada
uma interface baseada na
sobreposição de informações virtuais geradas por computador (envolvendo imagens estáticas e dinâmicas, sons
espaciais e sensações hápticas) com o ambiente físico do usuário, percebida através de dispositivos tecnológicos e
usando as interações naturais do usuário, no mundo físico.
Claudio Kirner
SOURCE: Adapted from ESA (2009) and Capua (2008)
08
:38
20
fb.c
om
/RV
A.B
R
Christopher Vision
Informação virtual
Interação Natural
dispositivos tecnológicos
08
:38
21
fb.c
om
/RV
A.B
R
Realidades – Realidade Cruzada
é um ambiente de realidade misturada ubíqua, que vem da fusão de uma rede de sensores e atuadores
(que coletam e enviam dados relacionados ao mundo real) com mundos virtuais compartilhados,
usando a interface da realidade aumentada.
Claudio Kirner
Intr
od
uçã
o
08
:38
fb
.co
m/R
VA
.BR
22
Kirner’s Diagram – elementos
Source: (KIRNER et al., 2012)
Lego
Cave
08
:38
fb
.co
m/R
VA
.BR
23
Realidade Cruzada
Objetos reais
Sensores e Atuadores
Realidade Aumentada
OC
R
NO
CR
NO
CR
Intr
od
uçã
o
08
:38
fb
.co
m/R
VA
.BR
24
[ ] Hololens
• Talvez o primeiro hardware de RA, com sobreposição, que entra com força no mercado.
• Google Glass tentou, mas na maioria dos apps não tinha sobreposição intrínseca.
• Vídeo 1
08
:38
fb
.co
m/R
VA
.BR
26
Qual o motivo de RA ainda não ter pego em atividades de engenharia?
HWs de interação?
HWs de visualização?
Frameworks de rastreio
SWs
Frameworks geo-localizados
Frameworks de interação
08
:38
fb
.co
m/R
VA
.BR
27
Um dos motivos ao nosso ver é:
• Dificuldade de criação e manipulação de uma interface
de RA a medida que é utilizada.
• Poderíamos então pensar em uma estratégia, de criação
de uma interface física e indicar sua camada virtual a medida que é necessário (on-demand) e a função desejada.
• Esses minicurso essencialmente é para mostrar uma opção de criação de interface em RA on demand.
• “Solução simplificada: openFrameworks + visão computacional.”
08
:38
fb
.co
m/R
VA
.BR
28
OPENFRAMEWORKS
08
:38
fb
.co
m/R
VA
.BR
29
Processing
Adobe Flash
Unity
Cinder
openFrameworks
08
:38
fb
.co
m/R
VA
.BR
30
openframeworks.cc
• Vídeo oF1..\..\media_videos\7_openFrameworks1.mp4
• Vídeo oF2..\..\media_videos\7_openFrameworks2.mp4
08
:38
fb
.co
m/R
VA
.BR
31
oF
• Criado para artistas e designers • Desenvolvido por: Zach Liberman, Theo Watson, Artuno
Castro e Chris O’Shea
•Proposta: Arrumar a falta de comunicação entre
diversas bibliotecas em C++, e permitir portabilidade.
• Escrita em C++
• Licença: MIT (educacional e venda)
• Usar quando:
• O projeto renderiza muitos gráficos 3D, e/ou;
• Utilizar muita visão computacional, e/ou;
• Controlar equipamentos, como, por exemplo:
• o ARDUINO.
08
:38
fb
.co
m/R
VA
.BR
32
33
08
:38
fb
.co
m/R
VA
.BR
C++ Portável!!!!
34
08
:38
fb
.co
m/R
VA
.BR
Página Principal – openframeworks.cc
08
:38
fb
.co
m/R
VA
.BR
35
Libs no pacote padrão
• OpenGL, GLEW, GLUT, libtess2 e cairo para gráficos.
• rtAudio, PortAudio ou FMOD e Kiss FFT para entrada, saída e análise de áudio.
• FreeType para fontes.
• FreeImage para salvar e carregar imagens.
• Quicktime e videoInput para playback e aquisição de vídeo.
• Poco, que contém uma variedade de utilidades.
08
:38
fb
.co
m/R
VA
.BR
36
Addons da comunidade: ofxaddons.com
08
:38
fb
.co
m/R
VA
.BR
37
OSX, Linux, Windows, iOS, Android, Linux ARM
08
:38
fb
.co
m/R
VA
.BR
38
ERA TROGLODITA (C++)
• Graduação (2010): • ARToolKit
• PTAMM
• Bolsista DTI (2011): • basAR
ERA DO FOGO (C++/oF)
• Mestrado (2012):
• Doutorado (2014):
08
:38
fb
.co
m/R
VA
.BR
39
MDE (Model Driven Engineering) MBSE (Model Based System Engineering)
• MDE é um conjunto de práticas de engenharia, baseadas em ferramentas que utilizam ao mesmo tempo meta-modelagem e transformações de modelos para atingirem automaticamente objetivos em produção, manutenção ou operação de sistemas intensivos em software.
40
Motivador - Matlab
08
:38
fb
.co
m/R
VA
.BR
Três tipos principais de aplicações MDE
MDE
Geração automática Descoberta do Modelo Interoperabilidade de sistemas
41
Um modelo pode ser transformado em outro modelo.
Um meta-modelo é um conjunto de conceitos e relações que o modelo pode realizar. “Filtro” de possibilidade.
Uma representação gera um conjunto de elementos.
08
:38
fb
.co
m/R
VA
.BR
MDE Natural Env.
08
:38
fb
.co
m/R
VA
.BR
42
• Introdução à utilização de openFrameworks para o desenvolvimento de aplicações de RVA
Link
• Construção de aplicações de Realidade Cruzada Projetiva utilizando openFrameworks e ARDUINO
Link
• Utilização de Realidade Aumentada, com marcadores(ARToolKitPlus) e outros (utilizando openCV), para controle e inspeção de hardware, utilizando a interface ARDUINO.
Link
08
:38
fb
.co
m/R
VA
.BR
43
SVR2013 - Resultados
44
08
:38
fb
.co
m/R
VA
.BR
SVR2014 - Resultados
08
:38
45
fb.c
om
/RV
A.B
R
WRVA2014 - Resultados
08
:38
fb
.co
m/R
VA
.BR
46
PRIMEIRO PROGRAMA EM OF
Vou pular, coloquei aqui para ficar + completo
08
:38
fb
.co
m/R
VA
.BR
47
Onde encontrar?
08
:38
fb
.co
m/R
VA
.BR
48
Level Easy: Gerador Automático
49
08
:38
fb
.co
m/R
VA
.BR
Escolhendo expansões
50
Do
co
re o
F
Da
com
un
idad
e
08
:38
fb
.co
m/R
VA
.BR
Estrutura do projeto
08
:38
fb
.co
m/R
VA
.BR
51
Exemplo básico
08
:38
fb
.co
m/R
VA
.BR
52
// ofApp.cpp
void ofApp::setup(){
mensagem = "Hello SVR2015!!!";
raio = 0;
}
void ofApp::draw(){
ofEnableAlphaBlending(); ofSetColor(ofColor::blue,128);
ofCircle(x_i,y_i,raio);
ofSetColor(ofColor::red);
ofDrawBitmapString(mensagem,mouseX,mouseY);
}
// ofApp.cpp
void ofApp::mouseDragged(int x, int y, int button){
raio = ofDist(x_i,y_i,x,y);
}
void ofApp::mousePressed(int x, int y, int button){
x_i = x;
y_i = y;
}
void ofApp::mouseReleased(int x, int y, int button){
raio = ofDist(x_i,y_i,x,y);
}
// ofApp.h
...
string mensagem;
int x_i,y_i;
float raio;
...
ARDUINO
O que é? Onde vivem?
Existe? Hoje no Glob..
Fritzing, Arduino 1.0, Firmata, exemplo openFrameworks
08
:38
fb
.co
m/R
VA
.BR
53
O que tem no ARDUINO?
08
:38
54
fb.c
om
/RV
A.B
R
Outras versões
08
:38
55
fb.c
om
/RV
A.B
R
Shields
08
:38
56
fb.c
om
/RV
A.B
R
FIRMATA
Literatura indica FIRMATA: http://firmata.org/wiki/Download
08
:38
57
fb.c
om
/RV
A.B
R
O que a FIRMATA faz!?
• Transforma o ARDUINO numa interface de controle, podendo ser modificado por um host.
• Quais as vantagens?
• O host controla a execução!
• O host tem mais memória.
• O host resolve as lógicas de controle muito mais rápido.
• Desvantagens?!
• Tem que ficar atrelado ao host!
08
:38
58
fb.c
om
/RV
A.B
R
Obs.: Mudança da nomenclatura dos pinos após Firmata 2.3 (Arduino 1.0) Tomar cuidado na hora de desenvolver!!!!!!!!!!
08
:38
fb
.co
m/R
VA
.BR
59
Instalando a Firmata no Arduino • Faça download do
Arduino 1.0.6 http://arduino.cc/en/Main/Software
• Abra o sketch do Firmata Standard.
• Transfira para a board.
08
:38
fb
.co
m/R
VA
.BR
60
http://arduino.cc/en/reference/firmata
“esquemático”
08
:38
61
fb.c
om
/RV
A.B
R
• #crashcourse fritzing
• http://fritzing.org/home/
Arduino e openFrameworks
08
:38
fb
.co
m/R
VA
.BR
62
Métodos para conectar com o ARDUINO connect()
disconnect()
getAnalog()
getAnalogPinReporting()
getDigital()
getDigitalPinMode()
getPwm()
getString()
isArduinoReady()
isInitialized()
sendAnalogPinReporting()
sendByte()
sendDigital()
sendDigitalPinMode()
sendPwm()
sendReset()
sendString()
setUseDelay()
update()
08
:38
63
fb.c
om
/RV
A.B
R
Código no openFrameworks
• Exploração do exemplo: communicationfirmataExample
08
:38
fb
.co
m/R
VA
.BR
64
Fluxograma básico
08
:38
fb
.co
m/R
VA
.BR
65
SETUP Habilita callback de Arduino Alive
Arduino Responde Evento
Alive
Configura Arduino
Loop de execução – sem Arduino
Enviar comandos pro Arduino
Recebeu um evento Digital
Recebeu um evento Analógico
Loop de execução – Arduino Update
Código
08
:38
fb
.co
m/R
VA
.BR
66
//ofApp.h #pragma once #include "ofMain.h" class ofApp : public ofBaseApp{ public: … ofArduino ard; bool bSetupArduino; private: void setupArduino(const int & version); void digitalPinChanged(const int & pinNum); void analogPinChanged(const int & pinNum); void updateArduino(); };
//ofApp.cpp void ofApp::setup(){ ... ard.connect("COM3", 57600); //conecta com arduino ofAddListener(ard.EInitialized, this, &ofApp::setupArduino); bSetupArduino= false;// flag arduino ok }
void ofApp::setupArduino(const int & version) { ofRemoveListener(ard.EInitialized, this, &ofApp::setupArduino); bSetupArduino = true; // agora pode usar o arduino. ard.sendDigitalPinMode(2, ARD_INPUT); //pino entrada digital ard.sendAnalogPinReporting(0, ARD_ANALOG); // pino entrada analógica ard.sendDigitalPinMode(13, ARD_OUTPUT); // configura pino saída digital ard.sendDigitalPinMode(11, ARD_PWM); // configura pino saída PWM ard.sendServoAttach(9); // diz que o pino tem um servo. ofAddListener(ard.EDigitalPinChanged, this, &ofApp::digitalPinChanged); //callback para eventos de pino digital. ofAddListener(ard.EAnalogPinChanged, this, &ofApp::analogPinChanged); //callback para eventos de pino analógico }
void ofApp::updateArduino(){ ard.update(); // verifica se algo mudou no Arduino - obrigatório if (bSetupArduino) { //envia o que for para o Arduino. ard.sendPwm(11, (int)(128 + 128 * sin(ofGetElapsedTimef()))); // pwm... } }
//ofApp.cpp void ofApp::digitalPinChanged(const int & pinNum) { // trata o pino digital - ard.getDigital(pinNum) } void ofApp::analogPinChanged(const int & pinNum) { // trata o pino analógico - ard.getAnalog(pinNum) }
//outros comandos ard.sendServo(9, 180, false); ard.sendDigital(8, ARD_HIGH);
ESTUDO DE CASOS:
08
:38
fb
.co
m/R
VA
.BR
67
Casos
• 0.a. O que vamos fazer:
• 1. Detecção de infraestrutura (contornos)
• 2. Detecção de atuador (diferença de imagens)
• 3. Interação mão / virtual / Arduino
• 4. Rastreio dos controles. (contornos)
• Real (Drawn) e Virtual Matching
08
:38
fb
.co
m/R
VA
.BR
68
0.a. O que vamos fazer:
Ambiente reativo à altura da areia
Ambiente reativo ao comportamento dos
pontos (soldados)
08
:38
fb
.co
m/R
VA
.BR
70
Conceito de ambiente dividido em pontos e camadas
08
:38
fb
.co
m/R
VA
.BR
71
Diagrama de etapas
08
:38
fb
.co
m/R
VA
.BR
72
O que vamos usar para o exemplo
08
:38
fb
.co
m/R
VA
.BR
73
Só use mais hardware quando realmente precisar.
08
:38
fb
.co
m/R
VA
.BR
74
DETECTING INFRASTRUCTURE
Encontrando uma folha de papel para servir de referência
para a montagem dos elementos de
interação.
08
:38
fb
.co
m/R
VA
.BR
75
Detectar a infraestrutura
Capturar Imagem
Procurar “coisas” brancas
Identificar polígono
• Maior de todos?
é um retângulo?
Encontrei a infraestrutura
Retirar a imagem da área
ide interação
08
:38
fb
.co
m/R
VA
.BR
76
Exemplo: Rastreio de Marcador
08
:38
fb
.co
m/R
VA
.BR
77
USANDO O OPENCV
Cores, blobs e exemplos
08
:38
fb
.co
m/R
VA
.BR
78
Cores
RGB-A (red, green, blue)-alfa HSV (hue (cor), saturation, value)
08
:38
79
fb.c
om
/RV
A.B
R
Blobs
• Método de busca de características.
• Blobs compartilham propriedades constantes que podem ser “percebidas” na imagem.
08
:38
80
fb.c
om
/RV
A.B
R
Construção dos artefatos : Rastreio Cores
08
:38
81
fb.c
om
/RV
A.B
R
Bonus: Projection Mapping
08
:38
fb
.co
m/R
VA
.BR
82
PM
PM
Usos
• Table-tops
• Projeções em paredes
08
:38
83
fb.c
om
/RV
A.B
R
Sensetable
L.A.S.E.R. Tag
Climbing
OASIS
Continuando ....
Usando o addon: ofxCv de Kyle McDonald https://github.com/kylemcdonald/ofxCv
08
:38
fb
.co
m/R
VA
.BR
84
Tipo: ofxCv::ContourFinder
• Tipo/função que procura blobs numa imagem.
• Retorna os blobs e características:
• Posição
• Contorno
• Centroide, média centro, pontos das bordas, menor retângulo contornável, menor elipse, etc etc etc...
08
:38
fb
.co
m/R
VA
.BR
85
Project Generator addons
08
:38
fb
.co
m/R
VA
.BR
86
// Camera:
ofVideoGrabber cam; // Video Tracking source
int camHeight;
int camWidth;
ofImage unwarped; // RAW image from camera
// Descobridor de papel
ofxCv::ContourFinder contourFinder;
int thresholdCV;
ofPolyline contour;
ofImage warpedPaper;
bool foundInfra;
// ## Função para retornar tipo de forma geométrica
private:
string findForm(const ofPolyline &poly);
template <class T>
string findForm(const T &poly){
return findForm(ofxCv::toOf(poly)); }
};
Câmera
Procurador de papel
08
:38
fb
.co
m/R
VA
.BR
87
Identificar Formas Convexas
• Entrada: número de vértices
• 3 vértices = TRIANGULO
• 4 vértices e cos +/- 0 = RETÂNGULO
• 5 vértices e cos +/- 0.3 = PENTÁGONO
• 6 vértices e cos +/- 0.5 = HEXÁGONO
• > 6 Círculo
• http://opencv-code.com/tutorials/detecting-simple-shapes-in-an-image/ • https://github.com/bsdnoobz/opencv-code/blob/master/shape-detect.cpp
08
:38
fb
.co
m/R
VA
.BR
88
string ofApp::findForm(const ofPolyline &poly){
vector<ofPoint> corners = poly.getVertices(); // pega todos os vértices
if ( corners.size() == 3) return "TRIANGLE";
if ( corners.size() >= 4 && corners.size() <= 6){ // de 4 a 6 vertices
int vtc = corners.size();
vector<double> cos;
for( int j = 2; j < vtc+1; j++) // mandrakaria para organizar cosenos
cos.push_back(angle(corners[j%vtc], corners[j-2], corners[j-1]));
sort(cos.begin(),cos.end());
double minCos = cos.front();
double maxCos = cos.back();
if( vtc == 4 && minCos >= -0.1 && maxCos <= 0.3) {
cv::Rect r = cv::boundingRect(toCv(poly));
double ratio = std::abs(1 - (double)r.width / r.height);
return (ratio <= 0.02) ? "SQUARE" : "RECTANGLE"; }
if( vtc == 5 && minCos >= -0.34 && maxCos <= -0.27) return "PENTAGON";
if( vtc == 6 && minCos >= -0.55 && maxCos <= 0.45) return "HEXAGON";
} else { // testar se circulo }
return "NO_MATCH";}
08
:38
fb
.co
m/R
VA
.BR
89
static double angle(ofPoint pt1, ofPoint pt2, ofPoint pt0)
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 +
dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
08
:38
fb
.co
m/R
VA
.BR
90
void ofApp::setup(){ // Parte 2 - Código do Exemplo ofLogNotice("[PAPER DETECTOR]") << " Configure CV "; contourFinder.setMinAreaRadius(10); // configura menor área . contourFinder.setMaxAreaRadius(200); // configura maior área thresholdCV = 100; // inicializa o nível da camera. //contourFinder.setTargetColor(targetColor, trackingColorMode); // configura cor / padrão contourFinder.setThreshold(thresholdCV); // configura nível ofLogNotice("[PAPER DETECTOR]") << " Configure Camera "; camWidth = 640; camHeight = 480; cam.initGrabber(camWidth,camHeight); cam.listDevices(); unwarped.allocate(camWidth, camHeight, OF_IMAGE_COLOR); // // ALLOCATE RAW IMAGE warpedPaper.allocate(camWidth,camHeight,OF_IMAGE_COLOR); }
08
:38
fb
.co
m/R
VA
.BR
91
void ofApp::update(){ cam.update(); float area; if(cam.isFrameNew()){ //ofLogNotice("[PAPER DETECTOR]") << "New Frame"; vector<cv::Point> maxQuad;//maior quadrado na imagem = folha de papel. float maxArea = -numeric_limits<float>::infinity();//escolhe pela maior área. contourFinder.findContours(cam); // realiza a procura por folhas brancas em um fundo preto int n = contourFinder.size(); for(int i = 0; i < n; i++){ //ofLogNotice("[PAPER DETECTOR]") << "update: " + ofToString(i); ofPolyline minAreRect = toOf(contourFinder.getMinAreaRect(i)); vector<cv::Point> quad = contourFinder.getFitQuad(i); area = contourFinder.getContourArea(i); //encontrar o maior quadrado na imagem = folha de papel. if (area > maxArea) { //ofLogNotice("[PAPER DETECTOR]") << "1. " + ofToString(area) + " - " + ofToString(maxArea); maxArea = area; maxQuad = quad; } }
08
:38
fb
.co
m/R
VA
.BR
92
// teste se foi encontrado algo parecido com um retângulo. this->foundInfra = false; if ( this->findForm(maxQuad) == "RECTANGLE"){ this->foundInfra = true; // warp paper vector<Point2f> warpPoints; warpPoints.push_back(maxQuad[3]); // para organizar a ordem dos pontos. warpPoints.push_back(maxQuad[0]); warpPoints.push_back(maxQuad[1]); warpPoints.push_back(maxQuad[2]); //copy(maxQuad.begin(), maxQuad.end(), back_inserter(warpPoints)); unwarpPerspective(cam, unwarped, warpPoints); unwarped.update(); } //ofLogNotice("[PAPER DETECTOR]") << "2. " + ofToString(area) + " - " + ofToString(maxArea) + " - " + ofToString(this->foundInfra); } }
08
:38
fb
.co
m/R
VA
.BR
93
void ofApp::draw(){ ofSetColor(ofColor::white); cam.draw(0,0); ofNoFill(); int n = contourFinder.size(); for(int i = 0; i < n; i++) { ofSetColor(ofColor::red); // smallest rectangle that fits the contour ofPolyline minAreRect = toOf(contourFinder.getMinAreaRect(i)); minAreRect.draw(); ofSetColor(yellowPrint); // convex hull of the contour ofPolyline convexHull = toOf(contourFinder.getConvexHull(i)); convexHull.draw(); ofSetColor(cyanPrint); vector<cv::Point> quad = contourFinder.getFitQuad(i); vector<cv::Point>::iterator it; for (it = quad.begin(); it != quad.end(); it++) ofCircle(toOf(*it),3); } ofSetColor(255); unwarped.draw(640, 0); if(this->foundInfra){ ofDrawBitmapStringHighlight("Found infra",10,ofGetHeight() - 80);} ofDrawBitmapStringHighlight(ofToString(this->thresholdCV),10,ofGetHeight() - 100); }
08
:38
fb
.co
m/R
VA
.BR
94
Resultado Esperado
08
:38
fb
.co
m/R
VA
.BR
95
WHERE IS MY HAND? DETECTING THE ACTUATOR
Detectando a mão, caneta, ou qualquer outra coisa (medo) que você queira
usar para fazer interação.
08
:38
fb
.co
m/R
VA
.BR
96
Rastrear mão (apontador 2D)
• Restrições (para facilitar)
Fundo homogêneo
Longe das quinas
Uma mão só
• Pseudo-código
• Fazer diferença entra o fundo e o frame atual para encontrar a mão.
• Com a diferença rastreia o blob.
• Do blob calcula as quinas
• Das quinas pega a mais distante do centroide e que não seja próximo das quinas ( braço).
08
:38
fb
.co
m/R
VA
.BR
97
Lets code.... Babe!!!! #include "ofMain.h"
#include "ofxOpenCv.h" //Cabeçalho do OpenCV
#include "ofxCv.h"
class ofApp : public ofBaseApp{
...
int width, height, threshold;
ofVideoGrabber vidGrabber; // Componente do oF que pega a câmera.
ofxCvColorImage colorImage; // imagem capturada pela câmera
ofxCvGrayscaleImage grayBg, grayImage, grayDiff;// bg, cinza, diferença
bool bLearnBackground;
ofxCv::TrackingColorMode trackingColorMode;
ofxCv::ContourFinder contourFinder;
ofColor targetColor;
ofPoint apontador;
ofPoint encontraPontoMaisDistante();
};
08
:38
fb
.co
m/R
VA
.BR
98
Tipo: ofPoint
• ofPoint é um tipo muito utilizado no openFrameworks que
faz as vezes de um ponto na área da janela.
• Com ele podemos posicionar pontos na área da janela.
• Essencialmente é um vetor X,Y,Z. Herda de ofVec3f
• Possui propriedades sobrecarregadas de operação de vetores.
08
:38
fb
.co
m/R
VA
.BR
99
Setup void ofApp::setup(){
width = 320; height = 240;
vidGrabber.initGrabber(width, height); //abre câmera
colorImage.allocate(width,height); //aloca memória para as imagens
grayBg.allocate(width,height);
grayImage.allocate(width,height);
grayDiff.allocate(width,height);
contourFinder.setMinAreaRadius(10); //configura rastreador
contourFinder.setMaxAreaRadius(150);
contourFinder.setTargetColor(ofColor::white, TRACK_COLOR_RGB);
threshold = 50;
bLearnBakground = true;
}
08
:38
fb
.co
m/R
VA
.BR
100
Comparando imagens
08
:38
fb
.co
m/R
VA
.BR
101
Update void ofApp::update(){
vidGrabber.update(); // Pega frame da câmera
if(vidGrabber.isFrameNew()){ // é um frame novo ???
colorImage.setFromPixels(vidGrabber.getPixels(),width,height);
grayImage = colorImage;
if (bLearnBackground == true){
grayBg = grayImage;// salva o fundo da tela - TO ROUBANDO MESMO!
bLearnBackground = false;}
grayDiff.absDiff(grayBg, grayImage); // fazer fundo x atual
grayDiff.threshold(threshold);
contourFinder.setThreshold(threshold); // procurar blobs no atual
contourFinder.findContours(grayDiff);
apontador = encontraPontoMaisDistante(); // procurar “dedo”
}
}
08
:38
fb
.co
m/R
VA
.BR
102
Marcando dedos (pontos distantes)
08
:38
fb
.co
m/R
VA
.BR
103
Ponto mais distante – p1
ofPoint ofApp::encontraPontoMaisDistante(){
int n = contourFinder.size(); //qtidade de blobs capturados
ofPoint maisDistante ; //nosso ponto mais distante
for(int i = 0; i < n; i++) {
ofVec2f centroid = toOf(contourFinder.getCentroid(i)); //Centroide;
ofPolyline convexHull = toOf(contourFinder.getConvexHull(i)); //quinas;
vector<ofPoint> vertices = convexHull.getVertices(); // vetorDeQuinas
vector<ofPoint>::iterator itVec; // para percorrer as quinas.
maisDistante = (*vertices.begin());
float tamMaisDistante = centroid.distanceSquared(maisDistante);
float distanciaAtual = 0;
08
:38
fb
.co
m/R
VA
.BR
104
Ponto mais distante – p2
for(itVec = vertices.begin(); itVec != vertices.end(); itVec++){
//encontrar ponto mais distante do dedo --> eliminar as bordas
if( ((*itVec).x > 40) && ((*itVec).x < width - 40) &&
((*itVec).y > 40) && ((*itVec).y < height - 40)){ //elimina bordas
distanciaAtual = centroid.distanceSquared((*itVec));
if(distanciaAtual > tamMaisDistante){ //maior “simple as possible”
maisDistante = (*itVec);
tamMaisDistante = distanciaAtual;
}
}
}
}
return maisDistante;
}
08
:38
fb
.co
m/R
VA
.BR
105
Draw
void ofApp::draw(){
ofSetColor(255); grayImage.draw(0, 0); // desenha câmera
ofTranslate(320, 0); grayDiff.draw(0, 0); // desenha threshold
ofTranslate(-320,240); contourFinder.draw(); //desenha contorno encontrado
ofSetColor(ofColor::hotPink);
ofDrawBitmapString("openCV Threshold: " + ofToString(threshold),50,50);
ofTranslate(0,-240);
ofFill();
ofSetColor(ofColor::green);
ofCircle(apontador,5); //desenha bola verde no “dedo”
}
08
:38
fb
.co
m/R
VA
.BR
106
Resultado desejado:
08
:38
fb
.co
m/R
VA
.BR
107
PLAYING IN THE AIR!
Interagindo em elementos virtuais com a mão.
08
:38
fb
.co
m/R
VA
.BR
108
Definindo um conjunto de ações • Assunto: Controlar um Arduino • Ações:
• Acender e Apagar um LED via Arduino, com replicação virtual, via botão físico e botão virtual.
• Controlar um servo motor a partir de uma barra virtual.
Dica: Esse é um bom algoritmo para fazer o debounce do toque: http://drmarty.blogspot.com.br/2009/05/best-switch-debounce-routine-ever.html
08
:38
fb
.co
m/R
VA
.BR
109
Espalhando controles virtuais pela área capturada pela câmera
• 1º Projeto de Interface (livre em toda a área)
“É você Google Glass? É voce Hololens?” (Clotilde, 1985)
08
:38
fb
.co
m/R
VA
.BR
110
Tipo: ofRectangle
• Tipo que define um retângulo na tela.
• Retângulos são muito usados para definir áreas de toque. (Forma mais simples)
• O tipo ofRectangle possui:
• X,Y,(Z) do canto superior esquerdo (Referência)
• Height, Width
• Operadores sobrecarregados
• Varias funções de posicionamento
• Adição, crescimento para englobar outros, etc etc etc.
08
:38
fb
.co
m/R
VA
.BR
111
Some code... (testar toques? inside() )
ofRectangle controleServo;
ofRectangle chaveOff;
ofRectangle chaveOn;
ofPoint LEDIndicativo;
ofColor ledColor;
bool ledStatus;
// Configurar posição dos
elementos da interface
controleServo.set(30,85,40,140);
chaveOff.set(197,160,40,40);
chaveOn.set(237,160,40,40);
LEDIndicativo.set(160,50);
if( this->controleServo.inside(dedo) ) {
// trata para controle doservo}
if( this->chaveOn.inside(dedo)) {
// trata evento do botão On}
if( this->chaveOff.inside(dedo)) {
// trata evento do botão Off}
ofApp.h ofApp.cpp – setup()
ofApp.cpp – update()
08
:38
fb
.co
m/R
VA
.BR
112
Resultado desejado
08
:38
fb
.co
m/R
VA
.BR
113
Adicionando comandos e leitura do Arduino
// Setup Arduino() ard.sendDigitalPinMode(2, ARD_INPUT); // led do exemplo ard.sendDigitalPinMode(11, ARD_OUTPUT); // botão ard.sendServoAttach(9); // servo ofAddListener(ard.EDigitalPinChanged, this, &ofApp::digitalPinChanged);
void ofApp::digitalPinChanged(const int & pinNum) { this->ledStatus = ard.getDigital(pinNum); if ( this->ledStatus == true){ ard.sendDigital(11,ARD_HIGH); } else { ard.sendDigital(11,ARD_LOW); } }
08
:38
fb
.co
m/R
VA
.BR
114
bool ofApp::updateInterface(ofPoint dedo){ if( this->controleServo.inside(dedo) ) { // trata evento com servo // |controleServo.y ----- dedo.y ---- controleServo.y + controleServo.height| // |0 --------------------- y --------------------------- 180 | positionServo = (int) (dedo.y - controleServo.y)*180/controleServo.height; ard.sendServo(9, positionServo, false); } if( this->chaveOn.inside(dedo)) { // trata evento com botão On ledStatus = 1; ard.sendDigital(11,ARD_HIGH); // envia comando pro arduino } if( this->chaveOff.inside(dedo)) { // trata evento com botão Off ledStatus = 0; ard.sendDigital(11,ARD_LOW); //envia comando para o arduino } // pega dado do status do led this->ledStatus == 1 ? this->ledColor = ofColor::green : this->ledColor = ofColor::white; return true; }
08
:38
fb
.co
m/R
VA
.BR
115
TRACKING PHYSICAL CONTROLS. THINGS ARE GETING REAL
Rastreando controles desenhados na infraestrutura
08
:38
fb
.co
m/R
VA
.BR
116
*Versão projetada
08
:38
fb
.co
m/R
VA
.BR
117
Pseudo Código
• Encontrada a infraestrutura:
• Update:
1. Processar imagem
2. Procurar controles 1. Procurar tipo Slide
2. Procurar tipo Switch
3. Procurar tipo Button
3. Tendo os controles 1. Associar um controle a uma função predefinida de uma paleta de
controles
• Draw:
1. Desenhar paleta de funções num canto da infraestrutura
2. Desenhar controles encontrados (Forma e status) 1. Ajustar ponto de vista!!! (mesmo usando vista superior, em caso de
projeção é necessário corrigir, pois câmera e projetor não estão no mesmo ponto)
08
:38
fb
.co
m/R
VA
.BR
118
Processamento de Imagem
Infra
08
:38
fb
.co
m/R
VA
.BR
119
Etapas do processamento
2. Canny
3. Dilate
4. Erode
6. contourFinder
infra 08
:38
fb
.co
m/R
VA
.BR
120
No “ofApp.h” // Controles ofxCv::ContourFindercontrolFinder; booldoControlDetection; // auxiliares para forçar as imagens: cv::Mat grayImg; cv::Mat canny; cv::Mat eroded; cv::Mat dilated; cv::Mat edgesInput; cv::Mat edges; int BORDER; float ALPHA; vector<Controle> listaDeControles; voidtoogleControlDetection() { doControlDetection == true ? doControlDetection = false : doControlDetection = true; }; void encontraControles(); void processaInteracao(); void desenhaControles();
08
:38
fb
.co
m/R
VA
.BR
121
Processamento da imagem void ofApp::encontraControles(){ Mat img = ofxCv::toCv(this->unwarped); // Get the image in the right format and run edge detection - cheat - tricks ofxCv::convertColor(img, grayImg, CV_RGB2GRAY); //converte a imagem para cinza cv::Canny(grayImg, edgesInput, 160, 180, 3); // extrapola as bordas - edge map canny = edgesInput; cv::dilate(edgesInput, edgesInput, Mat::ones(2, 2, CV_8U), cv::Point(-1, -1), 7); //dilata as bordas dilated = edgesInput; cv::erode(edgesInput, edgesInput, Mat::ones(2, 2, CV_8U), cv::Point(-1, -1), 5); // erode as bordas eroded = edgesInput; const cv::Rect &roi = cv::Rect(BORDER, BORDER, edgesInput.cols - 2*BORDER, edgesInput.rows - 2*BORDER); edgesInput(roi).copyTo(edges); // Find contours in the edge detected image controlFinder.findContours(edges); //... continues }
08
:38
fb
.co
m/R
VA
.BR
122
Procurar Controles
08
:38
fb
.co
m/R
VA
.BR
123
Tipos de Controles:
Switch – Posição 0 ou 1, memoriza a função
Button – Posição 0 ou 1, instantâneo, funciona enquanto clicado, depois perde o valor
Slide - Valor variável entre um máx. e um mín.
08
:38
fb
.co
m/R
VA
.BR
124
Identificação dos tipos de controles
08
:38
fb
.co
m/R
VA
.BR
125
Estrutura de dados genérica (super-hiper-mega simplificada)
enum controlType{ CT_SWITCH, CT_BUTTON, CT_SLIDE };
class Controle { public: int id; controlType type; int value; int max; int min; //Para desenhar: ofPoint pos; ofRectangle posRect; float radius; float angle; void draw(){}; //para preencher os campos void fillSlider( cv::RotatedRect rotRect){} void fillSwitch( cv::RotatedRect rotRect){}; void fillButton( cv::Point center, float radius){}; bool interact(ofPoint dedo); };
Ideal é que sejam feitas classes e polimorfismo de tudo.
class Class Model
ofBaseApp
ofApp
+ par :ofxProjAR
+ setup() :void
+ update() :void
+ draw() :void
+ keyPressed(int) :void
+ keyReleased(int) :void
+ mouseMoved(int, int) :void
+ mouseDragged(int, int, int) :void
+ mousePressed(int, int, int) :void
+ mouseReleased(int, int, int) :void
+ windowResized(int, int) :void
+ dragEvent(ofDragInfo) :void
+ gotMessage(ofMessage) :void
ofxProjAR
+ m_infra :ofxPAR_Infra*
+ m_actuator :ofxPAR_Actuator*
+ controlManager :ofxPAR_ControlManager
+ debug :bool
- actualState :ofxPAR_State*
- stateList :vector<ofxPAR_State*>
+ ofxProjAR()
+ ~ofxProjAR()
+ standardSetup() :void
+ standardUpdate() :void
+ standardDraw() :void
+ setState(AppState) :bool
+ getActualState() :ofxPAR_State*
ofxPAR_Infra
+ cam :ofVideoGrabber
+ camHeight :int
+ camWidth :int
+ unwarped :ofImage
+ showVideoFeed :bool
+ foundInfra :bool
+ projectorPoints :vector<cv::Point2f>
+ toProjectorMatrix :cv::Mat
+ alignmentComplete :bool
+ projWidth :int
+ projHeight :int
# name :string
+ ofxPAR_Infra()
+ ~ofxPAR_Infra()
+ detect() :void
+ draw() :void
+ update() :void
+ setup() :void
+ getTransformation(int, int) :cv::Mat
+ getContours() :ofPolyline
+ unwarpPoint(ofPoint&, int, int) :ofPoint
+ getCamera() :ofVideoGrabber
+ setCamera(ofVideoGrabber) :void
+ setCamera(int, int) :void
+ getRawUnwarped() :ofImage
+ toogleVideoFeed() :void
+ isInfraFound() :bool
+ addCalibrationPoints(int, int) :void
+ resetCalibration() :void
+ doCalibration() :void
+ drawProjectionLimits() :void
# computeProjectorAlignment() :void
# loadProjectorAlignment() :bool
# saveProjectorAlignment() :bool
# resetProjectorAlignment() :void
ofxPAR_Infra_PaperDetector
# trackingColorMode :ofxCv::TrackingColorMode
# contourFinder :ofxCv::ContourFinder
# targetColor :ofColor
# thresholdCV :int
# contour :ofPolyline
# paperImage :cv::Mat
# paper :vector<cv::Point>
# adaptedToScreenSize :vector<cv::Point>
+ ofxPAR_Infra_PaperDetector()
+ ~ofxPAR_Infra_PaperDetector()
+ draw() :void
+ detect() :void
+ calibrate() :void
+ update() :void
+ setup() :void
+ getTransformation(int, int) :cv::Mat
+ getContours() :ofPolyline
+ unwarpPoint(ofPoint&, int, int) :ofPoint
- configureCV() :void
- drawPaperContour() :void
- drawPaperContourIn(int, int) :void
- unwarp(S&, D&) :void
- unwarp(D&) :void
- unwarpPointIntern(ofPoint&, int, int) :ofPoint
ofxPAR_Actuator
+ topBackground :ofxCv::RunningBackground
+ foreground :cv::Mat
+ ofxPAR_Actuator()
+ ~ofxPAR_Actuator()
+ draw() :void
+ drawDetectorInput(float, float, float, float) :void
+ detect(T&, S&, ofPolyline&) :bool
+ detect(cv::Mat&, ofPolyline&) :bool
+ getActuatorPoint() :ofPoint
+ setActuatorThresold(int) :void
ofxPAR_HandDetector
- topFinder :ofxCv::ContourFinder
- sideFinder :ofxCv::ContourFinder
- topFilled :cv::Mat
- contour :ofPolyline
- fingers :vector<size_t>
- fingerPoint :ofPoint
- foundFingerInLast :bool
- fingerThreshold :int
- fAlpha :float
- elem2x2 :cv::Mat
- elem3x3 :cv::Mat
- useCenter :cv::Point {readOnly}
+ ofxPAR_HandDetector()
+ ~ofxPAR_HandDetector()
+ draw() :void
+ drawDetectorInput(float, float, float, float) :void
+ detect(cv::Mat&, ofPolyline&) :bool
+ getActuatorPoint() :ofPoint
+ setActuatorThresold(int) :void
- findFingers(ofPolyline&) :bool
- getFingerPoint() :ofPoint
ofxPAR_ControlManager
+ doControlDetection :bool
+ accent1 :ofColor {readOnly}
+ accent2 :ofColor {readOnly}
+ finder :ofxCv::ContourFinder
+ grayImg :cv::Mat
+ canny :cv::Mat
+ eroded :cv::Mat
+ dilated :cv::Mat
+ edgesInput :cv::Mat
+ edges :cv::Mat
+ controlList :vector<ofxPAR_Control *>
+ lastInputPoint :ofPoint
+ BORDER :int
+ ALPHA :float
+ ofxPAR_ControlManager()
+ ~ofxPAR_ControlManager()
+ setup() :void
+ reset() :void
+ drawDetectorInput(float, float, float, float) :void
+ detect(T&) :void
+ detect(cv::Mat) :void
+ toogleControlDetection() :void
+ processInteraction(ofPoint&) :void
+ drawControls() :void
+ drawDebug() :void
+ assignControls() :void
+ isButton(size_t) :bool
+ isSlider(size_t) :bool
+ isSwitch(size_t) :bool
ofxPAR_Control
# id :int
# name :string
# value :int
# color :ofColor
# controlType :ControlType
# selected :bool
+ ofxPAR_Control()
+ ~ofxPAR_Control()
+ setId(int) :void
+ draw() :void
+ setColor(ofColor&) :void
+ contains(float, float) :bool
+ contains(ofPoint&) :bool
+ onInteraction(float, float) :bool
+ onInteraction(ofPoint&) :bool
+ setSelection(bool) :void
+ isSelected() :bool
+ getValue() :int
ofxPAR_C_Button
- cx :float
- cy :float
- radius :float
- active :bool
- entered :bool
+ ofxPAR_C_Button()
+ ~ofxPAR_C_Button()
+ ofxPAR_C_Button(float, float, float)
+ draw() :void
+ contains(float, float) :bool
+ onInteraction(float, float) :bool
+ operator==(ofxPAR_C_Button&) :bool
+ operator!=(ofxPAR_C_Button&) :bool
ofxPAR_C_Slider
- value :float
+ ofxPAR_C_Slider()
+ ~ofxPAR_C_Slider()
+ ofxPAR_C_Slider(cv::RotatedRect&)
+ draw() :void
+ onInteraction(float, float) :bool
ofxPAR_C_Switch
- active :bool
+ ofxPAR_C_Switch()
+ ~ofxPAR_C_Switch()
+ ofxPAR_C_Switch(cv::RotatedRect&)
+ draw() :void
+ onInteraction(float, float) :bool
ofxPAR_State
+ stateType :AppState
+ name :string
+ app :ofxProjAR*
+ ofxPAR_State()
+ ~ofxPAR_State()
+ draw() :void
+ update() :void
ofxPAR_State_Idle
+ ofxPAR_State_Idle()
+ ~ofxPAR_State_Idle()
+ draw() :void
+ update() :void
ofxPAR_State_InfraSetup
+ ofxPAR_State_InfraSetup()
+ ~ofxPAR_State_InfraSetup()
+ draw() :void
+ update() :void
ofxPAR_State_Play
+ ofxPAR_State_Play()
+ ~ofxPAR_State_Play()
+ draw() :void
+ update() :void
- reprojectPaper() :void
ofxPAR_Control_Rectangular
# angle :float
# rect :ofRectangle
+ ofxPAR_Control_Rectangular()
+ ~ofxPAR_Control_Rectangular()
+ ofxPAR_Control_Rectangular(cv::RotatedRect&)
+ draw() :void
+ contains(float, float) :bool
+ operator==(ofxPAR_Control_Rectangular&) :bool
+ operator!=(ofxPAR_Control_Rectangular&) :bool
# alignPoint(float, float) :ofVec2f
+app
08
:38
fb
.co
m/R
VA
.BR
126
Tipo: vector (de classes)
• Vector é uma estrutura para armazenar sequencialmente.
• No C++ vários tipos de estruturas de dados foram definidas usando containers genéricos. (STL)
vector<Controle> listaDeControles;
• Adição: Controle a;
listaDeControles.push_back(a);
• Remoção: listaDeControles.erase( listaDeControles.begin() + x);
• Percorrer:
vector<Controle>::iterator it = listaDeControles.begin();
for(; it != listaDeControles.end(); it++){
it->id; } // se vetor de ponteiros (*it)->
http://www.openframeworks.cc/tutorials/c++%20concepts/001_stl_vectors_basic.html
vector<Controle*> listaDeControles; 08:3
8
fb.c
om
/RV
A.B
R
127
//... Continuing - void ofApp::encontraControles() listaDeControles.clear(); int n = controlFinder.size(); for(int i = 0; i< n; i++){
if (isButton(i)) { float radius; cv::Point center = controlFinder.getMinEnclosingCircle(i, radius); Controle *b = new Controle(); b->id = i; b->fillButton(center, radius); listaDeControles.push_back(b);
} else if (isSlider(i)) { cv::RotatedRect rect = controlFinder.getMinAreaRect(i); Controle *s = new Controle(); s->id = i; s->fillSlider(rect); listaDeControles.push_back(s);
} else if (isSwitch(i)) { cv::RotatedRect rect = controlFinder.getMinAreaRect(i); Controle *s = new Controle(); s->id = 1; s->fillSwitch(rect); listaDeControles.push_back(s); } } }
08
:38
fb
.co
m/R
VA
.BR
128
//--------------------------------------------------------- bool ofApp::isButton(int i) { float radius; controlFinder.getMinEnclosingCircle(i, radius); float circleArea = PI * radius * radius; double contourArea = controlFinder.getContourArea(i); return abs(1.0 - circleArea / contourArea) < 0.3; } //--------------------------------------------------------- bool ofApp::isSlider(int i) { float radius; ofVec2f center = ofxCv::toOf(controlFinder.getMinEnclosingCircle(i, radius)); float circleArea = PI * radius * radius; double contourArea = controlFinder.getContourArea(i); return circleArea / contourArea > 12; } //--------------------------------------------------------- bool ofApp::isSwitch(int i) { RotatedRect rr = controlFinder.getMinAreaRect(i); float rectArea = rr.size.width * rr.size.height; double contourArea = controlFinder.getContourArea(i); return abs(1.0 - rectArea / contourArea) < 0.25 && abs(1.0 - (float) rr.size.width / rr.size.height) > 0.1; }
08
:38
fb
.co
m/R
VA
.BR
129
Cri
ar o
s co
ntr
ole
s -F
YI
void fillSlider( cv::RotatedRect rotRect){
this->type = controlType::CT_SLIDE;
const cv::Size &s = rotRect.size;
bool switchHeight = s.width < s.height;
if (switchHeight) {
this->posRect.setFromCenter(ofxCv::toOf(rotRect.center), s.height, s.width);
angle = rotRect.angle + 90;
} else {
this->posRect.setFromCenter(ofxCv::toOf(rotRect.center), s.width, s.height);
angle = rotRect.angle; }
this->value = 0;
};
void fillSwitch( cv::RotatedRect rotRect){
this->type = controlType::CT_SWITCH;
const cv::Size &s = rotRect.size;
bool switchHeight = s.width < s.height;
if (switchHeight) {
this->posRect.setFromCenter(ofxCv::toOf(rotRect.center), s.height, s.width);
angle = rotRect.angle + 90;
} else {
this->posRect.setFromCenter(ofxCv::toOf(rotRect.center), s.width, s.height);
angle = rotRect.angle;
}
this->value = 0;
};
void fillButton( cv::Point center, float radius){
this->type = controlType::CT_BUTTON;
this->pos.x = center.x; this->pos.y = center.y; this->radius = radius;
this->value = 0;
};
08
:38
fb
.co
m/R
VA
.BR
130
Draw do botão:
case controlType::CT_BUTTON :{
ofPushMatrix();
ofSetColor(ofColor::green);
ofSetLineWidth(5);
if (this->value == 1) {
ofFill();
} else {
ofNoFill();
}
ofCircle(this->pos, this->radius);
ofPopMatrix();
break;}
08
:38
fb
.co
m/R
VA
.BR
131
case controlType::CT_SWITCH:{
ofPushMatrix();
ofTranslate(this->posRect.getCenter());
ofRotate(this->angle);
ofNoFill();
ofSetLineWidth(3);
ofSetColor(ofColor::blue);
ofRect(-this->posRect.width / 2, -this->posRect.height / 2, this->posRect.width, this->posRect.height);
ofLine(0, -this->posRect.height / 2, 0, this->posRect.height / 2);
float x = (this->value ? 0 : -this->posRect.width / 2);
ofLine(x, -this->posRect.height / 2, x + this->posRect.width / 2, this->posRect.height / 2);
ofLine(x + this->posRect.width / 2, -this->posRect.height / 2, x, this->posRect.height / 2);
ofPopMatrix();
break;}
Draw da Chave:
08
:38
fb
.co
m/R
VA
.BR
132
case controlType::CT_SLIDE:{
ofPushMatrix();
ofTranslate(this->posRect.getCenter());
ofRotate(this->angle);
ofNoFill();
ofSetLineWidth(4);
ofSetColor(ofColor::red);
// Draw the main shape
ofLine(-this->posRect.width / 2, 0, this->posRect.width / 2, 0);
ofLine(-this->posRect.width / 2, -this->posRect.height / 2, -this->posRect.width / 2, this->posRect.height / 2);
ofLine(this->posRect.width / 2, -this->posRect.height / 2, this->posRect.width / 2, this->posRect.height / 2);
// Draw the tick marks
ofSetLineWidth(2);
for (size_t i = 1; i < 8; i++) {
float dx = ((float) i / 8) * this->posRect.width;
ofLine(-this->posRect.width / 2 + dx, -this->posRect.height / 3, -this->posRect.width / 2 + dx, this->posRect.height / 3);
}
// Draw the value marker
float xval = ((float)this->value/100) * this->posRect.width;
float knobHeight = 5 * this->posRect.height / 4;
float knobWidth = this->posRect.width / 8;
// Fill the rectangle with black to erase the background
ofPushStyle();
ofSetColor(0);
ofFill();
ofRect(-this->posRect.width / 2 + xval - knobWidth / 2, -knobHeight / 2, knobWidth, knobHeight);
ofPopStyle();
ofSetLineWidth(5);
ofRect(-this->posRect.width / 2 + xval - knobWidth / 2, -knobHeight / 2, knobWidth, knobHeight);
ofPopMatrix();
break;}
Draw do Slider
08
:38
fb
.co
m/R
VA
.BR
133
FINAL LAP: INTERAÇÃO COM OS CONTROLES
08
:38
fb
.co
m/R
VA
.BR
134
Fixar os pontos de corte da figura!!!!!!!!!!!!!!
08
:38
fb
.co
m/R
VA
.BR
135
Inside!?
bool buttonContains(int x, int y){
return ofDistSquared((float)pos.x, (float)pos.y, (float)x, (float)y) < radius * radius;
}
bool rectContains(int x, int y){
return posRect.inside(x, y);
}
08
:38
fb
.co
m/R
VA
.BR
136
switch(type){
case controlType::CT_BUTTON :
if (buttonContains(dedo.x, dedo.y)) {
value == 1 ? value = 0 : value = 1;
return true; }
break;
case controlType::CT_SWITCH :
if(rectContains(dedo.x,dedo.y)) {
bool right = dedo.x > posRect.getCenter().x;
if ((bool)value != right) value = right;
return true;}
break;
case controlType::CT_SLIDE :
if(rectContains(dedo.x,dedo.y)){
value = (int) ((dedo.x - posRect.x)*100 / posRect.width);
return true;}
break;
default: break;
};
interaction
08
:38
fb
.co
m/R
VA
.BR
137
Resultado Esperado
08
:38
fb
.co
m/R
VA
.BR
138
WRAP-UP
08
:38
fb
.co
m/R
VA
.BR
139
O QUE APRENDEMOS HOJE
O QUE ACONTECEU:
class src
«enumeratio...
controlType
CT_SWITCH
CT_BUTTON
CT_SLIDE
Controle
+ id :int
+ type :controlType
+ value :int
+ max :int
+ min :int
+ pos :ofPoint
+ posRect :ofRectangle
+ radius :float
+ angle :float
+ fi l lSlider(rotRect :cv::RotatedRect) :void
+ fi l lSwitch(rotRect :cv::RotatedRect) :void
+ fi l lButton(center :cv::Point, radius :float) :void
+ draw() :void
+ buttonContains(x :int, y :int) :bool
+ rectContains(x :int, y :int) :bool
+ interact(dedo :ofPoint) :bool
ofBaseApp
ofApp
+ debugApp :bool
+ cam :ofVideoGrabber
+ camHeight :int
+ camWidth :int
+ original :ofImage
+ paperFinder :ofxCv::ContourFinder
+ warpedPaper :ofImage
+ verticesInfra :vector<Point2f>
+ foundInfra :bool
+ StopToGetInfraPosition :bool
+ controlFinder :ofxCv::ContourFinder
+ doControlDetection :bool
+ grayImg :cv::Mat
+ canny :cv::Mat
+ eroded :cv::Mat
+ dilated :cv::Mat
+ edgesInput :cv::Mat
+ edges :cv::Mat
+ BORDER :int
+ ALPHA :float
+ listaDeControles :vector<Controle*>
+ colorImage :ofxCvColorImage
+ grayBg :ofxCvGrayscaleImage
+ grayImage :ofxCvGrayscaleImage
+ grayDiff :ofxCvGrayscaleImage
+ bLearnBackground :bool
+ trackingColorMode :ofxCv::TrackingColorMode
+ handFinder :ofxCv::ContourFinder
+ targetColor :ofColor
+ apontador :ofPoint
+ setup() :void
+ update() :void
+ draw() :void
+ keyReleased(key :int) :void
+ trataInfraestrutura() :void
+ desenhaInfraestrutura(x :int, y :int) :void
+ encontraControles(source :ofImage) :void
+ isButton(i :int) :bool
+ isSwitch(i :int) :bool
+ isSlider(i :int) :bool
+ desenhaControles(x :int, y :int) :void
+ processaInteracao() :void
+ encontraPontoMaisDistante() :ofPoint
+ desenhaMao(x :int, y :int) :void
+ encontraMao(source :ofImage) :void
+ findForm(poly :ofPolyline&) :string
+ findForm(poly :T&) :string
+type
08
:38
fb
.co
m/R
VA
.BR
140
class Class Model
ofBaseApp
ofApp
+ par :ofxProjAR
+ setup() :void
+ update() :void
+ draw() :void
+ keyPressed(int) :void
+ keyReleased(int) :void
+ mouseMoved(int, int) :void
+ mouseDragged(int, int, int) :void
+ mousePressed(int, int, int) :void
+ mouseReleased(int, int, int) :void
+ windowResized(int, int) :void
+ dragEvent(ofDragInfo) :void
+ gotMessage(ofMessage) :void
ofxProjAR
+ m_infra :ofxPAR_Infra*
+ m_actuator :ofxPAR_Actuator*
+ controlManager :ofxPAR_ControlManager
+ debug :bool
- actualState :ofxPAR_State*
- stateList :vector<ofxPAR_State*>
+ ofxProjAR()
+ ~ofxProjAR()
+ standardSetup() :void
+ standardUpdate() :void
+ standardDraw() :void
+ setState(AppState) :bool
+ getActualState() :ofxPAR_State*
ofxPAR_Infra
+ cam :ofVideoGrabber
+ camHeight :int
+ camWidth :int
+ unwarped :ofImage
+ showVideoFeed :bool
+ foundInfra :bool
+ projectorPoints :vector<cv::Point2f>
+ toProjectorMatrix :cv::Mat
+ alignmentComplete :bool
+ projWidth :int
+ projHeight :int
# name :string
+ ofxPAR_Infra()
+ ~ofxPAR_Infra()
+ detect() :void
+ draw() :void
+ update() :void
+ setup() :void
+ getTransformation(int, int) :cv::Mat
+ getContours() :ofPolyline
+ unwarpPoint(ofPoint&, int, int) :ofPoint
+ getCamera() :ofVideoGrabber
+ setCamera(ofVideoGrabber) :void
+ setCamera(int, int) :void
+ getRawUnwarped() :ofImage
+ toogleVideoFeed() :void
+ isInfraFound() :bool
+ addCalibrationPoints(int, int) :void
+ resetCalibration() :void
+ doCalibration() :void
+ drawProjectionLimits() :void
# computeProjectorAlignment() :void
# loadProjectorAlignment() :bool
# saveProjectorAlignment() :bool
# resetProjectorAlignment() :void
ofxPAR_Infra_PaperDetector
# trackingColorMode :ofxCv::TrackingColorMode
# contourFinder :ofxCv::ContourFinder
# targetColor :ofColor
# thresholdCV :int
# contour :ofPolyline
# paperImage :cv::Mat
# paper :vector<cv::Point>
# adaptedToScreenSize :vector<cv::Point>
+ ofxPAR_Infra_PaperDetector()
+ ~ofxPAR_Infra_PaperDetector()
+ draw() :void
+ detect() :void
+ calibrate() :void
+ update() :void
+ setup() :void
+ getTransformation(int, int) :cv::Mat
+ getContours() :ofPolyline
+ unwarpPoint(ofPoint&, int, int) :ofPoint
- configureCV() :void
- drawPaperContour() :void
- drawPaperContourIn(int, int) :void
- unwarp(S&, D&) :void
- unwarp(D&) :void
- unwarpPointIntern(ofPoint&, int, int) :ofPoint
ofxPAR_Actuator
+ topBackground :ofxCv::RunningBackground
+ foreground :cv::Mat
+ ofxPAR_Actuator()
+ ~ofxPAR_Actuator()
+ draw() :void
+ drawDetectorInput(float, float, float, float) :void
+ detect(T&, S&, ofPolyline&) :bool
+ detect(cv::Mat&, ofPolyline&) :bool
+ getActuatorPoint() :ofPoint
+ setActuatorThresold(int) :void
ofxPAR_HandDetector
- topFinder :ofxCv::ContourFinder
- sideFinder :ofxCv::ContourFinder
- topFilled :cv::Mat
- contour :ofPolyline
- fingers :vector<size_t>
- fingerPoint :ofPoint
- foundFingerInLast :bool
- fingerThreshold :int
- fAlpha :float
- elem2x2 :cv::Mat
- elem3x3 :cv::Mat
- useCenter :cv::Point {readOnly}
+ ofxPAR_HandDetector()
+ ~ofxPAR_HandDetector()
+ draw() :void
+ drawDetectorInput(float, float, float, float) :void
+ detect(cv::Mat&, ofPolyline&) :bool
+ getActuatorPoint() :ofPoint
+ setActuatorThresold(int) :void
- findFingers(ofPolyline&) :bool
- getFingerPoint() :ofPoint
ofxPAR_ControlManager
+ doControlDetection :bool
+ accent1 :ofColor {readOnly}
+ accent2 :ofColor {readOnly}
+ finder :ofxCv::ContourFinder
+ grayImg :cv::Mat
+ canny :cv::Mat
+ eroded :cv::Mat
+ dilated :cv::Mat
+ edgesInput :cv::Mat
+ edges :cv::Mat
+ controlList :vector<ofxPAR_Control *>
+ lastInputPoint :ofPoint
+ BORDER :int
+ ALPHA :float
+ ofxPAR_ControlManager()
+ ~ofxPAR_ControlManager()
+ setup() :void
+ reset() :void
+ drawDetectorInput(float, float, float, float) :void
+ detect(T&) :void
+ detect(cv::Mat) :void
+ toogleControlDetection() :void
+ processInteraction(ofPoint&) :void
+ drawControls() :void
+ drawDebug() :void
+ assignControls() :void
+ isButton(size_t) :bool
+ isSlider(size_t) :bool
+ isSwitch(size_t) :bool
ofxPAR_Control
# id :int
# name :string
# value :int
# color :ofColor
# controlType :ControlType
# selected :bool
+ ofxPAR_Control()
+ ~ofxPAR_Control()
+ setId(int) :void
+ draw() :void
+ setColor(ofColor&) :void
+ contains(float, float) :bool
+ contains(ofPoint&) :bool
+ onInteraction(float, float) :bool
+ onInteraction(ofPoint&) :bool
+ setSelection(bool) :void
+ isSelected() :bool
+ getValue() :int
ofxPAR_C_Button
- cx :float
- cy :float
- radius :float
- active :bool
- entered :bool
+ ofxPAR_C_Button()
+ ~ofxPAR_C_Button()
+ ofxPAR_C_Button(float, float, float)
+ draw() :void
+ contains(float, float) :bool
+ onInteraction(float, float) :bool
+ operator==(ofxPAR_C_Button&) :bool
+ operator!=(ofxPAR_C_Button&) :bool
ofxPAR_C_Slider
- value :float
+ ofxPAR_C_Slider()
+ ~ofxPAR_C_Slider()
+ ofxPAR_C_Slider(cv::RotatedRect&)
+ draw() :void
+ onInteraction(float, float) :bool
ofxPAR_C_Switch
- active :bool
+ ofxPAR_C_Switch()
+ ~ofxPAR_C_Switch()
+ ofxPAR_C_Switch(cv::RotatedRect&)
+ draw() :void
+ onInteraction(float, float) :bool
ofxPAR_State
+ stateType :AppState
+ name :string
+ app :ofxProjAR*
+ ofxPAR_State()
+ ~ofxPAR_State()
+ draw() :void
+ update() :void
ofxPAR_State_Idle
+ ofxPAR_State_Idle()
+ ~ofxPAR_State_Idle()
+ draw() :void
+ update() :void
ofxPAR_State_InfraSetup
+ ofxPAR_State_InfraSetup()
+ ~ofxPAR_State_InfraSetup()
+ draw() :void
+ update() :void
ofxPAR_State_Play
+ ofxPAR_State_Play()
+ ~ofxPAR_State_Play()
+ draw() :void
+ update() :void
- reprojectPaper() :void
ofxPAR_Control_Rectangular
# angle :float
# rect :ofRectangle
+ ofxPAR_Control_Rectangular()
+ ~ofxPAR_Control_Rectangular()
+ ofxPAR_Control_Rectangular(cv::RotatedRect&)
+ draw() :void
+ contains(float, float) :bool
+ operator==(ofxPAR_Control_Rectangular&) :bool
+ operator!=(ofxPAR_Control_Rectangular&) :bool
# alignPoint(float, float) :ofVec2f
+app
IDEAL:
08
:38
fb
.co
m/R
VA
.BR
141
• Existe um framework em C++ chamado openFrameworks. Novo player interessante: Unreal Engine + Blueprints (AWESOME)
• openFrameworks pode ser expandido com componentes da comunidade, chamados addons.
• Com ele é possível controlar o Arduino (em C++).
• O Arduino pode se transformar numa interface de
dados usando a lib Firmata.
• Arduino é barato e fácil de usar. Um possível software para desenhar circuitos é o Fritzing
•Realidade Aumentada é só uma interface de acesso aos elementos virtuais.
08
:38
fb
.co
m/R
VA
.BR
142
• openCV é uma biblioteca de visão computacional.
• Com openCV podemos rastrear cores, diferenças de
imagens, etc.
• Pense se sua aplicação pode usar equipamentos
simples!
• Quanto mais simples e do cotidiano do usuário melhor.
• Visão computacional nem sempre é um resultado por si.
Ideal são soluções hibridas.
• Adicionando elementos de interação físicos (sensores e atuadores) à Realidade Aumentada, que fazem a troca
bidirecional de informação cria-se Realidade Cruzada.
08
:38
fb
.co
m/R
VA
.BR
143
Dúvidas: [email protected] Site: http://cscerqueira.com.br
Facebook: http://fb.com/RVA.BR
Para maiores dúvidas: INPE – SJC
Prédio Satélite Sala 95
Drª. Ana Maria Ambrosio
Msc. Christopher Cerqueira
aluno doutorado
Dr. Claudio Kirner
08
:38
fb
.co
m/R
VA
.BR
145