0
votes

I am working on a region growing algorithm in python for unstructured point clouds. I try to implement the algorithm according to http://pointclouds.org/documentation/tutorials/region_growing_segmentation.php#region-growing-segmentation and SEGMENTATION OF POINT CLOUDS USING SMOOTHNESS CONSTRAINT.
Using the same point cloud, I fail to reproduce the results with the pcl implementation, though all the steps and the parameters seem to be the same. My implementation yields 217 regions, whereas pcl's one gets 24 (oversegemented). The point cloud can be found:

https://github.com/scrox/region-growing/blob/master/test.txt

What causes this significant difference? I also welcome performance improvements on my python code.

import math
import numpy as np
from sklearn.neighbors import KDTree
unique_rows=np.loadtxt("test.txt")
tree    = KDTree(unique_rows, leaf_size=2) 
dist,nn_glob = tree.query(unique_rows[:len(unique_rows)], k=30) 

def normalsestimation(pointcloud,nn_glob,VP=[0,0,0]):
    ViewPoint = np.array(VP)
    normals = np.empty((np.shape(pointcloud)))
    curv    = np.empty((len(pointcloud),1))
    for index in range(len(pointcloud)):
        nn_loc = pointcloud[nn_glob[index]]
        COV = np.cov(nn_loc,rowvar=False)
        eigval, eigvec = np.linalg.eig(COV)
        idx = np.argsort(eigval)
        nor = eigvec[:,idx][:,0]
        if nor.dot((ViewPoint-pointcloud[index,:])) > 0:
            normals[index] = nor
        else:
            normals[index] = - nor
        curv[index] = eigval[idx][0] / np.sum(eigval)

     return normals,curv

def regiongrowing(pointcloud,nn_glob,theta_th = 'auto', cur_th = 'auto'):
    normals,curvature = normalsestimation(pointcloud,nn_glob=nn_glob)
    order             = curvature[:,0].argsort().tolist()
    region            = []
    if theta_th == 'auto':
        theta_th          = 15.0 / 180.0 * math.pi # in radians
    if cur_th == 'auto':
        cur_th            = np.percentile(curvature,98)
    while len(order) > 0:
        region_cur = []
        seed_cur   = []
        poi_min    = order[0] #poi_order[0]
        region_cur.append(poi_min)
        seed_cur.append(poi_min)
        order.remove(poi_min)
        for i in range(len(seed_cur)):
            nn_loc  = nn_glob[seed_cur[i]]
            for j in range(len(nn_loc)):
                nn_cur = nn_loc[j]
                if all([nn_cur in order , np.arccos(np.abs(np.dot(normals[seed_cur[i]],normals[nn_cur])))<theta_th]):
                    region_cur.append(nn_cur)
                    order.remove(nn_cur)
                    if curvature[nn_cur] < cur_th:
                        seed_cur.append(nn_cur)
        region.append(region_cur)
    return region

region  = regiongrowing(unique_rows,nn_glob)

Here is the pcl code:

#include <iostream>
#include <vector>
#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/segmentation/region_growing.h>

int
main (int argc, char** argv)
{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  if ( pcl::io::loadPCDFile <pcl::PointXYZ> ("test.pcd", *cloud) == -1)
  {
    std::cout << "Cloud reading failed." << std::endl;
    return (-1);
  }

  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);
  normal_estimator.setKSearch (30);
  normal_estimator.compute (*normals);


  pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;
  reg.setSearchMethod (tree);
  reg.setNumberOfNeighbours (30);
  reg.setInputCloud (cloud);
  reg.setInputNormals (normals);
  reg.setSmoothnessThreshold (15.0 / 180.0 * M_PI);
  reg.setCurvatureThreshold (0.088614);

  std::vector <pcl::PointIndices> clusters;
  reg.extract (clusters);

  std::cout << "Number of clusters is equal to " << clusters.size () << std::endl;
  std::cout << "First cluster has " << clusters[0].indices.size () << " points." << endl;
  std::cout << "These are the indices of the points of the initial" <<                          
     std::endl << "cloud that belong to the first cluster:" << std::endl;

  int counter = 0;
  while (counter < clusters[0].indices.size ())
  {
   std::cout << clusters[0].indices[counter] << ", ";
   counter++;
   }
   std::cout << std::endl;

   pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();
   pcl::visualization::CloudViewer viewer ("Cluster viewer");
   viewer.showCloud(colored_cloud);
   while (!viewer.wasStopped ())
   {
   }

   return (0);
}
1

1 Answers

2
votes

I believe it has to do with for i in range(len(seed_cur)): in your def regiongrowing(pointcloud,nn_glob,theta_th = 'auto', cur_th = 'auto'):. I believe when python uses a for loop, it doesn't actually loop through any elements that are added to the list once the loop has started. so for

if curvature[nn_cur] < cur_th:
      seed_cur.append(nn_cur)

It doesn't continue to loop through nn_cur. I modified your code slightly using a while loop. My result was 21 regions. Not exactly like it is in PCL, but closer. My function is regiongrowing1 for reference.


import math
import numpy as np
from sklearn.neighbors import KDTree


unique_rows=np.loadtxt("test.txt")
tree    = KDTree(unique_rows, leaf_size=2) 
dist,nn_glob = tree.query(unique_rows[:len(unique_rows)], k=30) 

def normalsestimation(pointcloud,nn_glob,VP=[0,0,0]):
    ViewPoint = np.array(VP)
    normals = np.empty((np.shape(pointcloud)))
    curv    = np.empty((len(pointcloud),1))
    for index in range(len(pointcloud)):
        nn_loc = pointcloud[nn_glob[index]]
        COV = np.cov(nn_loc,rowvar=False)
        eigval, eigvec = np.linalg.eig(COV)
        idx = np.argsort(eigval)
        nor = eigvec[:,idx][:,0]
        if nor.dot((ViewPoint-pointcloud[index,:])) > 0:
            normals[index] = nor
        else:
            normals[index] = - nor
        curv[index] = eigval[idx][0] / np.sum(eigval)
    return normals,curv
#seed_count = 0
#while seed_count < len(current_seeds)


def regiongrowing(pointcloud,nn_glob,theta_th = 'auto', cur_th = 'auto'):
    normals,curvature = normalsestimation(pointcloud,nn_glob=nn_glob)
    order             = curvature[:,0].argsort().tolist()
    region            = []
    if theta_th == 'auto':
        theta_th          = 15.0 / 180.0 * math.pi # in radians
    if cur_th == 'auto':
        cur_th            = np.percentile(curvature,98)
    while len(order) > 0:
        region_cur = []
        seed_cur   = []
        poi_min    = order[0] #poi_order[0]
        region_cur.append(poi_min)
        seed_cur.append(poi_min)
        order.remove(poi_min)
        for i in range(len(seed_cur)):
            nn_loc  = nn_glob[seed_cur[i]]
            for j in range(len(nn_loc)):
                nn_cur = nn_loc[j]
                if all([nn_cur in order , np.arccos(np.abs(np.dot(normals[seed_cur[i]],normals[nn_cur])))<theta_th]):
                    region_cur.append(nn_cur)
                    order.remove(nn_cur)
                    if curvature[nn_cur] < cur_th:
                        seed_cur.append(nn_cur)
        region.append(region_cur)
    return region

def regiongrowing1(pointcloud,nn_glob,theta_th = 'auto', cur_th = 'auto'):
    normals,curvature = normalsestimation(pointcloud,nn_glob=nn_glob)
    order             = curvature[:,0].argsort().tolist()
    region            = []
    if theta_th == 'auto':
        theta_th          = 15.0 / 180.0 * math.pi # in radians
    if cur_th == 'auto':
        cur_th            = np.percentile(curvature,98)
    while len(order) > 0:
        region_cur = []
        seed_cur   = []
        poi_min    = order[0] #poi_order[0]
        region_cur.append(poi_min)
        seedval = 0

        seed_cur.append(poi_min)
        order.remove(poi_min)
#        for i in range(len(seed_cur)):#change to while loop
        while seedval < len(seed_cur):
            nn_loc  = nn_glob[seed_cur[seedval]]
            for j in range(len(nn_loc)):
                nn_cur = nn_loc[j]
                if all([nn_cur in order , np.arccos(np.abs(np.dot(normals[seed_cur[seedval]],normals[nn_cur])))<theta_th]):
                    region_cur.append(nn_cur)
                    order.remove(nn_cur)
                    if curvature[nn_cur] < cur_th:
                        seed_cur.append(nn_cur)
            seedval+=1
        region.append(region_cur)
    return region

region= regiongrowing(unique_rows,nn_glob)
region1  = regiongrowing1(unique_rows,nn_glob)

Try that and see what you get.