PCLで時系列点群データを可視化する
はじめに
PCL(Point Cloud Library)を使って時系列点群データ(動画のように撮影された3次元点群データの集まり)を可視化する方法を紹介します。
可視化のイメージはこんな感じです。
実装
pcl::visualization::PCLVisualizer
を使います。
PCLが提供している可視化クラスは、一つの動画ファイルとして保存されるような時系列点群データには対応していません。
そのため、フレームごとにpcl::PointCloud
型にデータを入れ、表示する点群データを都度切り替えることにより、コマ送り(あるいはパラパラ漫画)の要領で可視化を行います。
この発想を踏まえてpcl::visualization::PCLVisualizer
のリファレンスを見てみると、updatePointCloud
という関数が目に留まります。
updatePointCloud
では、既にaddPointCloud
された点群データのid
を指定し、表示される点群データを上書きします。
さて、以上の情報をもとにプログラムを作成すると、次のようになります。
vis->spinOnce
とstd::this_thread::sleep_for
の時間は適当です。
using PointXYZRGB = pcl::PointXYZRGB;
using CloudXYZRGB = pcl::PointCloud<PointXYZRGB>;
using PCLVisualizer = pcl::visualization::PCLVisualizer;
using namespace std::chrono_literals;
void visualizeVideo(const std::vector<CloudXYZRGB::Ptr>& video) {
const int size = video.size();
if (size == 0) {
return;
}
PCLVisualizer::Ptr vis(new PCLVisualizer);
int index = 0;
const std::string id_cloud = "cloud";
vis->addPointCloud(video[index], id_cloud);
const int x_pos = 100;
const int y_pos = 100;
const std::string id_text = "text";
vis->addText(std::to_string(index + 1) + "/" + std::to_string(size), x_pos, y_pos, id_text);
while (!vis->wasStopped()) {
vis->spinOnce(15);
std::this_thread::sleep_for(15ms);
index++;
if (index == size) {
index = 0;
}
vis->updatePointCloud(video[index], id_cloud);
vis->updateText(std::to_string(index + 1) + "/" + std::to_string(size), x_pos, y_pos, id_text);
}
}
デモンストレーションのため、疑似的な動画を作成する関数も定義します。
void makeVideoExample(std::vector<CloudXYZRGB::Ptr>& video_out) {
// parameters
const int x_min = -30;
const int x_max = 30;
const int y_max = 30;
const int frames = 100;
const int cloud_width = x_max - x_min + 1;
// define gaussian distribution
const double pow_sigma = 100.0;
const double coeff = 1.0 / sqrt(2.0 * M_PI * pow_sigma);
auto calculateGauss = [&coeff, &pow_sigma](double val) -> double {
return coeff * std::exp(-1.0 * val * val / (2.0 * pow_sigma));
};
const double normalize_coeff = 1.0 / calculateGauss(0.50 * (x_min + x_max));
// resize output vector
video_out.clear();
video_out.resize(frames);
// make pseudo-video
const double dr = 0.50 / frames;
double rate = 0.0;
for (int i = 0; i < frames; i++, rate += dr) {
// reset shared_ptr
video_out[i].reset(new CloudXYZRGB);
video_out[i]->resize(cloud_width);
int id_cloud = 0;
// write info
for (int x = x_min; x <= x_max; x++, id_cloud++) {
PointXYZRGB point;
point.x = x;
point.y = rate * calculateGauss(x) * normalize_coeff * y_max;
point.r = 255;
point.g = 255;
point.b = 255;
video_out[i]->at(id_cloud) = point;
}
}
}
以上の関数をmain
の中で呼び出します。
int main() {
std::vector<CloudXYZRGB::Ptr> video;
makeVideoExample(video);
visualizeVideo(video);
return 0;
}
これで、冒頭の可視化が得られます。
おわりに
本記事では、PCLで時系列点群データを可視化する方法を紹介しました。
元々、1年以上前に同じことを試みたのですが、当時はC/C++の理解度とリファレンスを読む能力が足りずに挫折したので、プログラムを作ることができて自身の成長を感じました。
質問や疑問などは、コメント欄かX(Twitter)でご連絡ください。
本記事が少しでもお役に立てば幸いです。
Discussion