Open3DのPythonチュートリアルをC++に移植(その5) customized_visualizer.py
Open3Dは、2018年3月現在v0.1です。頻繁にアップデートがあると思われますので、この記事はすぐ風化する予定です。
customized_visualizerは、キー入力、アニメーション、画面キャプチャなどのサンプルを提供しています。
これと類似のテストがすでにc++で用意されていますので、基本的にはこのソースは見る必要がありません。(精神衛生上、取り残しがあると気持ち悪いから、移植してみたまでです。一部手抜きをしています。)
元のpythonソースはこちら。
Open3D/customized_visualization.py at 50a31d8daaf3218b767ddbacc0db4e4f125f13d6 · IntelVCL/Open3D · GitHub
C++の参考ソースはこちらとこちら。
Open3D/TestVisualizer.cpp at 50a31d8daaf3218b767ddbacc0db4e4f125f13d6 · IntelVCL/Open3D · GitHub
Open3D/TestDepthCapture.cpp at 50a31d8daaf3218b767ddbacc0db4e4f125f13d6 · IntelVCL/Open3D · GitHub
TestCustomizedVisualizer.cpp
// ---------------------------------------------------------------------------- // - Open3D: www.open3d.org - // ---------------------------------------------------------------------------- // The MIT License (MIT) // // Copyright (c) 2018 www.open3d.org // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // ---------------------------------------------------------------------------- #include <iostream> #include <memory> #include <thread> #include <Core/Core.h> #include <IO/IO.h> #include <Visualization/Visualization.h> #include <string> #include <iomanip> #include <sstream> using namespace three; bool CustomDrawGeometry(const std::vector<std::shared_ptr<const PointCloud>> &pcd_ptrs) { const std::string &window_name = "Open3D"; int width = 640; int height = 480; int left = 50; int top = 50; // The following code achieves the same effect as: // bool DrawGeometries({pcd}) Visualizer visualizer; if(visualizer.CreateWindow(window_name, width, height, left, top) == false) { std::cout << "[DrawGeometries] Failed creating OpenGL window." << std::endl; return false; } for(const auto &pcd_ptr : pcd_ptrs) { if(visualizer.AddGeometry(pcd_ptr) == false) { std::cout << "[DrawGeometries] Failed addomg geometry." << std::endl; std::cout << "[DrawGeometries] Possibly due to bad geometry or wrong geometry type." << std::endl; return false; } } visualizer.Run(); visualizer.DestroyWindow(); return true; } bool CustomDrawGeometryWithRotation(const std::vector<std::shared_ptr<const PointCloud>> &pcd_ptrs) { int width = 640; int height = 480; std::vector<std::shared_ptr<const Geometry>> geom_ptrs; geom_ptrs.assign(pcd_ptrs.begin(), pcd_ptrs.end()); // return DrawGeometriesWithAnimationCallback( {geom_ptrs}, [&](Visualizer *vis) { vis->GetViewControl().Rotate(10, 0); std::this_thread::sleep_for(std::chrono::milliseconds(30)); return false; }, "Spin", width, height); } bool CustomDrawGeometryLoadOption(const std::vector<std::shared_ptr<const PointCloud>> &pcd_ptrs) { const std::string &window_name = "Open3D"; int width = 640; int height = 480; int left = 50; int top = 50; Visualizer visualizer; if(visualizer.CreateWindow(window_name, width, height, left, top) == false) { std::cout << "[DrawGeometries] Failed creating OpenGL window." << std::endl; return false; } for(const auto &pcd_ptr : pcd_ptrs) { if(visualizer.AddGeometry(pcd_ptr) == false) { std::cout << "[DrawGeometries] Failed addomg geometry." << std::endl; std::cout << "[DrawGeometries] Possibly due to bad geometry or wrong geomeetry type." << std::endl; return false; } } RenderOption ro; ReadIJsonConvertible("../../lib/TestData/renderoption.json", ro); visualizer.GetRenderOption() = ro; visualizer.Run(); visualizer.DestroyWindow(); return true; } class CustomVisualizerWithKeyCallback : public Visualizer { protected: void KeyPressCallback(GLFWwindow *window, int key, int scannode, int action, int mods) override { if(action == GLFW_RELEASE){ return; } if(key == GLFW_KEY_K){ Eigen::Vector3d background_color = Eigen::Vector3d::Zero(); this->GetRenderOption().background_color_ = background_color; } if(key == GLFW_KEY_R){ RenderOption ro; ReadIJsonConvertible("../../lib/TestData/renderoption.json", ro); this->GetRenderOption() = ro; } if(key == GLFW_KEY_COMMA){ std::string depth_filename = "depth.png"; bool do_render = true; double depth_scale = 1000.0; std::string camera_filename = "camera.json"; this->CaptureDepthImage(depth_filename, do_render, depth_scale); PinholeCameraTrajectory camera; camera.extrinsic_.resize(1); this->view_control_ptr_->ConvertToPinholeCameraParameters( camera.intrinsic_, camera.extrinsic_[0]); WriteIJsonConvertible(camera_filename, camera); //auto depth = this->CaptureDepthFloatBuffer(); //DrawGeometries({depth}, "Depth", depth->width_, depth->height_); // Segmentation falt // maybe, a best solution is to use cv::imshow() } if(key == GLFW_KEY_PERIOD){ std::string filename = "image.jpg"; bool do_render =true; this->CaptureScreenImage(filename, do_render); //auto image = this->CaptureScreenFloatBuffer(); //DrawGeometries({image}, "Image", image->width_, image->height_); } UpdateRender(); } }; bool CustomDrawGeometryWithKeyCallback(const std::vector<std::shared_ptr<const PointCloud>> &pcd_ptrs) { const std::string &window_name = "Open3D"; int width = 640; int height = 480; int left = 50; int top = 50; // The following code achieves the same effect as: // bool DrawGeometries({pcd}) CustomVisualizerWithKeyCallback visualizer; if(visualizer.CreateWindow(window_name, width, height, left, top) == false) { std::cout << "[DrawGeometries] Failed creating OpenGL window." << std::endl; return false; } for(const auto &pcd_ptr : pcd_ptrs) { if(visualizer.AddGeometry(pcd_ptr) == false) { std::cout << "[DrawGeometries] Failed addomg geometry." << std::endl; std::cout << "[DrawGeometries] Possibly due to bad geometry or wrong geometry type." << std::endl; return false; } } visualizer.Run(); visualizer.DestroyWindow(); return true; } bool CustomDrawGeometryWithCameraTrajectory(const std::vector<std::shared_ptr<const PointCloud>> &pcd_ptrs) { PinholeCameraTrajectory trajectory; ReadPinholeCameraTrajectory("../../lib/TestData/camera_trajectory.json", trajectory); std::vector<std::shared_ptr<const Geometry>> geom_ptrs; geom_ptrs.assign(pcd_ptrs.begin(), pcd_ptrs.end()); // return DrawGeometriesWithAnimationCallback({geom_ptrs}, [&](Visualizer *vis) { static int index = -1; static PinholeCameraTrajectory trajectory; if(index == -1){ // overhead ReadPinholeCameraTrajectory("../../lib/TestData/camera_trajectory.json", trajectory); } if(index >= 0){ std::cout << "Capture image " << std::setfill('0') << std::setw(5) << std::to_string(index) << std::endl; std::string dpath, cpath; std::stringstream ss1, ss2; ss1 << "TempData/depth" << std::setfill('0') << std::setw(5) << std::to_string(index) << ".png"; ss1 >> dpath; ss2 << "TempData/image" << std::setfill('0') << std::setw(5) << std::to_string(index) << ".jpg"; ss2 >> cpath; // auto depth = vis->CaptureDepthFloatBuffer(); // auto image = vis->CaptureScreenFloatBuffer(); // WriteImage(dpath, *CreateImageFromFloatImage<uint16_t>(*depth)); // int quality = 90; // WriteImage(cpath, *CreateImageFromFloatImage<uint16_t>(*image), quality); // above is not work due to mendoukusai. vis->CaptureDepthImage(dpath, false); vis->CaptureScreenImage(cpath, false); } index++; if(index < trajectory.extrinsic_.size()){ // ViewControlWithCustomAnimation & auto ctr = vis->GetViewControl(); ctr.ConvertFromPinholeCameraParameters(trajectory.intrinsic_, trajectory.extrinsic_[index]); } else { vis->RegisterAnimationCallback(nullptr); } return true; }, "Capture Animation", trajectory.intrinsic_.width_, trajectory.intrinsic_.height_); } int main(int argc, char *argv[]) { SetVerbosityLevel(VerbosityLevel::VerboseAlways); std::shared_ptr<PointCloud> pcd; pcd = CreatePointCloudFromFile("../../lib/TestData/fragment.ply"); std::cout << "1. Customized visualization to mimic DrawGeometry" << std::endl; CustomDrawGeometry({pcd}); std::cout << "2. Customized visualization with a rotating view" << std::endl; CustomDrawGeometryWithRotation({pcd}); std::cout << "3. Customized visualization showing normal rendering" << std::endl; CustomDrawGeometryLoadOption({pcd}); std::cout << "4. Customized visualization with key press callbacks" << std::endl; std::cout << " Press 'K' to change background color to black" << std::endl; std::cout << " Press 'R' to load a customized render option, showing normals" << std::endl; std::cout << " Press ',' to capture the depth buffer and show it" << std::endl; std::cout << " Press '.' to capture the screen and show it" << std::endl; CustomDrawGeometryWithKeyCallback({pcd}); std::cout << "5. Customized visualization playing a camera trajectory" << std::endl; CustomDrawGeometryWithCameraTrajectory({pcd}); return 1; }
1. DrawGeometriesの代わりにVisualizerを自前で作って単に表示
2. コールバックを使って、アニメーション表示
3. jsonファイルに保存された画面設定を読み込んで表示
4. キー入力イベントコールバックを使って、インタラクティブに表示の変更、と、画面キャプチャ
5. アニメーションをしながらコールバックして、アニメーションを画像として保存。ただし、実画面サイズとカメラ内部パラメータの幅、高さが一致していないと上手く動かない。