This section explains how to access data from the CVB point cloud classes in C++ and .NET:
For more details on the concept behind the CVB point cloud classes, please refer to the section CVB Point Cloud.
Accessing Point Cloud Data via Pointers
This section describes a generic approach to accessing point cloud data. However, C++ offers more convenient, flexible and recommended methods for data access, outlined in section C++: Flexible and Fast Pixel Access with Cvb::Visit.
An easy way to access point cloud values is using a pointer to a 3D point structure in C++ or an array created from an enumeration in .NET:
#include <cvb/point_cloud.hpp>
using PointType = float;
auto cloud = ...;
cloud->Points(pPoints);
auto end = pPoints + cloud->NumPoints();
for (auto it = pPoints; it != end; it++)
{
auto X = it->X();
auto Y = it->Y();
auto Z = it->Z();
}
using (var cloud = ...)
{
var points = cloud.TryEnumeratePointsAs<
Point3Df>().ToArray();
for (int i = 0; i < cloud.NumPoints; i++)
{
var X = points[i].X;
var Y = points[i].Y;
var Z = points[i].Z;
}
}
(1) The pointer to the 3D points is obtained in C++, while in .NET, the points are retrieved as an array from an enumeration.
(2) X, Y, and Z values are accessed through this pointer in C++ or via indexed access in .NET. Note that in .NET, point data can be read but not modified here.
Alternatively point clouds can be access via the base pointers and increments as demonstrated in the following example:
auto nPoints = cloud->NumPoints();
auto pPointX = components.BasePtrX();
auto pPointY = components.BasePtrY();
auto pPointZ = components.BasePtrZ();
auto xInc = components.XInc();
auto yInc = components.YInc();
auto zInc = components.ZInc();
for (int i = 0; i < nPoints; i++)
{
auto X = *reinterpret_cast<PointType *>(pPointX);
auto Y = *reinterpret_cast<PointType *>(pPointY);
auto Z = *reinterpret_cast<PointType *>(pPointZ);
pPointX += xInc;
pPointY += yInc;
pPointZ += zInc;
}
using (var cloud = ...)
{
var nPoints = cloud.NumPoints;
var components = cloud.PointComponents;
var xInc = (int)components.X.Increment;
var yInc = (int)components.Y.Increment;
var zInc = (int)components.Z.Increment;
unsafe
{
var pPointX = (byte*)components.X.BasePtr;
var pPointY = (byte*)components.Y.BasePtr;
var pPointZ = (byte*)components.Z.BasePtr;
for (int i = 0; i < nPoints; i++)
{
var X = *(float*)pPointX;
var Y = *(float*)pPointY;
var Z = *(float*)pPointZ;
pPointX += xInc;
pPointY += yInc;
pPointZ += zInc;
}
}
}
(1) The base pointers and increments for each component (in this case X, Y, and Z) are retrieved.
(2) X, Y, and Z values are accessed via these pointers and casted correctly.
(3) The pointers are incremented accordingly.
Note, that for organized (dense) point clouds you can iterate over width and height instead of the total number of points, too:
auto width = cloud->LatticeSize().Width();
auto height = cloud->LatticeSize().Height();
auto pBaseX = components.BasePtrX();
auto pBaseY = components.BasePtrY();
auto pBaseZ = components.BasePtrZ();
auto xInc = components.XInc();
auto yInc = components.YInc();
auto zInc = components.ZInc();
for (int y = 0; y < height; y++)
{
auto pLineX = pBaseX + y * width * xInc;
auto pLineY = pBaseY + y * width * yInc;
auto pLineZ = pBaseZ + y * width * zInc;
for (int x = 0; x < width; x++)
{
auto pPointX = pLineX + x * xInc;
auto pPointY = pLineY + x * yInc;
auto pPointZ = pLineZ + x * zInc;
auto X = *reinterpret_cast<PointType *>(pPointX);
auto Y = *reinterpret_cast<PointType *>(pPointY);
auto Z = *reinterpret_cast<PointType *>(pPointZ);
}
}
{
var yInc = (int)components.Y.Increment;
var zInc = (int)components.Z.Increment;
unsafe
{
var pBaseX = (byte*)components.X.BasePtr;
var pBaseY = (byte*)components.Y.BasePtr;
var pBaseZ = (byte*)components.Z.BasePtr;
for (int y = 0; y < height; y++)
{
var pLineX = pBaseX + y * width * xInc;
var pLineY = pBaseY + y * width * yInc;
var pLineZ = pBaseZ + y * width * zInc;
for (int x = 0; x < width; x++)
{
var pPointX = pLineX + x * xInc;
var pPointY = pLineY + x * yInc;
var pPointZ = pLineZ + x * zInc;
var X = *(float*)pPointX;
var Y = *(float*)pPointY;
var Z = *(float*)pPointZ;
}
}
}
}
ComponentsPointer3D PointComponents
(1) Get width and height of dense point cloud.
(2) The base pointers and increments for each component (in this case X, Y, and Z) are retrieved.
(3) Loop over grid of cloud.
(4) Get pointer to next line.
(5) Get pointer to next element.
(6) X, Y, and Z values are accessed via these pointers and casted correctly.
C++: Flexible and Fast Pixel Access with Cvb::Visit
The Cvb::Visit
function provides the fastest and most flexible way to access point cloud data. It allows you to write algorithms only once independently of the data type and memory layout.
- Note
- This approach is described in detail in section Efficiently Writing Image and Point Cloud Algorithms Using Visit.
The following example demonstrates how to process a simple point cloud using a lambda function. More advanced examples, including the use of function objects and simultaneous access to additional planes, can be found in Efficiently Write Image and Point Cloud Algorithms Using Visit.
#include <cvb/point_cloud.hpp>
#include <cvb/block.hpp>
auto cloud = ...;
Cvb::Visit(
[](auto block) {
for (int y = 0; y < block.Height(); ++y)
{
for (int x = 0; x < block.Width(); ++x)
{
auto X = block(x, y).X();
auto Y = block(x, y).Y();
auto Z = block(x, y).Z();
}
}
},
*cloud);
Examples and Further Reading
Point Cloud Concept
Point Cloud Stream
Code Example (C++): Point Cloud Acquisition