Using Kinect for real-time mapping – Part IIb: Create a real-time mask from depth image: openFrameworks

In this chapter we will be doing in open frameworks what we have done for processing before  (in case you need more detailed explanation on what we are doing, you can find it in the article for processing): We want a real-time mask (Let’s say we want to map a projection on people or objects that move). Masks can e.g. be black/white images, so what we do is very easy: We define a maximum and a minimum distance and use the distance information, the kinect delivers, to create a simple black/white Image, that is updated in real-time.
Below you find the complete code for creating the mask image from kinect. Find remarks inside the code. The main difference from processing is that the distances are not seen in cm but as brightness values for each pixel. So if we look at a pixel in the original depth image from kinect:
0 means completely far away
255 means completely near
This is important to understand how our helper variables for far and near distance Threshold nearBorder and farBorder work.

ofApp.h

#pragma once

#include "ofMain.h"
#include "ofxKinect.h"
#include "ofxOpenCv.h"

class ofApp : public ofBaseApp{

public:
void setup();
void setupKinect();
void update();
void draw();
void prepareMaskImageFromKinect();

void keyPressed(int key);

// kinect
ofxKinect kinect;
// grayscale depth image
ofxCvGrayscaleImage grayImage;
// helper variables (which pixel shall
// be white, depending on distances)
int nearBorder;
int farBorder;
int angle;
bool setupFlag;

};

ofApp.cpp

#include "ofApp.h"
 
//--------------------------------------------------------------
void ofApp::setup(){
    setupKinect();
}
 
//--------------------------------------------------------------
void ofApp::setupKinect(){
	ofSetLogLevel(OF_LOG_VERBOSE);
	
	// enable depth->video image calibration
	kinect.setRegistration(true);
    
    // init kinect like this
	//kinect.init();
    // OR like next line: shows infrared instead of RGB video image
	//kinect.init(true);
    // OR like next line: disable video image (faster fps)
	kinect.init(false, false);
	
    // opens first available kinect
	kinect.open();
    // OR: open a kinect by id, starting with 0 (sorted by
    // serial # lexicographically))
	//kinect.open(1);
    // OR: open a kinect using it's unique serial #    
	//kinect.open("A00362A08602047A");
	
	// info only: 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";
        setupFlag = true;
	}
    
	grayImage.allocate(kinect.width, kinect.height);
    
  	nearBorder = 165;
	farBorder = 143;
    	
	// zero the tilt on startup
	angle = 0;
	kinect.setCameraTiltAngle(angle);
}
 
//--------------------------------------------------------------
void ofApp::update(){
    prepareMaskImageFromKinect();    
}
 
//--------------------------------------------------------------
void ofApp::draw(){
    
	ofSetColor(255, 255, 255);
    if (setupFlag) {
        kinect.drawDepth(10, 10, 400, 300);
        grayImage.draw(10, 320, 400, 300);
    }
        
    // now for some info only output
	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
	<< "set near threshold " << nearBorder << " (press: + -)" << endl
	<< "set far threshold " << farBorder << " (press: < >) "
	<< ", 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::prepareMaskImageFromKinect(){
    // Here we prepare an image consisting of white or 
    // black pixels. We later use this image as a mask
    
    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(), kinect.width, kinect.height);
        
        //get a pixel map
        unsigned char * pix = grayImage.getPixels();
        // get number of all pixels
        int numPixels = grayImage.getWidth() * grayImage.getHeight();
        // run through all pixels
        for(int i = 0; i < numPixels; i++) { // check the distance - if it is in beteen our borders (farBorder/nearBorder) change it to white, else black if(pix[i] > farBorder && pix[i] < nearBorder) { pix[i] = 255; } else { pix[i] = 0; } } // update the cv image grayImage.flagImageChanged(); // now we have an image consisting of only white or black pixels. We now could blur it or the like to adapt the mask to our needs. We used ofcCv image to make this easier } } //-------------------------------------------------------------- void ofApp::keyPressed(int key){ // chekc for keys pressed // use this to adjust borders (Threshold) switch (key) { case '>':
		case '.':
			farBorder ++;
			if (farBorder > 255) farBorder = 255;
			break;
			
		case '<':
		case ',':
			farBorder --;
			if (farBorder < 0) farBorder = 0; break; case '+': nearBorder ++; if (nearBorder > 255) nearBorder = 255;
			break;
		case '-':
			nearBorder --;
			if (nearBorder < 0) nearBorder = 0; 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;            
    }
}