Remove points outside defined 3D box inside PCL visualizer
Asked Answered
I

2

7

In a given point cloud, I want to remove all the points which are less than min and greater than max for all x, y and z direction. Below is the sample code:

#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/passthrough.h>
#include <pcl/visualization/pcl_visualizer.h>

// Define min and max for X, Y and Z
float minX = -0.1, minY = -0.5, minZ = -2.5;
float maxX = +0.1, maxY = +0.5, maxZ = +2.5;

int main (int argc, char** argv)
{
    pcl::visualization::PCLVisualizer viewer("Cloud Viewer");

    pcl::PointCloud<pcl::PointXYZRGBA>::Ptr body (new pcl::PointCloud<pcl::PointXYZRGBA>);
    pcl::io::loadPCDFile ("body.pcd", *body);

    pcl::PointCloud<pcl::PointXYZRGBA>::Ptr bodyFiltered (new pcl::PointCloud<pcl::PointXYZRGBA>);

    pcl::PassThrough<pcl::PointXYZRGBA> filter;
    filter.setInputCloud (body);
    filter.setFilterFieldName ("x");
    filter.setFilterLimits (minX, maxX);
    filter.setFilterFieldName ("y");
    filter.setFilterLimits (minY, maxY);
    filter.setFilterFieldName ("z");
    filter.setFilterLimits (minZ, maxZ);
    filter.filter (*bodyFiltered);

    viewer.addPointCloud (bodyFiltered,"body");
    viewer.spin();
    return 0;
}

It seems that only last filter is being applied. Any solution, please?

Impressionist answered 21/8, 2017 at 6:48 Comment(4)
Can you show some sample data, sample output and explain how you conclude that only the last filter is applied?Urson
@Yunnosch: The data was acquired from a sensor and it is very noisy. Presently, I am trying to find out the best bounds, so that I can remove most of the noise. By the way, is the above way looking correct to you?Impressionist
In case you do not want to remove the points from the cloud but want to get the indices of valid points you can use pcl::Clipper3D<PointT>::clipPointCloud3D.Radioactive
@GabrielDevillers: That seems a good alternative. However, my final objective is to remove the noise, which is present in the point cloud. I finally would like to have point cloud, which contains data bounded by the defined box.Impressionist
R
21

What about using pcl::CropBox? (documentation)

pcl::CropBox<pcl::PointXYZRGBA> boxFilter;
boxFilter.setMin(Eigen::Vector4f(minX, minY, minZ, 1.0));
boxFilter.setMax(Eigen::Vector4f(maxX, maxY, maxZ, 1.0));
boxFilter.setInputCloud(body);
boxFilter.filter(*bodyFiltered);

To know why this filter takes Vector4f (and not Vector3f) see the comments below and this question.

Radioactive answered 21/8, 2017 at 9:44 Comment(6)
Excellent. it works like charm. Thank you very much.Impressionist
@Gabriel Devillers, what is the use of 1.0 in setMin as well as in setMax? (documentation link you have mentioned is broken, probably because of PCL website update)Orthotropic
@Orthotropic link fixed. This is because PCL often uses homogeneous coordinates with 4 coordinates to work in 3D. I hard-coded w=1 which is the correct value to concatenate to coordinates x,y,z (from the question) in order to represent the 3D point x,y,z using homogeneous coordinates. That said, if you have a Vector4 which represents a point in space (w != 0) you can pass it without normalizing it yourself (i.e. getting w=1). I have no idea if passing a "point at infinity" (w = 0) would make sense for cropping.Radioactive
@GabrielDevillers, thanks a lot for updating the link and for addressing my question. The link you shared on Homogeneous coordinates is also helpful. Thank you for that as well. Just to confirm, by "... if you have a Vector4 which represents a point in space (w != 0) you can pass it without normalizing it yourself" you meant: if I have a point represented by [4, 8, 6, 2] in a Homogeneous coordinate system which corresponds to a point [2, 4, 3, 1] in Ecludian coordinate system, I can directly pass [4, 8, 6, 2], right?Orthotropic
@Orthotropic that is what I meant, but I just checked the source code and it does not seem to use the fourth coordinate, so it seems to only accept normalized homogeneous coordinates. Feel free to make tests to confirm or refute this hypothesis.Radioactive
Hi @GabrielDevillers in my application, setMin & setMax are not enough as the region I want to crop is somewhat tilted. So, just to confirm through setRotation & setTranslate I can rotate & translate, respectively, that parallelogram cropBox w.r.t. the coordinate system of the point cloud, not the coordinate system of cropBox itself, right? How can I rotate that parallelogram cropBox w.r.t. its own axis? Is it even possible?Orthotropic
S
4

You have found what the documentation makes clear.

PassThrough passes points in a cloud based on constraints for one particular field of the point type.

For multiple fields a different filter should be used, such as ConditionalRemoval

The following is untested, but it'll be something like this.

pcl::ConditionOr<PointT>::Ptr range_cond (new pcl::ConditionOr<PointT> ()); 
range_cond->addComparison (pcl::FieldComparison<PointT>::Ptr (new pcl::FieldComparison<PointT>("x", pcl::ComparisonOps::GT, minX)));
range_cond->addComparison (pcl::FieldComparison<PointT>::Ptr (new pcl::FieldComparison<PointT>("x", pcl::ComparisonOps::LT, maxX)));
range_cond->addComparison (pcl::FieldComparison<PointT>::Ptr (new pcl::FieldComparison<PointT>("y", pcl::ComparisonOps::GT, minY)));
range_cond->addComparison (pcl::FieldComparison<PointT>::Ptr (new pcl::FieldComparison<PointT>("y", pcl::ComparisonOps::LT, maxY)));
range_cond->addComparison (pcl::FieldComparison<PointT>::Ptr (new pcl::FieldComparison<PointT>("z", pcl::ComparisonOps::GT, minZ)));
range_cond->addComparison (pcl::FieldComparison<PointT>::Ptr (new pcl::FieldComparison<PointT>("z", pcl::ComparisonOps::LT, maxZ)));

pcl::ConditionalRemoval<PointT> range_filt;
range_filt.setInputCloud(body);
range_filt.setCondition (range_cond);
range_filt.filter(*bodyFiltered);
Sites answered 21/8, 2017 at 8:20 Comment(5)
I am checking it... I included the headers #include <pcl/filters/conditional_removal.h> and #include <pcl/impl/point_types.hpp> but still getting many errors such as error: ‘PointT’ was not declared in this scope, error: ‘range_cond’ was not declared in this scope etc.Impressionist
using PointT = pcl::PointXYZRGBA;Sites
I just realised that your code has removed the point cloud inside the defined bounds. Why!!!! I want to remove the points outside the box and keep the points inside the box. Can you please have a look again into it?Impressionist
@RaviJoshi Think about it. If you understood my answer you would have known how easy it was to fix my wrong-way-round logic. I've amended the answer. Anyway you changed your accepted answer so presumably copy+paste works for you.Sites
I appreciate your suggestion. I think it is much more flexible and can be applied in various kind of comparison. As you said, I also found out the logic to change the output. Indeed, it was interesting to know this solution. Anyway, the other solution was short and exact as per the requirement and can't be ignored without accepting it. Hope you understand it.Impressionist

© 2022 - 2024 — McMap. All rights reserved.