11# cython: embedsignature=True
22
3+ from collections import Sequence
4+ import numbers
35import numpy as np
46
57cimport numpy as cnp
68
79cimport pcl_defs as cpp
810
11+ cimport cython
912from cython.operator import dereference as deref
1013from libcpp.string cimport string
1114from libcpp cimport bool
@@ -117,12 +120,31 @@ cdef class SegmentationNormal:
117120 mpcl_sacnormal_set_axis(deref(self .me),ax,ay,az)
118121
119122cdef class PointCloud:
120- """
121- Represents a class of points, supporting the PointXYZ type.
123+ """ Represents a cloud of points in 3-d space.
124+
125+ A point cloud can be initialized from either a NumPy ndarray of shape
126+ (n_points, 3), from a list of triples, or from an integer n to create an
127+ "empty" cloud of n points.
128+
129+ To load a point cloud from disk, use pcl.load.
122130 """
123131 cdef cpp.PointCloud[cpp.PointXYZ] * thisptr
124- def __cinit__ (self ):
132+
133+ def __cinit__ (self , init = None ):
125134 self .thisptr = new cpp.PointCloud[cpp.PointXYZ]()
135+
136+ if init is None :
137+ return
138+ elif isinstance (init, (numbers.Integral, np.integer)):
139+ self .resize(init)
140+ elif isinstance (init, np.ndarray):
141+ self .from_array(init)
142+ elif isinstance (init, Sequence):
143+ self .from_list(init)
144+ else :
145+ raise TypeError (" Can't initialize a PointCloud from a %s "
146+ % type (init))
147+
126148 def __dealloc__ (self ):
127149 del self .thisptr
128150 property width :
@@ -138,6 +160,7 @@ cdef class PointCloud:
138160 """ property containing whether the cloud is dense or not """
139161 def __get__ (self ): return self .thisptr.is_dense
140162
163+ @ cython.boundscheck (False )
141164 def from_array (self , cnp.ndarray[cnp.float32_t , ndim = 2 ] arr not None ):
142165 """
143166 Fill this object from a 2D numpy array (float32)
@@ -149,43 +172,43 @@ cdef class PointCloud:
149172 self .thisptr.width = npts
150173 self .thisptr.height = 1
151174
175+ cdef cpp.PointXYZ * p
152176 for i in range (npts):
153- self .thisptr.at(i).x = arr[i,0 ]
154- self .thisptr.at(i).y = arr[i,1 ]
155- self .thisptr.at(i).z = arr[i,2 ]
177+ p = & self .thisptr.at(i)
178+ p.x, p.y, p.z = arr[i, 0 ], arr[i, 1 ], arr[i, 2 ]
156179
180+ @ cython.boundscheck (False )
157181 def to_array (self ):
158182 """
159183 Return this object as a 2D numpy array (float32)
160184 """
161185 cdef float x,y,z
162186 cdef cnp.npy_intp n = self .thisptr.size()
163- cdef cnp.ndarray[float , ndim= 2 ] result = np.empty([n,3 ], dtype = np.float32)
187+ cdef cnp.ndarray[cnp.float32_t, ndim= 2 , mode= " c" ] result
188+ cdef cpp.PointXYZ * p
189+
190+ result = np.empty((n, 3 ), dtype = np.float32)
164191
165192 for i in range (n):
166- x = self .thisptr.at(i).x
167- y = self .thisptr.at(i).y
168- z = self .thisptr.at(i).z
169- result[i,0 ] = x
170- result[i,1 ] = y
171- result[i,2 ] = z
193+ p = & self .thisptr.at(i)
194+ result[i, 0 ] = p.x
195+ result[i, 1 ] = p.y
196+ result[i, 2 ] = p.z
172197 return result
173198
174199 def from_list (self , _list ):
175200 """
176201 Fill this pointcloud from a list of 3-tuples
177202 """
178- assert len (_list)
179- assert len (_list[0 ]) == 3
180-
181203 cdef Py_ssize_t npts = len (_list)
204+ cdef cpp.PointXYZ * p
205+
182206 self .resize(npts)
183207 self .thisptr.width = npts
184208 self .thisptr.height = 1
185209 for i,l in enumerate (_list):
186- self .thisptr.at(i).x = l[0 ]
187- self .thisptr.at(i).y = l[1 ]
188- self .thisptr.at(i).z = l[2 ]
210+ p = & self .thisptr.at(i)
211+ p.x, p.y, p.z = l
189212
190213 def to_list (self ):
191214 """
@@ -200,46 +223,54 @@ cdef class PointCloud:
200223 """
201224 Return a point (3-tuple) at the given row/column
202225 """
203- # grr.... the following doesnt compile to valid
204- # cython.. so just take the perf hit
205- # cdef PointXYZ &p = self.thisptr.at(x,y)
206- cdef x = self .thisptr.at(row,col).x
207- cdef y = self .thisptr.at(row,col).y
208- cdef z = self .thisptr.at(row,col).z
209- return x,y,z
226+ cdef cpp.PointXYZ * p = & self .thisptr.at(row, col)
227+ return p.x, p.y, p.z
210228
211229 def __getitem__ (self , cnp.npy_intp idx ):
212- cdef x = self .thisptr.at(idx).x
213- cdef y = self .thisptr.at(idx).y
214- cdef z = self .thisptr.at(idx).z
215- return x,y,z
230+ cdef cpp.PointXYZ * p = & self .thisptr.at(idx)
231+ return p.x, p.y, p.z
216232
217233 def from_file (self , char *f ):
218234 """
219235 Fill this pointcloud from a file (a local path).
220236 Only pcd files supported currently.
237+
238+ Deprecated; use pcl.load instead.
221239 """
240+ return self ._from_pcd_file(f)
241+
242+ def _from_pcd_file (self , const char *s ):
243+ cdef int error = 0
244+ with nogil:
245+ ok = cpp.loadPCDFile(string(s), deref(self .thisptr))
246+ return error
247+
248+ def _from_ply_file (self , const char *s ):
222249 cdef int ok = 0
223- cdef string s = string(f)
224- if f.endswith(" .pcd" ):
225- ok = cpp.loadPCDFile(s, deref(self .thisptr))
226- else :
227- raise ValueError (" Incorrect file extension (must be .pcd)" )
228- return ok
250+ with nogil:
251+ error = cpp.loadPLYFile(string(s), deref(self .thisptr))
252+ return error
229253
230- def to_file (self , char *f , bool ascii = True ):
231- """
232- Save this pointcloud to a local file.
233- Only saving to binary or ascii pcd is supported
254+ def to_file (self , const char *fname , bool ascii = True ):
255+ """ Save pointcloud to a file in PCD format.
256+
257+ Deprecated: use pcl.save instead.
234258 """
235- cdef bool binary = not ascii
236- cdef int ok = 0
259+ return self ._to_pcd_file(fname, not ascii)
260+
261+ def _to_pcd_file (self , const char *f , bool binary = False ):
262+ cdef int error = 0
237263 cdef string s = string(f)
238- if f.endswith(" .pcd" ):
239- ok = cpp.savePCDFile(s, deref(self .thisptr), binary)
240- else :
241- raise ValueError (" Incorrect file extension (must be .pcd)" )
242- return ok
264+ with nogil:
265+ error = cpp.savePCDFile(s, deref(self .thisptr), binary)
266+ return error
267+
268+ def _to_ply_file (self , const char *f , bool binary = False ):
269+ cdef int error = 0
270+ cdef string s = string(f)
271+ with nogil:
272+ error = cpp.savePLYFile(s, deref(self .thisptr), binary)
273+ return error
243274
244275 def make_segmenter (self ):
245276 """
@@ -608,13 +639,13 @@ cdef class OctreePointCloudSearch(OctreePointCloud):
608639
609640 def __dealloc__ (self ):
610641 del self .me
611-
612- """
613- Search for all neighbors of query point that are within a given radius.
614-
615- Returns: (k_indices, k_sqr_distances)
616- """
642+
617643 def radius_search (self , point , double radius , unsigned int max_nn = 0 ):
644+ """
645+ Search for all neighbors of query point that are within a given radius.
646+
647+ Returns: (k_indices, k_sqr_distances)
648+ """
618649 cdef vector[int ] k_indices
619650 cdef vector[float ] k_sqr_distances
620651 if max_nn > 0 :
0 commit comments