6

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?

ravi
  • 6,140
  • 18
  • 77
  • 154
  • Can you show some sample data, sample output and explain how you conclude that only the last filter is applied? – Yunnosch Aug 21 '17 at 06:50
  • @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? – ravi Aug 21 '17 at 08:12
  • 1
    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::clipPointCloud3D`. – Gabriel Devillers Aug 21 '17 at 08:29
  • @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. – ravi Aug 21 '17 at 08:36

2 Answers2

20

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.

Gabriel Devillers
  • 3,155
  • 2
  • 30
  • 53
  • Excellent. it works like charm. Thank you very much. – ravi Aug 21 '17 at 10:03
  • @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) – Milan Aug 11 '20 at 19:25
  • 1
    @Milan link fixed. This is because PCL often uses [homogeneous coordinates](https://stackoverflow.com/q/3847743) 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. – Gabriel Devillers Aug 12 '20 at 08:07
  • @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? – Milan Aug 12 '20 at 18:07
  • 1
    @Milan 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. – Gabriel Devillers Aug 13 '20 at 07:13
  • 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? – Milan Sep 15 '20 at 21:39
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);
acraig5075
  • 10,588
  • 3
  • 31
  • 50
  • I am checking it... I included the headers `#include ` and `#include ` 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. – ravi Aug 21 '17 at 08:33
  • 1
    `using PointT = pcl::PointXYZRGBA;` – acraig5075 Aug 21 '17 at 08:41
  • 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? – ravi Aug 21 '17 at 09:56
  • @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. – acraig5075 Aug 21 '17 at 10:09
  • 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. – ravi Aug 21 '17 at 10:30