ofxKinect affecting graphics of other addons (ofxInkSim)?

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)
{

}

Looking at the InkSim examples it seems that you are not using a brush for the strokes. In the brush example there is the following code:

void ofApp::mouseDragged(int x, int y, int button)
{
    ofColor c = moodmachine;
    inkSim.stroke(&brush, x, y, getInkColor(c.getHueAngle(), 255, 10));
}

Which is missing from your app. You can take the code above out of the mouuseDragged() method and place it into draw() in a for loop which iterates over your blobs and draws at the center of each one (x,y)

HTH

Hi maximillion, thanks for your answer.
I am not using the brush example, but the FillBufferExample to do this.
I took the section of the code in which it generates a drop of ink and put it under draw. In this way it makes only single drops rather than an actual stroke, which requires two points and it would make the visuals different from those I want to achieve.

Now that you mention the for loop, I have used it in my draw function. However, it could be that it’s called too many times? This could explain why the inks look oversaturated and are not really blending anymore. Am I calling the for loop in the wrong way?