앞에서는 평면과 실린더를 기반으로 세그먼테이션을 처리해 보았다. 이 기법들은 몇몇 한계가 있는 데, 평면은 평면만 추출하는 문제, 실린더 추출은 실린더 내 세밀한 곡률의 변화를 구분하지 못하는 문제가 있다. 그러므로, 이 글에서는 곡률을 기반으로 형상을 분리하는 예를 통해, 이런 문제를 좀 더 개선해 보려 한다.
1. 개요
곡률 기반 세그먼테이션은 포인트의 법선 벡터 N을 이용해, 이웃점 N과의 곡률 C를 계산하고, 곡률의 차이를 이용해, 형상을 세그먼테이션 하는 기술이다. 각 점의 C를 계산하고, 유사도에 의해, 점을 그룹핑한다. 이는 유사 특성을 가진 점의 영역을 키워나가는 것이므로 region growing 알고리즘으로도 알려져 있다. 이 결과로 변곡점 부분을 분리해, 포인트 클라우드를 곡률 단위로 분해한다. 자세한 부분은 다음 내용을 참조한다.
2. 코딩/빌드
다음과 같이 코딩하고 빌드한다.
#include <ros/ros.h>
// PCL specific includes
#include <sensor_msgs/PointCloud2.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/ros/conversions.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/search.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/region_growing.h>
ros::Publisher pub;
typedef pcl::PointXYZ PointT;
void
cloud_cb (const sensor_msgs::PointCloud2ConstPtr& input)
{
// Convert the sensor_msgs/PointCloud2 data to pcl/PointCloud
pcl::PointCloud<pcl::PointXYZ> cloud;
pcl::fromROSMsg (*input, cloud);
std::vector<int> indices;
pcl::removeNaNFromPointCloud(cloud, cloud, indices);
std::cout << "cloud points = " << cloud.size () << std::endl;
pcl::search::Search<pcl::PointXYZ>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZ> > (new pcl::search::KdTree<pcl::PointXYZ>);
pcl::PointCloud <pcl::Normal>::Ptr normals (new pcl::PointCloud <pcl::Normal>);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;
normal_estimator.setSearchMethod (tree);
normal_estimator.setInputCloud (cloud.makeShared());
normal_estimator.setKSearch (50);
normal_estimator.compute (*normals);
std::vector <pcl::PointIndices> clusters;
pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;
reg.setMinClusterSize (50);
reg.setMaxClusterSize (1000000);
reg.setSearchMethod (tree);
reg.setNumberOfNeighbours (50);
reg.setInputCloud (cloud.makeShared());
reg.setInputNormals (normals);
reg.setSmoothnessThreshold (3.0 / 180.0 * M_PI);
reg.setCurvatureThreshold (1.0);
reg.extract (clusters);
std::cout << "Number of clusters is equal to " << clusters.size () << std::endl;
pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();
// Convert To ROS data type
pcl::PCLPointCloud2 cloud_p;
pcl::toPCLPointCloud2(*colored_cloud, cloud_p);
sensor_msgs::PointCloud2 output;
pcl_conversions::fromPCL(cloud_p, output);
output.header.frame_id = "camera_depth_optical_frame";
pub.publish(output);
ROS_INFO("published it.");
}
int
main (int argc, char** argv)
{
// Initialize ROS
ros::init (argc, argv, "my_pcl_tutorial");
ros::NodeHandle nh;
// Create a ROS subscriber for the input point cloud
ros::Subscriber sub = nh.subscribe ("camera/depth/points", 1, cloud_cb);
// Create a ROS publisher for the output model coefficients
// pub = nh.advertise<pcl_msgs::ModelCoefficients> ("pclplaneoutput", 1);
pub = nh.advertise<sensor_msgs::PointCloud2> ("pclplaneoutput", 1);
// Spin
ros::spin ();
}
3. 실험
rosrun으로 실행한다.
그리고 rviz를 통해 확인하면, 곡률에 따라 세그먼테이션이 되고 있는 모습을 볼 수 있다.
파라메터를 조정하면, 얼마나 민감하게 곡률에 따라 클러스터링할 것인지를 조정할 수 있다. 아래 파이프를 보면, 크게 6개 영역으로 구분된 것을 확인할 수 있다.
다음 실제 파이프와 비교해 보았을 때, 곡률 경계에 따라 어느정도 적절히 구분되었음을 확인할 수 있다.
4. 결과
곡률에 따라 포인트 클라우드를 분리하여, 클러스터링하는 region growing 알고리즘은 형상을 곡률을 기반으로 한 세밀한 단위로 분해할 수 있도록 한다. 다만, 법선, 곡률 계산 및 클러스터링 처리까지 포함되어 있어, 계산 속도가 늦다. 만약, 뭉쳐진 포인트 클라우드 단위로 클러스터링 한다면, 유클리드 거리 기반 클러스터링 알고리즘을 사용한 것이 낫다.
우리 가족은 이제 아주 새로운 것이므로 걱정을하지 말고 약을 먹고 가족을 꾸리십시오. 당신의 일상 생활에서 대마초 기름을 사용하여 건강을 더 좋게 만드십시오. 모든 종류의 암 치료법에 대해 ( drpatrickoscar@yandex.com ) Dr.Patrick에 문의하십시오.
답글삭제