About the Normals Point type

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

About the Normals Point type

garratt
Hi All,

as promised, I'm starting a thread to discuss the normals point type

I feel that the Normals point type is unnecessary.  A normal has no
meaning without the point it belongs to, so why don't we just use a
pointXYZ type with a normal, such as PointXYZINormal?

It would make a number of functions a lot easier, for example
extractSubCloud would only have to be run once for PointXYZINormal, when
it would have to be run twice if you have your normals cloud as well as
your PointXYZ cloud.

In fact, I've already went and written a bunch of helper classes for
myself that make it so I never have to see the normals type.

It also has the effect of forcing the point cloud class to consider this
odd class when deciding general functions, when really, it only
intuitively makes sense to have the point cloud deal with actual points!
(I know this is circular reasoning when combined with my arguments for
including more helper functions in the pointcloud class, but it would
solve both problems)


Ok, done ranting. (about this)

cheers
Garratt



_______________________________________________
[hidden email] / http://pcl.ros.org
https://code.ros.org/mailman/listinfo/pcl-users
Reply | Threaded
Open this post in threaded view
|

Re: About the Normals Point type

Radu B. Rusu
Administrator
Warning: large e-mail ! :)

Since this e-mail involves some historical explanations, please take some time to read it...

On 10/07/2010 03:56 PM, garratt wrote:
> Hi All,
>
> as promised, I'm starting a thread to discuss the normals point type
>
> I feel that the Normals point type is unnecessary.  A normal has no
> meaning without the point it belongs to, so why don't we just use a
> pointXYZ type with a normal, such as PointXYZINormal?

Interesting.

Okay, I can provide some insight on why we designed the current code as it is, and maybe we can iterate over possible
changes before 1.0 hits the shelves. :)

The reasons why we wanted to have Normal separate from XYZ are threefold:

* One one hand, point clouds are huge. The XYZ data that we get from our stereo or lasers is _the largest_ data
structure in ROS, and there's nothing coming close to it, except maybe high resolution monocular images. But even in
that case, you can imagine people doing stereo with high resolution cameras, which makes the XYZ+ data "win" again at
the "largest hog" category. :)

Because of this, it seemed bad to create an even larger structure pcl::PointCloud<PointXYZINormal> that contains both
the original XYZI and the Normal, as now the serialization/deserialization takes longer, the packets are bigger (over
WiFi we might run into all sorts of troubles, with packets having to be resent, etc). Our first attempt at doing so was
a disaster, because we started running into synchronization issues (say you want Image + PointCloud<XYZ> +
PointCloud<Normal> synchronized... there might be cases where Image + PointCloud<XYZNormal> is harder to keep
synchronized and will force you to bump up your subscriber queue size, etc)...



* It became obvious that it will be hard to decide "where to draw the line" (similar to the other problem/thread :) on
what 3D features should be attached to XYZ and what not. Examples:

   - should Normal be attached to PointXYZ => PointXYZNormal
   - what about Boundary point information (0 if not on a 3D boundary/edge, 1 otherwise) => PointXYZINormalBoundary
   - what about FPFH features? or Spin images? or moment invariants? etc =>
PointXYZINormalBoundaryFPFHSpinMomentInvariants :)

In some sense all of them need the 3D XYZ information in order to be interpreted, but...



* it's not necessary always the case that Normal information is not useful by itself/alone. One could imagine a simple
algorithm that:

    - takes a PointCloud<XYZ> cluster as input
    - computes a PointCloud<Normal> cluster as output
    - performs statistics over the normal space and if X% or median or whatever, is parallel/perpendicular to some axis,
etc, the cluster gets rejected or accepted...

So in some "other" sense, there might be users out there who do not want to pay the penalty of copying over the XYZ
information (it's not the memcpy that's killing us, but the serialization/deserialization in ROS, and the extra
memory/data), especially when they already have the XYZ in memory. A simple example would be a PointCloud<XYZ> that is
2GB in RAM, and the output PointCloud<Normal> is 2GB more, but you only have 4GB of RAM or something... :) you get my point.




> It would make a number of functions a lot easier, for example
> extractSubCloud would only have to be run once for PointXYZINormal, when
> it would have to be run twice if you have your normals cloud as well as
> your PointXYZ cloud.

So that's true, in the case where people need to extract smaller pointclouds from larger ones, this is problematic. We
could however do a variadic template, or some general purpose set of methods, that accept X pointclouds as input, and
produce X sub-pointclouds as output given some indices vector.


The interesting paradigm shift that we introduced in PCL however is the use of indices (either via std::vector<int> or
via pcl::PointIndices) all over the place.

This means that for a PointCloud<XYZ> and a PointCloud<Normal>, you don't need to run an extractSubCloud to get both as
two separate point clouds, but instead, you can make your method do something like:

object.setInputCloud (PointCloud<XYZ>)
object.setInputNormals (PointCloud<Normal>)
object.setIndices (indices)

and basically use the same <for int i = 0; i < indices.size; ++i> loop to iterate and use both datasets at once.




Two more things to be said here:

* You can easily use a PointCloud<PointXYZNormal> with any methods that requires either PointCloud<XYZ> or
PointCloud<Normal> (bless templating! :) )

* We made it trivial to concatenate Point clouds together so that PointCloud<XYZ> + PointCloud<Normal> =
PointCloud<XYZINormal> for situations where that is required.

In fact there is no constraint that your PointCloud be XYZ or XYZNormal, as the templating will take care of it anyway.
The above example:

object.setInputCloud (PointCloud<XYZ>)
object.setInputNormals (PointCloud<Normal>)
object.setIndices (indices)

works just as well like this:

object.setInputCloud (PointCloud<XYZNormal>)
object.setInputNormals (PointCloud<XYZNormal>)
object.setIndices (indices)




> Ok, done ranting. (about this)

These discussions are _really_ helpful btw in understanding what PCL should become for all of us, so thanks!. We all
have different experiences and backgrounds, so please don't hesitate to "rant" about any of this stuff. :) The fact that
PCL is built this way is only because the group of people involved from Willow Garage thought that this might be a good
way to go, but the more core developers and users we get, the faster we will converge to an "optimal" framework that
everyone can use and benefit from.

Cheers,
Radu.
_______________________________________________
[hidden email] / http://pcl.ros.org
https://code.ros.org/mailman/listinfo/pcl-users