將 pcd 檔轉換成深度圖與 RGB 圖檔

當我們好不容易產生了一個 pcd 模型後,突然發現到忘記儲存原始的 RGB 影像以及深度影像, 這個時候要怎辦呢?你可以選擇重新使用 kinect 建立新的模型並拍下影像以及深度,也可以用 更聰明的方法 - 將 pcd 檔案轉換成 RGB 影像以及深度影像。

實際上,在 pcl 1.7 版提供了以下方法,讓你可以將 point cloud 存成 PNG 文件。

void pcl::io::savePNGFile (const std::string &file_name, const pcl::PointCloud< T > &cloud);

但是如果我們使用的是 pcl 1.6 或者以下的版本呢?這時候就得手動增加這部份的 code。

template <typename T>
void savePNGFile (const std::string &file_name, const pcl::PointCloud<T> &cloud)
{
    std::vector<unsigned char> char_data(cloud.width * cloud.height * 3);

    for (size_t i = 0; i < cloud.points.size(); ++i) {
        char_data[i*3 + 0] = cloud.points[i].r;
        char_data[i*3 + 1] = cloud.points[i].g;
        char_data[i*3 + 2] = cloud.points[i].b;
    }

    vtkSmartPointer<vtkImageImport> importer = vtkSmartPointer<vtkImageImport>::New();
    importer->SetNumberOfScalarComponents(3);
    importer->SetDataScalarTypeToUnsignedChar();
    importer->SetWholeExtent (0, cloud.width - 1, 0, cloud.height - 1, 0, 0);
    importer->SetDataExtentToWholeExtent();

    void* data = const_cast<void*> (reinterpret_cast<const void*> (&char_data[0]));
    importer->SetImportVoidPointer(data, 1);
    importer->Update();

    vtkSmartPointer<vtkImageFlip> flipYFilter = vtkSmartPointer<vtkImageFlip>::New();
    flipYFilter->SetFilteredAxis(1);
    flipYFilter->SetInputConnection(importer->GetOutputPort());
    flipYFilter->Update();

    vtkSmartPointer<vtkPNGWriter> writer = vtkSmartPointer<vtkPNGWriter>::New();
    writer->SetFileName(file_name.c_str());
    writer->SetInputConnection(flipYFilter->GetOutputPort());
    writer->Write();
}

使用上面的程式碼,你就可以在 pcl 1.6 中將你的 point cloud 存成 png 圖片。

現在我想要將我的點雲轉變成為深度圖檔,這時該怎麼辦呢?你可以用下面的程式完成他:

template <typename T>
void saveDepthToPNG (const std::string &file_name, const pcl::PointCloud<T> &cloud)
{
    pcl::PointCloud<T> depth = cloud;

    double max = 0, min = 0;

    for (int i = 0; i < (int) depth.points.size(); i++) {
        if(depth.points[i].z > max) max = depth.points[i].z;
        if(depth.points[i].z < min) min = depth.points[i].z;
    }

    float scale = (max - min) / 256;

    for (int i = 0; i < (int) depth.points.size(); i++) {
        unsigned int t = depth.points[i].z / scale;

        // make sure pixel value between 0 ~ 255
        t = (t > 255) ? 255 : (t < 0) ? 0 : t;

        depth.points[i].rgba = ((t << 16) | (t << 8) | t);
    }

    savePNGFile(file_name, depth);
}

這個程式透過範圍的計算,將深度轉換成 0 ~ 255 的灰階值, 並將顏色塗到點雲上面, 最後再將點雲存檔成為 png 圖檔。

透過這樣的簡單步驟,我們就可以將 pcd 檔案轉換成為 RGB 彩圖 與 深度圖了。


下面列出簡單的主程式與結果

#include <iostream>
#include <string>
#include <cmath>
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>

typedef pcl::PointXYZRGBA PointT;

int main(int argc, char *argv[])
{
    std::string pcd_file("room.pcd");

    pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
    pcl::io::loadPCDFile(pcd_file, *cloud);

    // save origin pcd file to png pictre
    std::string rgb_file("room.png");
    savePNGFile(rgb_file, *cloud);

    // save depth image
    std::string depth_file("room_depth.png");
    saveDepthToPNG(depth_file, *cloud);

    return 0;
}

透過這個程式,你就可以將 room.pcd 轉換成以下兩張圖形

RGB 彩圖

深度圖