Skip to content

Instantly share code, notes, and snippets.

@prisonerjohn
Last active November 18, 2019 04:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save prisonerjohn/53124f25130a9ffe336560ca21398cba to your computer and use it in GitHub Desktop.
Save prisonerjohn/53124f25130a9ffe336560ca21398cba to your computer and use it in GitHub Desktop.
Sensing Machines Homography
#include "ezBall.h"
#include "ofApp.h"
void ezBall::setup(int x, int y)
{
pos = glm::vec2(x, y);
mass = ofRandom(10, 30);
color = ofColor(ofRandom(127, 255), ofRandom(127, 255), ofRandom(127, 255));
}
void ezBall::update(glm::vec2 force)
{
// Add force.
acc += force / mass;
if (glm::length(vel) > 0)
{
// Add friction.
glm::vec2 friction = glm::normalize(vel * -1) * 0.01f;
acc += friction;
}
// Apply acceleration, then reset it!
vel += acc;
acc = glm::vec2(0.0f);
// Move object.
pos += vel;
// Bounce off walls, taking radius into consideration.
if (pos.x - mass < 0 || pos.x + mass > PROJECTOR_RESOLUTION_X)
{
pos.x = ofClamp(pos.x, mass, PROJECTOR_RESOLUTION_X - mass);
vel.x *= -1;
}
if (pos.y - mass < 0 || pos.y + mass > PROJECTOR_RESOLUTION_Y)
{
pos.y = ofClamp(pos.y, mass, PROJECTOR_RESOLUTION_Y - mass);
vel.y *= -1;
}
}
void ezBall::draw()
{
ofSetColor(color);
ofDrawCircle(pos, mass);
}
#pragma once
#include "ofMain.h"
class ezBall
{
public:
void setup(int x, int y);
void update(glm::vec2 force);
void draw();
private:
glm::vec2 pos;
glm::vec2 vel;
glm::vec2 acc;
float mass;
ofColor color;
};
#include "ofMain.h"
#include "ofApp.h"
int main()
{
ofGLFWWindowSettings settings;
settings.setSize(1280, 720);
settings.setPosition(ofVec2f(100, 100));
settings.resizable = true;
shared_ptr<ofAppBaseWindow> mainWindow = ofCreateWindow(settings);
settings.setSize(PROJECTOR_RESOLUTION_X, PROJECTOR_RESOLUTION_Y);
settings.setPosition(ofVec2f(ofGetScreenWidth(), 0));
settings.resizable = false;
settings.decorated = false;
settings.shareContextWith = mainWindow;
shared_ptr<ofAppBaseWindow> secondWindow = ofCreateWindow(settings);
secondWindow->setVerticalSync(false);
shared_ptr<ofApp> mainApp(new ofApp);
ofAddListener(secondWindow->events().draw, mainApp.get(), &ofApp::drawProjector);
ofRunApp(mainWindow, mainApp);
ofRunMainLoop();
}
#include "ofApp.h"
void ofApp::setup()
{
ofBackground(0);
renderFbo.allocate(PROJECTOR_RESOLUTION_X, PROJECTOR_RESOLUTION_Y);
}
void ofApp::update()
{
glm::vec2 gravity = glm::vec2(0, 9.8f);
renderFbo.begin();
{
ofClear(255, 255);
for (int i = 0; i < balls.size(); i++)
{
balls[i].update(gravity);
balls[i].draw();
}
}
renderFbo.end();
}
void ofApp::draw()
{
ofSetColor(255);
// Draw unwarped image on the left.
renderFbo.draw(0, 0, 640, 360);
}
void ofApp::drawProjector(ofEventArgs& args)
{
ofBackground(0);
ofSetColor(255);
renderFbo.draw(0, 0);
}
void ofApp::addBall(int x, int y)
{
// Add a new ezBall.
balls.push_back(ezBall());
// Setup the last added ezBall.
balls.back().setup(x, y);
}
void ofApp::keyPressed(int key)
{
if (key == ' ')
{
balls.clear();
}
}
void ofApp::mouseDragged(int x, int y, int button)
{
// Only add a ball if we're dragging in the preview window.
if (ofInRange(x, 0, 640) && ofInRange(y, 0, 360))
{
// Remap the ball to the FBO resolution.
int ballX = ofMap(x, 0, 640, 0, renderFbo.getWidth());
int ballY = ofMap(y, 0, 360, 0, renderFbo.getHeight());
addBall(ballX, ballY);
}
}
void ofApp::mousePressed(int x, int y, int button)
{
// Simply call the mouseDragged handler.
mouseDragged(x, y, button);
}
#pragma once
#include "ofMain.h"
#include "ezBall.h"
// This must match the display resolution of your projector
#define PROJECTOR_RESOLUTION_X 1920
#define PROJECTOR_RESOLUTION_Y 1080
class ofApp : public ofBaseApp
{
public:
void setup();
void update();
void draw();
void drawProjector(ofEventArgs& args);
void keyPressed(int key);
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void addBall(int x, int y);
std::vector<ezBall> balls;
ofFbo renderFbo;
};
#include "ofApp.h"
void ofApp::setup()
{
ofBackground(0);
renderFbo.allocate(PROJECTOR_RESOLUTION_X, PROJECTOR_RESOLUTION_Y);
warpedImg.allocate(PROJECTOR_RESOLUTION_X, PROJECTOR_RESOLUTION_Y, OF_IMAGE_COLOR);
srcPoints.push_back(glm::vec2(0, 0));
srcPoints.push_back(glm::vec2(1, 0));
srcPoints.push_back(glm::vec2(0, 1));
srcPoints.push_back(glm::vec2(1, 1));
dstPoints.push_back(glm::vec2(0, 0));
dstPoints.push_back(glm::vec2(1, 0));
dstPoints.push_back(glm::vec2(0, 1));
dstPoints.push_back(glm::vec2(1, 1));
activePoint = -1;
homographyReady = false;
adjustMapping.set("Adjust Mapping", false);
projectWarped.set("Project Warped", true);
guiPanel.setup("Homography", "settings.json");
guiPanel.add(adjustMapping);
guiPanel.add(projectWarped);
}
void ofApp::update()
{
if (adjustMapping)
{
// Copy points from glm to cv format.
std::vector<cv::Point2f> cvSrcPoints;
std::vector<cv::Point2f> cvDstPoints;
for (int i = 0; i < srcPoints.size(); i++)
{
// Scale points to projector dimensions.
cvSrcPoints.push_back(cv::Point2f(srcPoints[i].x * PROJECTOR_RESOLUTION_X, srcPoints[i].y * PROJECTOR_RESOLUTION_Y));
cvDstPoints.push_back(cv::Point2f(dstPoints[i].x * PROJECTOR_RESOLUTION_X, dstPoints[i].y * PROJECTOR_RESOLUTION_Y));
}
// Generate a homography from the two sets of points.
homographyMat = cv::findHomography(cv::Mat(cvSrcPoints), cv::Mat(cvDstPoints));
homographyReady = true;
}
glm::vec2 gravity = glm::vec2(0, 9.8f);
renderFbo.begin();
{
ofClear(255, 255);
for (int i = 0; i < balls.size(); i++)
{
balls[i].update(gravity);
balls[i].draw();
}
}
renderFbo.end();
if (homographyReady)
{
// Read the FBO to pixels.
renderFbo.readToPixels(renderPixels);
// Warp the pixels into a new image.
warpedImg.setFromPixels(renderPixels);
ofxCv::warpPerspective(renderPixels, warpedImg, homographyMat, CV_INTER_LINEAR);
warpedImg.update();
}
}
void ofApp::draw()
{
ofSetColor(255);
// Draw unwarped image on the left.
renderFbo.draw(0, 0, 640, 360);
if (homographyReady)
{
// Draw warped image on the right.
warpedImg.draw(640, 0, 640, 360);
}
if (adjustMapping)
{
// Draw mapping points.
for (int i = 0; i < srcPoints.size(); i++)
{
ofSetColor(0, 0, 255);
glm::vec2 srcPt = glm::vec2(ofMap(srcPoints[i].x, 0, 1, 0, 640), ofMap(srcPoints[i].y, 0, 1, 0, 360));
ofDrawCircle(srcPt, 10);
ofSetColor(255, 0, 0);
glm::vec2 dstPt = glm::vec2(ofMap(dstPoints[i].x, 0, 1, 640, 1280), ofMap(dstPoints[i].y, 0, 1, 0, 360));
ofDrawCircle(dstPt, 10);
ofSetColor(255, 0, 255);
ofDrawLine(srcPt, dstPt);
}
}
guiPanel.draw();
}
void ofApp::drawProjector(ofEventArgs& args)
{
ofBackground(0);
ofSetColor(255);
if (homographyReady && projectWarped)
{
warpedImg.draw(0, 0);
}
else
{
renderFbo.draw(0, 0);
}
if (adjustMapping)
{
// Draw mapping dst points.
for (int i = 0; i < dstPoints.size(); i++)
{
ofSetColor(255, 0, 0);
glm::vec2 dstPt = glm::vec2(dstPoints[i].x * PROJECTOR_RESOLUTION_X, dstPoints[i].y * PROJECTOR_RESOLUTION_Y);
ofDrawCircle(dstPt, 20);
}
}
}
void ofApp::addBall(int x, int y)
{
// Add a new ezBall.
balls.push_back(ezBall());
// Setup the last added ezBall.
balls.back().setup(x, y);
}
void ofApp::keyPressed(int key)
{
if (key == ' ')
{
balls.clear();
}
}
void ofApp::mouseDragged(int x, int y, int button)
{
if (adjustMapping)
{
if (activePoint > -1)
{
// Move the active Point under the mouse, but stick to edges.
glm::vec2 normPt = glm::vec2(ofMap(x, 640, 1280, 0, 1, true), ofMap(y, 0, 360, 0, 1, true));
dstPoints[activePoint] = normPt;
}
}
else
{
// Only add a ball if we're dragging in the preview window.
if (ofInRange(x, 0, 640) && ofInRange(y, 0, 360))
{
// Remap the ball to the FBO resolution.
int ballX = ofMap(x, 0, 640, 0, renderFbo.getWidth());
int ballY = ofMap(y, 0, 360, 0, renderFbo.getHeight());
addBall(ballX, ballY);
}
}
}
void ofApp::mousePressed(int x, int y, int button)
{
if (adjustMapping)
{
// Try to snap to a dst point.
for (int i = 0; i < dstPoints.size(); i++)
{
glm::vec2 dstPt = glm::vec2(ofMap(dstPoints[i].x, 0, 1, 640, 1280), ofMap(dstPoints[i].y, 0, 1, 0, 360));
glm::vec2 mousePt = glm::vec2(x, y);
if (glm::distance(dstPt, mousePt) < 20)
{
// Close enough, let's grab this one.
activePoint = i;
break;
}
}
}
else
{
mouseDragged(x, y, button);
}
}
void ofApp::mouseReleased(int x, int y, int button)
{
if (adjustMapping)
{
activePoint = -1;
}
}
#pragma once
#include "ofMain.h"
#include "ofxCv.h"
#include "ofxGui.h"
#include "ezBall.h"
// This must match the display resolution of your projector
#define PROJECTOR_RESOLUTION_X 1920
#define PROJECTOR_RESOLUTION_Y 1080
class ofApp : public ofBaseApp
{
public:
void setup();
void update();
void draw();
void drawProjector(ofEventArgs& args);
void keyPressed(int key);
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void addBall(int x, int y);
std::vector<ezBall> balls;
ofFbo renderFbo;
ofPixels renderPixels;
ofImage warpedImg;
std::vector<glm::vec2> srcPoints;
std::vector<glm::vec2> dstPoints;
int activePoint;
cv::Mat homographyMat;
bool homographyReady;
ofParameter<bool> adjustMapping;
ofParameter<bool> projectWarped;
ofxPanel guiPanel;
};
#include "ofApp.h"
void ofApp::setup()
{
ofBackground(0);
renderFbo.allocate(PROJECTOR_RESOLUTION_X, PROJECTOR_RESOLUTION_Y);
srcPoints.push_back(glm::vec2(0, 0));
srcPoints.push_back(glm::vec2(1, 0));
srcPoints.push_back(glm::vec2(0, 1));
srcPoints.push_back(glm::vec2(1, 1));
dstPoints.push_back(glm::vec2(0, 0));
dstPoints.push_back(glm::vec2(1, 0));
dstPoints.push_back(glm::vec2(0, 1));
dstPoints.push_back(glm::vec2(1, 1));
activePoint = -1;
adjustMapping.set("Adjust Mapping", false);
guiPanel.setup("Homography", "settings.json");
guiPanel.add(adjustMapping);
}
void ofApp::update()
{
glm::vec2 gravity = glm::vec2(0, 9.8f);
renderFbo.begin();
{
ofClear(255, 255);
for (int i = 0; i < balls.size(); i++)
{
balls[i].update(gravity);
balls[i].draw();
}
}
renderFbo.end();
}
void ofApp::draw()
{
ofSetColor(255);
// Draw unwarped image on the left.
renderFbo.draw(0, 0, 640, 360);
if (adjustMapping)
{
// Draw mapping points.
for (int i = 0; i < srcPoints.size(); i++)
{
ofSetColor(0, 0, 255);
glm::vec2 srcPt = glm::vec2(ofMap(srcPoints[i].x, 0, 1, 0, 640), ofMap(srcPoints[i].y, 0, 1, 0, 360));
ofDrawCircle(srcPt, 10);
ofSetColor(255, 0, 0);
glm::vec2 dstPt = glm::vec2(ofMap(dstPoints[i].x, 0, 1, 640, 1280), ofMap(dstPoints[i].y, 0, 1, 0, 360));
ofDrawCircle(dstPt, 10);
ofSetColor(255, 0, 255);
ofDrawLine(srcPt, dstPt);
}
}
guiPanel.draw();
}
void ofApp::drawProjector(ofEventArgs& args)
{
ofBackground(0);
ofSetColor(255);
renderFbo.draw(0, 0);
if (adjustMapping)
{
// Draw mapping dst points.
for (int i = 0; i < dstPoints.size(); i++)
{
ofSetColor(255, 0, 0);
glm::vec2 dstPt = glm::vec2(dstPoints[i].x * PROJECTOR_RESOLUTION_X, dstPoints[i].y * PROJECTOR_RESOLUTION_Y);
ofDrawCircle(dstPt, 20);
}
}
}
void ofApp::addBall(int x, int y)
{
// Add a new ezBall.
balls.push_back(ezBall());
// Setup the last added ezBall.
balls.back().setup(x, y);
}
void ofApp::keyPressed(int key)
{
if (key == ' ')
{
balls.clear();
}
}
void ofApp::mouseDragged(int x, int y, int button)
{
if (adjustMapping)
{
if (activePoint > -1)
{
// Move the active Point under the mouse, but stick to edges.
glm::vec2 normPt = glm::vec2(ofMap(x, 640, 1280, 0, 1, true), ofMap(y, 0, 360, 0, 1, true));
dstPoints[activePoint] = normPt;
}
}
else
{
// Only add a ball if we're dragging in the preview window.
if (ofInRange(x, 0, 640) && ofInRange(y, 0, 360))
{
// Remap the ball to the FBO resolution.
int ballX = ofMap(x, 0, 640, 0, renderFbo.getWidth());
int ballY = ofMap(y, 0, 360, 0, renderFbo.getHeight());
addBall(ballX, ballY);
}
}
}
void ofApp::mousePressed(int x, int y, int button)
{
if (adjustMapping)
{
// Try to snap to a dst point.
for (int i = 0; i < dstPoints.size(); i++)
{
glm::vec2 dstPt = glm::vec2(ofMap(dstPoints[i].x, 0, 1, 640, 1280), ofMap(dstPoints[i].y, 0, 1, 0, 360));
glm::vec2 mousePt = glm::vec2(x, y);
if (glm::distance(dstPt, mousePt) < 20)
{
// Close enough, let's grab this one.
activePoint = i;
break;
}
}
}
else
{
// Simply call the mouseDragged handler.
mouseDragged(x, y, button);
}
}
void ofApp::mouseReleased(int x, int y, int button)
{
if (adjustMapping)
{
activePoint = -1;
}
}
#pragma once
#include "ofMain.h"
#include "ofxGui.h"
#include "ezBall.h"
// This must match the display resolution of your projector
#define PROJECTOR_RESOLUTION_X 1920
#define PROJECTOR_RESOLUTION_Y 1080
class ofApp : public ofBaseApp
{
public:
void setup();
void update();
void draw();
void drawProjector(ofEventArgs& args);
void keyPressed(int key);
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void addBall(int x, int y);
std::vector<ezBall> balls;
ofFbo renderFbo;
std::vector<glm::vec2> srcPoints;
std::vector<glm::vec2> dstPoints;
int activePoint;
ofParameter<bool> adjustMapping;
ofxPanel guiPanel;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment