2014-02-18 9 views
7

Ich habe ein C++ Projekt, wo ich OpenCV und Libfreenect verwende. Ich möchte nicht etwas so großes und schweres wie OpenNI einbeziehen und OpenCV-Installationsabhängigkeit in dem Prozess erstellen. Ich möchte die Kalibrierungsinformationen here verwenden, um die RGB- und Tiefenbilder zu entzerren und auszurichten.Wie richtet man RGB und Tiefenbild von Kinect in OpenCV aus?

Die Bilder einzeln zu entzerren, basierend auf der Kameramatrix und den Verzerrungskoeffizienten, war einfach genug. Aber jetzt bin ich verwirrt darüber, wie ich die Rektifikations- und Projektionsmatrizen verwenden könnte, um das RGB- und Tiefenbild so auszurichten, dass sie mir im Wesentlichen die gleichen Dinge aus derselben Perspektive zeigen. Nach langem Suchen kann ich nicht feststellen, wie es mit OpenCV funktionieren soll. Es ist eine vage Schätzung, dass reprojectImageTo3D() und warpPerspective() verwendet werden kann, aber ich bin mir nicht sicher, wie.

Wie könnte ich dieses Problem angehen? Ich verwende die alte XBOX360 Kinect (mit 0-2047 Roh-Disparity-Wertebereich).

UPDATE

Hier ist der Teil-Code, den ich bisher geschrieben haben:

// I use callback functions to get RGB (CV_8UC3) and depth (CV_16UC1) 
// I undistort them and call the following method 
void AlignImages(cv::Mat& pRGB, cv::Mat& pDepth) { 

    rotationMat = (cv::Mat_<double_t>(3,3) << 9.9984628826577793e-01, 1.2635359098409581e-03, -1.7487233004436643e-02, -1.4779096108364480e-03, 9.9992385683542895e-01, -1.2251380107679535e-02, 1.7470421412464927e-02, 1.2275341476520762e-02, 9.9977202419716948e-01); 
    translationMat = (cv::Mat_<double_t>(3,1) << 1.9985242312092553e-02, -7.4423738761617583e-04, -1.0916736334336222e-02); 


    // make a copy in float to convert raw depth data to physical distance 
    cv::Mat tempDst; 
    pDepth.convertTo(tempDst, CV_32F); 

    // create a 3 channel image of precision double for the 3D points 
    cv::Mat tempDst3D = cv::Mat(cv::Size(640, 480), CV_64FC3, double(0)); 

    float_t* tempDstData = (float_t*)tempDst.data; 
    double_t* tempDst3DData = (double_t*)tempDst3D.data; 

    size_t pixelSize = tempDst.step/sizeof(float_t); 
    size_t pixel3DSize = tempDst3D.step/sizeof(double_t); 

    for (int row=0; row < tempDst.rows; row++) { 
     for (int col=0; col < tempDst.cols; col++) { 

      // convert raw depth values to physical distance (in metres) 
      float_t& pixel = tempDstData[pixelSize * row + col]; 
      pixel = 0.1236 * tanf(pixel/2842.5 + 1.1863); 

      // reproject physical distance values to 3D space 
      double_t& pixel3D_X = tempDst3DData[pixel3DSize * row + col]; 
      double_t& pixel3D_Y = tempDst3DData[pixel3DSize * row + col +1]; 
      double_t& pixel3D_Z = tempDst3DData[pixel3DSize * row + col + 2]; 

      pixel3D_X = (row - 3.3930780975300314e+02) * pixel/5.9421434211923247e+02; 
      pixel3D_Y = (col - 2.4273913761751615e+02) * pixel/5.9104053696870778e+02; 
      pixel3D_Z = pixel; 

     } 
    } 

    tempDst3D = rotationMat * tempDst3D + translationMat; 
} 

ich direkt die Zahlen verwendet haben, anstatt sie auf Variablen zuweisen, aber das sollte nicht sein, Problem beim Verständnis der Logik. An dieser Stelle soll ich folgendes machen:

P2D_rgb.x = (P3D'.x * fx_rgb/P3D'.z) + cx_rgb 
P2D_rgb.y = (P3D'.y * fy_rgb/P3D'.z) + cy_rgb 

Aber ich verstehe nicht, wie ich es genau machen soll. Vielleicht gehe ich überhaupt in die falsche Richtung. Aber ich kann kein Beispiel dafür finden.

+0

können Sie OpenNI statt openkinect/libfreenect verwenden? –

+0

Ich denke, Sie haben ein Problem damit, wie Sie auf Daten in Ihrem 'tempDst3DData'-Puffer zugreifen. Es sollte 'tempDst3DData [3 * pixel3DSize * row + 3 * col + channel]' sein. In Bezug auf Ihre aktualisierte Frage bearbeite ich meine Antwort, um sie klarer zu machen. – AldurDisciple

+0

Ich glaube auch, du hast 'row' und' col' in deinen 'pixel3D_X' und' pixel3D_Y' Ausdrücken gemischt. – AldurDisciple

Antwort

8

Grundsätzlich müssen Sie das 3D-Koordinatensystem ändern, um 3D-Punkte von der Tiefenkamera in 3D-Punkte zu konvertieren, die von der RGB-Kamera gesehen werden.

Sie können die Funktion reprojectImageTo3D() nicht verwenden, da sie eine Matrix Q erwartet, die Sie nicht haben. Stattdessen sollten Sie Ihre Disparity-Map mithilfe der Funktion raw_depth_to_meters auf der Seite, die Sie verlinkt haben, in eine Tiefenkarte konvertieren.

Dann müssen Sie für jedes Pixel der Depthmap den zugehörigen 3D-Punkt berechnen, der auf der verlinkten Seite mit P3D gekennzeichnet ist (siehe § "Pixel mit Farbpixeln zuordnen"). Dann müssen Sie die bereitgestellte 3D-Rotationsmatrix R und den 3D-Translationsvektor T, die die Transformation von der Tiefenkamera zur RGB-Kamera darstellen, auf jeden 3D-Punkt P3D anwenden, um den zugehörigen neuen 3D-Punkt P3D' zu erhalten. Schließlich können Sie mithilfe der Kalibrierungsmatrix der RGB-Kamera die neuen 3D-Punkte in das RGB-Bild projizieren und dem erhaltenen Pixel die zugehörige Tiefe zuweisen, um eine neue Tiefenkarte zu erzeugen, die auf das RGB-Bild ausgerichtet ist.

Beachten Sie, dass Sie dabei zwangsläufig an Genauigkeit verlieren, da Sie Okklusionen (nur die minimale Tiefe jedes Pixels) und die Bildinterpolation berücksichtigen müssen (da die projizierten 3D-Punkte im Allgemeinen nicht zugeordnet werden) mit ganzzahligen Pixelkoordinaten im RGB-Bild). In Bezug auf die Bildinterpolation empfehle ich, den nächsten Nachbaransatz zu verwenden, sonst könnte es zu merkwürdigem Verhalten an den Tiefengrenzen kommen.

bearbeiten nach der Frage Update

Hier ist ein Modell, von dem, was Sie sollten, um tun, die Kinect depthmap an den RGB-Cam-Sicht neu zuordnen:

cv::Mat_<float> pt(3,1), R(3,3), t(3,1); 
// Initialize R & t here 

depthmap_rgbcam = cv::Mat::zeros(height,width,CV_32FC1); // Initialize the depthmap to all zeros 
float *depthmap_rgbcam_buffer = (float*)depthmap_rgbcam.data; 
for(int row=0; row<height; ++row) 
{ 
    for(int col=0; col<width; ++col) 
    { 
     // Convert kinect raw disparity to depth 
     float raw_disparity = kinect_disparity_map_buffer[width*row+col]; 
     float depth_depthcam = disparity_to_depth(raw_disparity); 

     // Map depthcam depth to 3D point 
     pt(0) = depth*(col-cx_depthcam)/fx_depthcam; // No need for a 3D point buffer 
     pt(1) = depth*(row-cy_depthcam)/fy_depthcam; // here, unless you need one. 
     pt(2) = depth; 

     // Rotate and translate 3D point 
     pt = R*pt+t; 

     // If required, apply rgbcam lens distortion to X, Y and Z here. 

     // Project 3D point to rgbcam 
     float x_rgbcam = fx_rgbcam*pt(0)/pt(2)+cx_rgbcam; 
     float y_rgbcam = fy_rgbcam*pt(1)/pt(2)+cy_rgbcam; 

     // "Interpolate" pixel coordinates (Nearest Neighbors, as discussed above) 
     int px_rgbcam = cvRound(x_rgbcam); 
     int py_rgbcam = cvRound(y_rgbcam); 

     // Handle 3D occlusions 
     float &depth_rgbcam = depthmap_rgbcam_buffer[width*py_rgbcam+px_rgbcam]; 
     if(depth_rgbcam==0 || depth_depthcam<depth_rgbcam) 
      depth_rgbcam = depth_depthcam; 
    } 
} 

Das ist das ist Idee, modulo mögliche Tippfehler. Sie können auch konsistent den Datentyp ändern, wie Sie möchten. Was Ihren Kommentar betrifft, glaube ich nicht, dass es dafür eine eingebaute OpenCV-Funktion gibt.

+0

Entschuldigung für die späte Antwort. Ich habe die Grundidee in der Theorie verstanden. Die Frage war, wie ich das programmatisch mit OpenCV machen könnte. Die bereitgestellten yaml-Dateien haben Projektionsmatrizen. Können sie nicht dazu verwendet werden, den Prozess irgendwie zu beschleunigen, anstatt die Pixel manuell zu manipulieren? –

+0

@SubhamoySengupta Auch wenn Sie die beste Genauigkeit benötigen, ist es vielleicht eine gute Idee, Ihre eigene Kinect zu kalibrieren, anstatt die Werte anderer Personen zu verwenden. – AldurDisciple

+0

@Robin der Code-Extrakt, den ich gepostet habe, befasst sich nicht mit dem RGB-Bild, nur mit der Tiefenkarte. Es führt die Berechnung durch, die erforderlich ist, um die von der IR-Kamera betrachtete Tiefenkarte in die von der RGB-Kamera angezeigte Tiefenkarte zu transformieren, die gut auf das RGB-Bild ausgerichtet ist. – AldurDisciple

1

@AldurDisciple, von dem, was ich verstehen kann, ist das RGB-Bild von der Kamera in depthmap_rgbcam mit seinen RGB-Werten gespeichert, aber ich kann nicht sehen, wo und wann das Bild von der Kamera übernommen und an die Variable übergeben? Für mich ist es wie eine leere Matrix nach der Initialisierung des depthmap_rgbcam.

+0

Ich habe dich als Kommentar zu meinem Beitrag geantwortet, da deine Frage auch dort hätte gepostet werden sollen. – AldurDisciple