Good morning,
I am relatively new with c++, and I am doing a project using ofxKinect to track motion change and use that change to release Ink using ofxInkSim.
Now, I am aware that the way the code is laid down is not the best, and that is because I am merging the ofxKinect example with ofxInkSim addon, which is not as tidy as I wished.
Basically I’m using the ofxContourFinder to detect the blobs, and I am releasing ink at the center of each blob.
When I run ofxInkSim on a separate project the colors work perfectly and there does not seem to be any graphics problem.
Here is how the inks looks when they aren’t conjoint with the ofxKinect code:
https://ibb.co/nBNCKTQ
When I run the code of the compound projects there seems to be some sort of graphics problem making the ink blending in a weird way. Also the colors seem to be at a very high contrast.
To combine the 2 projects I simply used ProjectGenerator and included all the ofx addons, and then I copied-pasted each section from the past code.
I really hope someone knows if the ofxKinect applies some graphics to the whole project, and how to limit those only to the blob detection.
I am close to finalizing the project, and getting stuck here is quite upsetting…
I’m attaching the ofApp.h code just in case someone wants to look at it.
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup() {
ofSetLogLevel(OF_LOG_VERBOSE);
// enable depth->video image calibration
kinect.setRegistration(true);
kinect.init();
//kinect.init(true); // shows infrared instead of RGB video image
//kinect.init(true, true); // disable video image (faster fps)
kinect.open(); // opens first available kinect
// print the intrinsic IR sensor values
if(kinect.isConnected()) {
ofLogNotice() << "sensor-emitter dist: " << kinect.getSensorEmitterDistance() << "cm";
ofLogNotice() << "sensor-camera dist: " << kinect.getSensorCameraDistance() << "cm";
ofLogNotice() << "zero plane pixel size: " << kinect.getZeroPlanePixelSize() << "mm";
ofLogNotice() << "zero plane dist: " << kinect.getZeroPlaneDistance() << "mm";
}
colorImg.allocate(kinect.width, kinect.height);
grayImage.allocate(kinect.width, kinect.height);
grayThreshNear.allocate(kinect.width, kinect.height);
grayThreshFar.allocate(kinect.width, kinect.height);
nearThreshold = 230;
farThreshold = 70;
bThreshWithOpenCV = true;
ofSetFrameRate(frameRate); // set frame rate (how often it detects change). default was 60.
// zero the tilt on startup
angle = 25;
kinect.setCameraTiltAngle(angle);
// –---------------------------------------------------------
inkSim.setup();
gui.setup(inkSim.getUniformInfo());
fbo.allocate(ofGetWidth(), ofGetHeight());
ofDirectory dir;
dir.open("imgs");
dir.listDir();
tests.resize(dir.size());
for (int i = 0; i < dir.size(); i++)
{
string path = dir.getFile(i).getAbsolutePath();
ofLoadImage(tests.at(i), path);
}
dir.close();
}
//--------------------------------------------------------------
void ofApp::update() {
ofBackground(0, 0, 0);
kinect.update();
// there is a new frame and we are connected
if(kinect.isFrameNew()) {
// load grayscale depth image from the kinect source
grayImage.setFromPixels(kinect.getDepthPixels());
// we do two thresholds - one for the far plane and one for the near plane
// we then do a cvAnd to get the pixels which are a union of the two thresholds
if(bThreshWithOpenCV) {
grayThreshNear = grayImage;
grayThreshFar = grayImage;
grayThreshNear.threshold(nearThreshold, true);
grayThreshFar.threshold(farThreshold);
cvAnd(grayThreshNear.getCvImage(), grayThreshFar.getCvImage(), grayImage.getCvImage(), NULL);
} else {
// or we do it ourselves - show people how they can work with the pixels
ofPixels & pix = grayImage.getPixels();
int numPixels = pix.size();
for(int i = 0; i < numPixels; i++) {
if(pix[i] < nearThreshold && pix[i] > farThreshold) {
pix[i] = 255;
} else {
pix[i] = 0;
}
}
}
// update the cv images
grayImage.flagImageChanged();
// find contours which are between the size of 20 pixels and 1/3 the w*h pixels.
// also, find holes is set to true so we will get interior contours as well....
contourFinder.findContours(grayImage, 10, (kinect.width*kinect.height)/2, 20, false);
}
inkSim.update();
}
//--------------------------------------------------------------
void ofApp::draw() {
ofSetColor(255, 255, 255);
// draw from the live kinect
//kinect.drawDepth(420, 10, 400, 300); // depth map
kinect.draw(420, 10, 400, 300); // show kinect image
//grayImage.draw(420, 10, 400, 300); // default was (10, 320, 400, 300)
contourFinder.draw(420, 10, 400, 300);
/*
for( int i=0; i<(int)contourFinder.blobs.size(); i++ ) {
ofxCvBlob blob = contourFinder.blobs[i];
// ---------------------------- draw circle at the center of each blob:
ofFill();
ofSetColor(255, 255, 0, 255); // fill color
ofDrawCircle(blob.boundingRect.x + blob.boundingRect.width/2, blob.boundingRect.y + blob.boundingRect.height/2, 20); // draw circle
ofNoFill();
}
*/
// We save this round's blobs here and then copy it to the previousBlobs
std::vector<std::vector<int>> tempBlobs;
for( int i=0; i<(int)contourFinder.blobs.size(); i++ ) {
ofxCvBlob blob = contourFinder.blobs[i];
// Calc centre points if you don't wanna use centroids
// int x = blob.boundingRect.x + blob.boundingRect.width/2;
// int y = blob.boundingRect.y + blob.boundingRect.height/2;
int x = blob.centroid.x;
int y = blob.centroid.y;
int colorIndex = getBlobColor(x, y);
std::vector<int> v = {x, y, colorIndex};
tempBlobs.push_back(v);
// ---------------------------- draw circle at the center of each blob:
/*ofFill();
ofSetHexColor(colors[colorIndex]); // fill color
ofDrawCircle(x, y, 20); // draw circle
ofNoFill();*/
//for cycle
if (counter++ == 60) {
counter = 0;
//drawInk = true;
//} else { drawInk = false; };
// Start buffer
fbo.begin();
ofClear(0);
// Ciclo for starts
for (int i = 0; i < 1; i++) // number of mini-blobs per click
{
float rad = ofRandom(3, 7); //ofRandom(minRadius, maxRadius);
float depth = 255; //opacità //ofMap(rad, 2, 10, 255, 200, true);
// Positioning the blob
int blobX = x +- ofRandom(0, 30); // ofRandon(minimumDistanceFromClick, maxDistanceFromClick);
int blobY = y +- ofRandom(0, 30);
// Filling the blob
ofPushStyle();
ofSetHexColor(colors[colorIndex]);
// Drawing the blob
ofDrawCircle(blobX, blobY, rad);
ofPopStyle();
}
fbo.end();
// Draw buffer
inkSim.begin();
fbo.draw(0, 0);
inkSim.end();
}
}
// Now copy tempBlobs to previousBlobs
previousBlobs.clear();
for( int i=0; i<(int)tempBlobs.size(); i++ ) {
previousBlobs.push_back(tempBlobs[i]);
}
inkSim.draw();
/*ofDrawBitmapStringHighlight("press 'f' or 't' to fill buffer", 10, ofGetHeight() - 60);
ofDrawBitmapStringHighlight("press 'c' to clear buffer", 10, ofGetHeight() - 40);
ofDrawBitmapStringHighlight("press 'm' to hide gui", 10, ofGetHeight() - 20);*/
// draw instructions
ofSetColor(255, 255, 255);
stringstream reportStream;
if(kinect.hasAccelControl()) {
reportStream << "accel is: " << ofToString(kinect.getMksAccel().x, 2) << " / "
<< ofToString(kinect.getMksAccel().y, 2) << " / "
<< ofToString(kinect.getMksAccel().z, 2) << endl;
} else {
reportStream << "Note: this is a newer Xbox Kinect or Kinect For Windows device," << endl
<< "motor / led / accel controls are not currently supported" << endl << endl;
}
reportStream << "press p to switch between images and point cloud, rotate the point cloud with the mouse" << endl
<< "using opencv threshold = " << bThreshWithOpenCV <<" (press spacebar)" << endl
<< "set near threshold " << nearThreshold << " (press: + -)" << endl
<< "set far threshold " << farThreshold << " (press: < >) num blobs found " << contourFinder.nBlobs
<< ", fps: " << ofGetFrameRate() << endl
<< "press c to close the connection and o to open it again, connection is: " << kinect.isConnected() << endl;
if(kinect.hasCamTiltControl()) {
reportStream << "press UP and DOWN to change the tilt angle: " << angle << " degrees" << endl
<< "press 1-5 & 0 to change the led mode" << endl;
}
ofDrawBitmapString(reportStream.str(), 20, 652);
}
//--------------------------------------------------------------
void ofApp::exit() {
kinect.setCameraTiltAngle(0); // zero the tilt on exit
kinect.close();
}
//--------------------------------------------------------------
void ofApp::keyPressed (int key) {
switch (key) {
case ' ':
bThreshWithOpenCV = !bThreshWithOpenCV;
break;
case '>':
case '.':
farThreshold ++;
if (farThreshold > 255) farThreshold = 255;
break;
case '<':
case ',':
farThreshold --;
if (farThreshold < 0) farThreshold = 0;
break;
case '+':
case '=':
nearThreshold ++;
if (nearThreshold > 255) nearThreshold = 255;
break;
case '-':
nearThreshold --;
if (nearThreshold < 0) nearThreshold = 0;
break;
case 'w':
kinect.enableDepthNearValueWhite(!kinect.isDepthNearValueWhite());
break;
case 'o':
kinect.setCameraTiltAngle(angle); // go back to prev tilt
kinect.open();
break;
case '0':
kinect.setCameraTiltAngle(0); // zero the tilt
kinect.close();
break;
case '1':
kinect.setLed(ofxKinect::LED_GREEN);
break;
case '2':
kinect.setLed(ofxKinect::LED_YELLOW);
break;
case '3':
kinect.setLed(ofxKinect::LED_RED);
break;
case '4':
kinect.setLed(ofxKinect::LED_BLINK_GREEN);
break;
case '5':
kinect.setLed(ofxKinect::LED_BLINK_YELLOW_RED);
break;
case '6':
kinect.setLed(ofxKinect::LED_OFF);
break;
case OF_KEY_UP:
angle++;
if(angle>30) angle=30;
kinect.setCameraTiltAngle(angle);
break;
case OF_KEY_DOWN:
angle--;
if(angle<-30) angle=-30;
kinect.setCameraTiltAngle(angle);
break;
case 'm':
gui.toggleVisible();
break;
case 'r':
gui.resetToDefault();
break;
case 'c':
inkSim.clear();
break;
case 'f':
int angRange = ofRandom(100, 200);
int angMin = ofRandom(360);
fbo.begin();
ofClear(0);
for (int i = 0; i < 500; i++)
{
int ang = ofRandom(angMin, angRange);
if (ang > 360)
ang -= 360;
float rad = ofRandom(2, 10);
float depth = 255;//ofMap(rad, 2, 10, 255, 200, true);
ofColor c = getInkColor(ang, 255, depth);
ofPushStyle();
ofSetColor(c);
ofDrawCircle(ofRandomWidth(), ofRandomHeight(), rad);
ofPopStyle();
}
fbo.end();
inkSim.begin();
fbo.draw(0, 0);
inkSim.end();
break;
}
}
// RICCARDO CHIANELLA
int ofApp::getBlobColor(int x, int y)
{
for (int i = 0; i < (int)previousBlobs.size(); i++)
{
int px = previousBlobs[i][0];
int py = previousBlobs[i][1];
if ( sqrt( (x - px)*(x - px) + (y - py)*(y - py) ) < identityDistance )
{
return previousBlobs[i][2];
}
}
return ofRandom(0, sizeof(colors) );
}
// END RICCARDO CHIANELLA
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button)
{
}
//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button)
{
/*
// Start buffer
fbo.begin();
ofClear(0);
// Ciclo for starts
for (int i = 0; i < 1; i++) // number of mini-blobs per click
{
float rad = ofRandom(3, 7); //ofRandom(minRadius, maxRadius);
float depth = 255; //opacità //ofMap(rad, 2, 10, 255, 200, true);
// Positioning the blob
int blobX = x +- ofRandom(0, 30); // ofRandon(minimumDistanceFromClick, maxDistanceFromClick);
int blobY = y +- ofRandom(0, 30);
// Filling the blob
ofPushStyle();
ofSetHexColor(0x00fff);
// Drawing the blob
ofDrawCircle(blobX, blobY, rad);
ofPopStyle();
}
fbo.end();
// Draw buffer
inkSim.begin();
fbo.draw(0, 0);
inkSim.end();
*/
}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button)
{
}
//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){
}
//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){
}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h)
{
}