Dear @stevebaer and @dale this is my source code, do you think it is possible to avoid conversion to doubles and directly pass C# PointCloud to C++ ? (Let me know if you need compiled dll, I can add it too)
The pointcloud processing methods I am using is fast and the main bottleneck is this data transfer via PInvoke.
Headers
C#:
[DllImport(dllNameOpen3D, CallingConvention = CallingConvention.Cdecl)]
internal static extern int Open3DDownsample(
[MarshalAs(UnmanagedType.LPArray)] double[] p, ulong p_c,
[MarshalAs(UnmanagedType.LPArray)] double[] n, ulong n_n,
[MarshalAs(UnmanagedType.LPArray)] double[] c, ulong c_c,
int numberOfPoints,
ref IntPtr p_o, ref int p_c_o,
ref IntPtr n_o, ref int n_n_o,
ref IntPtr c_o, ref int c_c_o
);
C++:
PINVOKE void Open3DDownsample (
double* p, size_t p_c,
double* n, size_t n_c,
double* c, size_t c_c,
int numberOfPoints,
double*& p_o, int& p_c_o,
double*& n_o, int& n_c_o,
double*& c_o, int& c_c_o
);
Implementations:
C# :
public static PointCloud Downsample(PointCloud cloud, int numberOfPoints = 5000) {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Conversion from Rhino PointCloud to Flat Array
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double[] p = new double[cloud.Count * 3];
ulong p_c = (ulong)cloud.Count;
double[] n =new double[cloud.Count * 3] ;
ulong n_c = p_c;
double[] c = new double[cloud.Count * 3];
ulong c_c = p_c;
bool hasColors = cloud.ContainsColors;
System.Threading.Tasks.Parallel.For(0, cloud.Count, i =>
{
//for (int i = 0; i < cloud.Count; i++) {
p[i * 3 + 0] = cloud[i].Location.X;
p[i * 3 + 1] = cloud[i].Location.Y;
p[i * 3 + 2] = cloud[i].Location.Z;
if (cloud.ContainsNormals) {
n[i * 3 + 0] = cloud[i].Normal.X;
n[i * 3 + 1] = cloud[i].Normal.Y;
n[i * 3 + 2] = cloud[i].Normal.Z;
}
if (cloud.ContainsColors) {
c[i * 3 + 0] = cloud[i].Color.R;
c[i * 3 + 1] = cloud[i].Color.G;
c[i * 3 + 2] = cloud[i].Color.B;
}
});
//watch.Stop();
// Rhino.RhinoApp.WriteLine("Convert PointCloud to double array "+watch.ElapsedMilliseconds.ToString());
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Call C++
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
IntPtr p_pointer = IntPtr.Zero;
int p_pointer_c = 0;
IntPtr n_pointer = IntPtr.Zero;
int n_pointer_c = 0;
IntPtr c_pointer = IntPtr.Zero;
int c_pointer_c = 0;
UnsafeOpen3D.Open3DDownsample(
p, p_c,
n, n_c,
c, c_c,
numberOfPoints,
ref p_pointer, ref p_pointer_c,
ref n_pointer, ref n_pointer_c,
ref c_pointer, ref c_pointer_c
);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Convert Pointers to double arrays
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Convert faceIndicesPointer to C# int[]
double[] P = new double[p_pointer_c ];
Marshal.Copy(p_pointer, P, 0, P.Length);
double[] N = new double[n_pointer_c ];
Marshal.Copy(n_pointer, N, 0, P.Length);
double[] C = new double[c_pointer_c ];
Marshal.Copy(c_pointer, C, 0, P.Length);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Release C++ memory
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UnsafeOpen3D.ReleaseDouble(p_pointer, true);
UnsafeOpen3D.ReleaseDouble(n_pointer, true);
UnsafeOpen3D.ReleaseDouble(c_pointer, true);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Convert C++ to C# Pointcloud
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PointCloud newCloud = new PointCloud();
for (int i = 0; i < P.Length; i += 3) {
newCloud.Add(new Point3d(P[i + 0], P[i + 1], P[i + 2]));
if (C[i + 0] == 0 && C[i + 1] == 0 && C[i + 2] == 0) continue;
newCloud[(int)Math.Round((i / 3.0))].Color = System.Drawing.Color.FromArgb((int)C[i + 0], (int)C[i + 1], (int)C[i + 2]);
}
for (int i = 0; i < P.Length; i += 3) {
if (N[i + 0] == 0 && N[i + 1] == 0 && N[i + 2] == 0) continue;
newCloud[(int)Math.Round((i / 3.0))].Normal = new Vector3d(N[i + 0], N[i + 1], N[i + 2]);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Output
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return newCloud;
}
C++:
PINVOKE void Open3DDownsample (
double* p, size_t p_c,
double* n, size_t n_c,
double* c, size_t c_c,
int numberOfPoints,
double*& p_o, int& p_c_o,
double*& n_o, int& n_c_o,
double*& c_o, int& c_c_o) {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Convert Input to Open3D PointCloud
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
shared_ptr<PC> Open3DCloud (new PC);
Open3DCloud->points_.resize (p_c);
Open3DCloud->normals_.resize (p_c);
Open3DCloud->colors_.resize (p_c);
for ( size_t i = 0; i < p_c; i++ ) {
Open3DCloud->points_[i] = Eigen::Vector3d (p[3 * i + 0], p[3 * i + 1], p[3 * i + 2]);
Open3DCloud->normals_[i] = Eigen::Vector3d (n[3 * i + 0], n[3 * i + 1], n[3 * i + 2]);
Open3DCloud->colors_[i] = Eigen::Vector3d (c[3 * i + 0], c[3 * i + 1], c[3 * i + 2]);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Run Open3D Method
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int nOfPoints = numberOfPoints;
int b = (int)(Open3DCloud->points_.size () * (1.0 / numberOfPoints *1.0));
int nth = max (1,b );
Open3DCloud = Open3DCloud->UniformDownSample (nth);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Run Open3D Method
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
p_c_o = Open3DCloud->points_.size ()*3;
p_o = new double[p_c_o];
n_c_o = Open3DCloud->normals_.size ()*3 ;
n_o = new double[n_c_o];
c_c_o = Open3DCloud->colors_.size ()*3 ;
c_o = new double[c_c_o];
int i = 0;
for ( auto& p : Open3DCloud->points_ ) {
p_o[i++] = p.x ();
p_o[i++] = p.y ();
p_o[i++] = p.z ();
}
i = 0;
for ( auto& p : Open3DCloud->normals_ ) {
n_o[i++] = p.x ();
n_o[i++] = p.y ();
n_o[i++] = p.z ();
}
i = 0;
for ( auto& p : Open3DCloud->colors_ ) {
c_o[i++] = p.x ();
c_o[i++] = p.y ();
c_o[i++] = p.z ();
}
}
Something is just wrong with Add and AddRange constructor when 3 values are given, look at the performance below for 5000 points for creating a pointcloud in .NET. And yes I refereshed several times components to get a correct time stamp: