3D Range Data - Repository of 3D range data sets

On this webpage we share and make publicly available both real and simulated 3D range data sets from the Institute of Automatic Control Engineering. To make storage and processing efficient, our data sets are provided in a binary format. Files have the extension *.pc (point cloud) and may contain an arbitrary number of frames, each of whichs contains an ASCII-identifier, a time stamp, a view point and the 3D point coordinates for one scanline.

To access the data, you have two options:

  • If you simply want to view the data sets, you can download the (Windows) binary of our 3D Viewer. Due to the dependencies and required clean-up work we currently don't offer a source package for Linux, sorry :(.
  • If you want to use the data you will most likely want to convert it to a different format suitable for your algorithms. To this end we provide the following Matlab script to read the files. Save it in the same folder as the downloaded data sets, then type "help loadpcfile" in Matlab to learn how to use it.
    Note:It is not recommended to visualize point clouds in Matlab, as the 3D view will slow to a crawl when drawing more than a couple thousand line/point handles.
When working with the data sets, you should be aware of two things: Firstly, the real data sets may have an incomplete last frame, i.e. the total number of points may be slightly less than the number of scanlines times the number of points per scanline. Secondly, even for scans which were recorded from one view point, the points will usually be loaded with very many distinct view points. This is because the sensor pose estimate (in our data sets this is extracted from the joint encoders and calculation of the forward kinematics) will differ slightly in the last digits from frame to frame. You can remedy this by manually assigning one viewpoint (for example the column mean of V) to all points in Matlab.

 

Name # Points # Scanlines Size Preview
Seat & Armchair
Recorded with a Hokuyo UTM-30LX using only the front 180°
Frequency: 40Hz
Field of view: 180°
Angular resolution: 0.25°
720 points/scanline
seat_armchair_horizontal 297,360 413 6,991kB
seat_armchair_vertical 297,360 413 6,991kB
seat_armchair_rotational 110,880 154 2,608kB
Seat, Armchair, Table
Recorded with a Hokuyo UTM-30LX using only the front 180°
Frequency: 40Hz
Field of view: 180°
Angular resolution: 0.25°
720 points/scanline
seat_armchair_table_horizontal 296,640 412 6,975kB
seat_armchair_table_vertical 297,360 413 6,991kB
seat_armchair_rotational 158,400 220 3,712kB
Two Chairs
Recorded with a Hokuyo UTM-30LX using only the front 180°
Frequency: 40Hz
Field of view: 180°
Angular resolution: 0.25°
720 points/scanline
two_chairs_horizontal 198,720 276 4,671kB
two_chairs_vertical 295,920 411 6,959kB
two_chairs_rotational 177,840 247 4,176kB

The software framework that we currently use for simulation and segmentation of 3D range data has many dependencies and is not in any way cleaned up to allow for a public release. However, to facilitate a comparison of performance, we here provide the code which is at the core of our segmentation algorithm. Although it has undergone (and is still undergoing minor) modifications, the core clustering loop is that used and mentioned in several publications. In an implementation with linked lists, we have found this routine to perform significantly faster than region growing with seed points.

Note that the below snippets only provide relevant excerpts of the full classes and routines. In our code, the data structures make use of Qt containers, however, they can just as well be implemented with equivalent STL containers.

Using a point data structure like


  1. struct SmartPoint {
  2. //! The point
  3. Point3F p;
  4. //! The normal
  5. Point3F n;
  6. //! The average Euclidean distance to the neighbors
  7. double ad;
  8. //! User pointer
  9. void *up;
  10. };

and a linked list point cloud class such as


  1. class SmartPointCloud : public QLinkedList<SmartPoint*> {
  2.  
  3. //! Computes the normal vector of each point from its \a k nearest neighbors.
  4. void computeNormalVectors(int k=20);
  5.  
  6. //! Segments the points by grouping all points with at least \a l neighbors within distance \a r and normal vector angle difference of \a a degrees. Removes clusters with less than \a nMin points.
  7. QList<SmartPointCloud*> segmentByNormalVectors(double aMax, double rMax, int nMin);
  8.  
  9. //! Appends smart point cloud \a other to this one.
  10. SmartPointCloud& operator<<(const SmartPointCloud &other);
  11.  
  12. }

the clustering routine can be summarized as follows:


  1. QList<SmartPointCloud*> SmartPointCloud::segmentByNormalVectors(double aMax, double rMax, int nMin) {
  2. if (aMax<=0)
  3. return QList<SmartPointCloud*>();
  4.  
  5. qDebug("[SmartPointCloud] Segmenting smart point cloud by normal vectors...");
  6. time.start();
  7.  
  8. //Create a kd-tree if necessary, allocate space, prepare parameters
  9. if (!kdTree)
  10. kdTree = createKdTreeFromPoints();
  11. ANNpointArray points = kdTree->thePoints();
  12. const int k = MIN(50,count()-1);
  13. ANNidxArray indices = new ANNidx[k];
  14. ANNdistArray distances = new ANNdist[k];
  15. double aThresh = cos(aMax/180.0*M_PI);
  16.  
  17. //Make sure normal vectors have been estimated
  18. if (!normalVectorsComputed)
  19. computeNormalVectors(k);
  20.  
  21. //Create a temporary array of indices and clean the user pointers
  22. SmartPoint **pArray = new SmartPoint*[count()];
  23. int i=0;
  24. for (iterator p=begin(); p!=end(); p++) {
  25. pArray[i] = (*p);
  26. pArray[i]->up = NULL;
  27. i++;
  28. }
  29.  
  30. int id=0;
  31. for (iterator p=begin(); p!=end(); p++) {
  32. //Current point
  33. SmartPoint *cp = (*p);
  34.  
  35. //Get the nearest neighbors
  36. double r = MIN(rMax,cp->ad);
  37. kdTree->annkFRSearch(points[id],r*r,k,indices,distances);
  38.  
  39. //Now cluster
  40. for (int j=0; j<k; j++) {
  41. if (indices[j] == ANN_NULL_IDX)
  42. break;
  43. if (fabs(cp->n.dot(pArray[indices[j]]->n)) < aThresh)
  44. continue;
  45. //Neighbor point
  46. SmartPoint *np = pArray[indices[j]];
  47. if (cp->up) {
  48. if (np->up) {
  49. //If points belong to the same cluster continue
  50. if (cp->up == np->up)
  51. continue;
  52. //Else merge the clusters (append the smaller to the larger)
  53. bool g = ((SmartPointCloud*)cp->up)->count() > ((SmartPointCloud*)np->up)->count();
  54. SmartPointCloud *pc1 = g ? (SmartPointCloud*)cp->up : (SmartPointCloud*)np->up;
  55. SmartPointCloud *pc2 = g ? (SmartPointCloud*)np->up : (SmartPointCloud*)cp->up;
  56. for (iterator p=pc2->begin(); p!=pc2->end(); p++)
  57. (*p)->up = pc1;
  58. (*pc1) << (*pc2);
  59. delete pc2;
  60. } else {
  61. //Neighbor point doesn't have a cluster, add it to this one
  62. np->up = cp->up;
  63. ((SmartPointCloud*)cp->up)->append(np);
  64. }
  65. } else if (np->up) {
  66. //Add this point to neighbor cluster
  67. cp->up = np->up;
  68. ((SmartPointCloud*)np->up)->append(cp);
  69. } else {
  70. //Both points don't have a cluster -> Create a new one
  71. SmartPointCloud *nc = new SmartPointCloud;
  72. cp->up = (void*)nc;
  73. nc->append(cp);
  74. np->up = (void*)nc;
  75. nc->append(np);
  76. }
  77. }
  78. id++;
  79. }
  80.  
  81. //Gather the clusters and delete the ones with less than nMin points
  82. QList<SmartPointCloud*> clusters;
  83. for (iterator p=begin(); p!=end(); p++) {
  84. SmartPointCloud *cluster = (SmartPointCloud*)(*p)->up;
  85. if (!cluster)
  86. continue;
  87. if (clusters.contains(cluster)) {
  88. p = --erase(p);
  89. continue;
  90. }
  91. if (cluster->count() < nMin) {
  92. for (iterator cp=cluster->begin(); cp!=cluster->end(); cp++)
  93. (*cp)->up = NULL;
  94. delete cluster;
  95. } else {
  96. p = --erase(p);
  97. clusters.append(cluster);
  98. }
  99. }
  100.  
  101. //The point array and kd-tree are no longer valid for this point cloud
  102. annDeallocPts(points);
  103. delete kdTree;
  104. kdTree = NULL;
  105.  
  106. //Clean up and return
  107. delete indices;
  108. delete distances;
  109. delete inRange;
  110. delete pArray;
  111.  
  112. qDebug("[SmartPointCloud] Done (%d ms). %d cluster(s).",time.elapsed(),clusters.count());
  113. return clusters;
  114. }