Каков самый надежный способ записи потока Kinect для последующего воспроизведения?

Я работал с Processing и Cinder, чтобы модифицировать ввод Kinect на лету. Однако я также хотел бы записать полный поток (значения глубины + цвета + акселерометра и все, что там есть). Я записываю, чтобы попробовать разные эффекты/обработки на одном и том же материале.

Поскольку я все еще только изучаю Cinder, а обработка довольно медленная / отстает, у меня были проблемы с поиском совета по стратегии захвата потока - что угодно (предпочтительно в Cinder, oF или Processing) было бы действительно полезно.


person AKA    schedule 22.06.2011    source источник
comment
Вы используете OpenNI или Microsoft SDK?   -  person Artium    schedule 23.06.2011
comment
что вы имеете в виду под значением акселерометра?   -  person Padu Merloti    schedule 28.06.2011
comment
Спасибо за вопросы, извините, я не был более конкретным! Я не использую Microsoft SDK, и у меня больше всего опыта работы с библиотекой обработки (по-моему, Шиффмана) и блоком Cinder (автор R Hodgin)... Я понимаю, что в Kinect есть акселерометр (я не совсем уверен, почему он там - может быть, для обнаружения, когда он находится на неровной поверхности?), и было бы здорово иметь возможность записывать полный вывод, включая все другие датчики. Но это не обязательно, если это облегчит ответ на вопрос... Еще раз спасибо, АКА   -  person AKA    schedule 28.06.2011


Ответы (1)


Я пробовал и Processing, и OpenFrameworks. Обработка медленнее при отображении обоих изображений (глубина и цвет). OpenFrameworks немного замедляется при записи данных на диск, но вот основной подход:

  1. Настройка Openframeworks (откройте и скомпилируйте любой образец, чтобы убедиться, что он работает)
  2. Загрузите аддон ofxKinect и скопируйте пример проекта, как описано на github.
  3. После того, как вы запустили OF и пример ofxKinect, нужно просто добавить несколько переменных для сохранения ваших данных:

В этой базовой настройке я создал пару экземпляров ofImage и логическое значение для переключения сохранения. В этом примере буферы глубины и RGB сохраняются в экземплярах ofxCvGrayscaleImage, но я недостаточно использовал OF и OpenCV, чтобы знать, как сделать что-то столь же простое, как сохранение изображения на диск, поэтому я использовал два ofImage.

Я не знаю, насколько вам комфортно с Processing, OF, Cinder, так что ради аргументов предположу, что вы знаете, что разбираетесь в Processing, но все еще занимаетесь C++.

OF очень похож на Processing, но есть несколько отличий:

  1. В обработке у вас есть объявление переменных, и они используются в одном файле. В OF у вас есть файл .h, в котором вы объявляете свои переменные, и файл .cpp, в котором вы инициализируете и используете свои переменные.
  2. В Processing у вас есть методы setup() (инициализация переменных) и draw() (обновление переменных и рисование на экране), в то время как в OF у вас есть setup() (то же, что и в Processing), update() (только обновление переменных, ничего visual) и draw() (отрисовка на экране с использованием обновленных значений)
  3. При работе с изображениями, поскольку вы пишете код на C++, вам нужно сначала выделить память, в отличие от Processing/Java, где у вас есть управление памятью.

Есть и другие различия, которые я не буду здесь подробно описывать. Проверьте OF для обработки пользователей на вики.

Вернемся к примеру с Kinect, вот мои основные настройки:

.h файл:

#pragma once

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

class testApp : public ofBaseApp {
    public:

        void setup();
        void update();
        void draw();
        void exit();

        void drawPointCloud();

        void keyPressed  (int key);
        void mouseMoved(int x, int y );
        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 windowResized(int w, int h);

        ofxKinect kinect;

        ofxCvColorImage     colorImg;

        ofxCvGrayscaleImage     grayImage;
        ofxCvGrayscaleImage     grayThresh;
        ofxCvGrayscaleImage     grayThreshFar;

        ofxCvContourFinder  contourFinder;

        ofImage             colorData;//to save RGB data to disk
        ofImage             grayData;//to save depth data to disk 

        bool                bThreshWithOpenCV;
        bool                drawPC;
        bool                saveData;//save to disk toggle

        int                 nearThreshold;
        int                 farThreshold;

        int                 angle;

        int                 pointCloudRotationY;
        int                 saveCount;//counter used for naming 'frames'
};

и файл .cpp:

#include "testApp.h"


//--------------------------------------------------------------
void testApp::setup() {
    //kinect.init(true);  //shows infrared image
    kinect.init();
    kinect.setVerbose(true);
    kinect.open();

    colorImg.allocate(kinect.width, kinect.height);
    grayImage.allocate(kinect.width, kinect.height);
    grayThresh.allocate(kinect.width, kinect.height);
    grayThreshFar.allocate(kinect.width, kinect.height);
    //allocate memory for these ofImages which will be saved to disk
    colorData.allocate(kinect.width, kinect.height, OF_IMAGE_COLOR);
    grayData.allocate(kinect.width, kinect.height, OF_IMAGE_GRAYSCALE);

    nearThreshold = 230;
    farThreshold  = 70;
    bThreshWithOpenCV = true;

    ofSetFrameRate(60);

    // zero the tilt on startup
    angle = 0;
    kinect.setCameraTiltAngle(angle);

    // start from the front
    pointCloudRotationY = 180;

    drawPC = false;

    saveCount = 0;//init frame counter
}

//--------------------------------------------------------------
void testApp::update() {
    ofBackground(100, 100, 100);

    kinect.update();
    if(kinect.isFrameNew()) // there is a new frame and we are connected
    {

        grayImage.setFromPixels(kinect.getDepthPixels(), kinect.width, kinect.height);

        if(saveData){
            //if toggled, set depth and rgb pixels to respective ofImage, save to disk and update the 'frame' counter 
            grayData.setFromPixels(kinect.getDepthPixels(), kinect.width, kinect.height,true);
            colorData.setFromPixels(kinect.getCalibratedRGBPixels(), kinect.width, kinect.height,true);
            grayData.saveImage("depth"+ofToString(saveCount)+".png");
            colorData.saveImage("color"+ofToString(saveCount)+".png");
            saveCount++;
        }

        //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 ){
            grayThreshFar = grayImage;
            grayThresh = grayImage;
            grayThresh.threshold(nearThreshold, true);
            grayThreshFar.threshold(farThreshold);
            cvAnd(grayThresh.getCvImage(), grayThreshFar.getCvImage(), grayImage.getCvImage(), NULL);
        }else{

            //or we do it ourselves - show people how they can work with the pixels

            unsigned char * pix = grayImage.getPixels();
            int numPixels = grayImage.getWidth() * grayImage.getHeight();

            for(int i = 0; i < numPixels; i++){
                if( pix[i] < nearThreshold && pix[i] > farThreshold ){
                    pix[i] = 255;
                }else{
                    pix[i] = 0;
                }
            }
        }

        //update the cv image
        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);
    }
}

//--------------------------------------------------------------
void testApp::draw() {
    ofSetColor(255, 255, 255);
    if(drawPC){
        ofPushMatrix();
        ofTranslate(420, 320);
        // we need a proper camera class
        drawPointCloud();
        ofPopMatrix();
    }else{
        kinect.drawDepth(10, 10, 400, 300);
        kinect.draw(420, 10, 400, 300);

        grayImage.draw(10, 320, 400, 300);
        contourFinder.draw(10, 320, 400, 300);
    }


    ofSetColor(255, 255, 255);
    stringstream reportStream;
    reportStream << "accel is: " << ofToString(kinect.getMksAccel().x, 2) << " / "
                                 << ofToString(kinect.getMksAccel().y, 2) << " / " 
                                 << ofToString(kinect.getMksAccel().z, 2) << endl
                 << "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
                 << "press s to toggle saving depth and color data. currently saving:  " << saveData << endl
                 << "press UP and DOWN to change the tilt angle: " << angle << " degrees";
    ofDrawBitmapString(reportStream.str(),20,656);
}

void testApp::drawPointCloud() {
    ofScale(400, 400, 400);
    int w = 640;
    int h = 480;
    ofRotateY(pointCloudRotationY);
    float* distancePixels = kinect.getDistancePixels();
    glBegin(GL_POINTS);
    int step = 2;
    for(int y = 0; y < h; y += step) {
        for(int x = 0; x < w; x += step) {
            ofPoint cur = kinect.getWorldCoordinateFor(x, y);
            ofColor color = kinect.getCalibratedColorAt(x,y);
            glColor3ub((unsigned char)color.r,(unsigned char)color.g,(unsigned char)color.b);
            glVertex3f(cur.x, cur.y, cur.z);
        }
    }
    glEnd();
}

//--------------------------------------------------------------
void testApp::exit() {
    kinect.setCameraTiltAngle(0); // zero the tilt on exit
    kinect.close();
}

//--------------------------------------------------------------
void testApp::keyPressed (int key) {
    switch (key) {
        case ' ':
            bThreshWithOpenCV = !bThreshWithOpenCV;
        break;
        case'p':
            drawPC = !drawPC;
            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 'c':
            kinect.setCameraTiltAngle(0);       // zero the tilt
            kinect.close();
            break;
        case 's'://s to toggle saving data
            saveData = !saveData;
            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;
    }
}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y) {
    pointCloudRotationY = x;
}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button)
{}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button)
{}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button)
{}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h)
{}

Это очень простая установка. Не стесняйтесь изменять (добавлять угол наклона к сохраненным данным и т. д.). Я почти уверен, что есть способы улучшить это с точки зрения скорости (например, не обновлять экземпляры xCvGrayscaleImage и не рисовать изображения на экране во время сохранения или складывать несколько кадров и записывать их с интервалом, а не на каждом кадре и т. д.)

Удачи

person George Profenza    schedule 31.08.2011