blob: 8e32842fe55dfc9198c377083c977b7cc73c5aea [file] [log] [blame]
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -04001#include <pcl/io/openni_grabber.h>
2#include <pcl/visualization/cloud_viewer.h>
3#include <pcl/octree/octree.h>
4#include <iostream>
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -04005
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -04006#include <pcl/filters/voxel_grid.h> // VoxelGrid
7#include <pcl/segmentation/extract_clusters.h> // EuclideanClusterExtraction
8#include <pcl/sample_consensus/ransac.h> // RandomSampleConsensus
9#include <pcl/sample_consensus/sac_model_plane.h> // SampleConsensusModelPlane
Dmitriy Kozmenko02a9bd32013-05-22 10:01:16 -040010
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -040011#include "config.h"
12#include "tools.h"
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -040013
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -040014class box {
15private:
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -040016
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -040017 pcl::PointCloud<pcl::PointXYZRGB>::Ptr *workCloudRGB_ptr;
18
19 int p;
20 pcl::PointXYZ m[3];
21 pcl::PointCloud<pcl::PointXYZ>::Ptr *a_ptr[3];
22 pcl::PointXYZ c;
23 float side[3][2];
24
25public:
26
27 /**
28 * constructor
29 */
30 box() {
31 workCloudRGB_ptr = new pcl::PointCloud<pcl::PointXYZRGB>::Ptr(new pcl::PointCloud<pcl::PointXYZRGB>);
32 a_ptr[0] = new pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
33 a_ptr[1] = new pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
34 a_ptr[2] = new pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
35 p = 0;
36 for (int i = 0; i < 3; i++) {
37 for (int g = 0; g < 2; g++) {
38 side[i][g] = 0.0f;
39 }
40 }
41 }
42
43 /**
44 * Add plane to box class
45 * @param PointCloud _cloud - plane
46 */
47 void addPlane(pcl::PointCloud<pcl::PointXYZ>::Ptr &_cloud) {
48
49 if (p > 2) {
50 return;
51 }
52
53 // create mesh
54 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
55
56 pcl::VoxelGrid<pcl::PointXYZ> vg;
57 vg.setInputCloud(_cloud);
58 vg.setLeafSize(LEAFSIZE, LEAFSIZE, LEAFSIZE);
59 vg.filter(*cloud);
Dmitriy Kozmenko02a9bd32013-05-22 10:01:16 -040060
61
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -040062 pcl::PointCloud<pcl::PointXYZ>::Ptr a = *a_ptr[p];
63 pcl::PointCloud<pcl::PointXYZRGB>::Ptr workCloudRGB = *workCloudRGB_ptr;
64 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudRGB(new pcl::PointCloud<pcl::PointXYZRGB>);
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -040065
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -040066 double x = 0, y = 0, z = 0;
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -040067
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -040068 // add and
69 cloudRGB->points.resize(cloud->size());
70
71 for (size_t i = 0; i < cloud->size(); i++) {
72 x += cloudRGB->points[i].x = cloud->points[i].x;
73 y += cloudRGB->points[i].y = cloud->points[i].y;
74 z += cloudRGB->points[i].z = cloud->points[i].z;
75 switch (p) {
76 case 0:
77 cloudRGB->points[i].rgb = RGBCOLOR_RED;
78 break;
79 case 1:
80 cloudRGB->points[i].rgb = RGBCOLOR_GREEN;
81 break;
82 case 2:
83 cloudRGB->points[i].rgb = RGBCOLOR_BLUE;
84 break;
85 }
86 }
87
88 // get middle point
89 m[p].x = x / cloud->size();
90 m[p].y = y / cloud->size();
91 m[p].z = z / cloud->size();
92
93 getCorner(cloudRGB, a);
94
95 *workCloudRGB += *cloudRGB;
96
97 p++;
98 }
99
100 /**
101 * Get corner and return array of corner
102 * @param PointCloud cloud - plane
103 * @param PointCloud a - corner
104 */
105 void getCorner(pcl::PointCloud<pcl::PointXYZRGB>::Ptr &cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr &a) {
106
107 a->points.resize(4);
108
109 for (size_t e = 0; e < 4; e++) {
110 float d = 0.0f;
111
112 for (size_t i = 1; i < cloud->size(); i++) {
113 float f = 0.0f;
114 if (e == 0) {
115 if (cloud->points[i].x != 0 && cloud->points[i].y != 0 && cloud->points[i].z != 0) {
116 f = sqrt(pow((m[e].x - cloud->points[i].x), 2) + pow((m[e].y - cloud->points[i].y), 2) + pow((m[e].z - cloud->points[i].z), 2));
117 }
118 } else {
119 for (size_t g = 0; g < e; g++) {
120 if (cloud->points[i].x != 0 && cloud->points[i].y != 0 && cloud->points[i].z != 0) {
121 f = f + sqrt(pow((a->points[g].x - cloud->points[i].x), 2) + pow((a->points[g].y - cloud->points[i].y), 2) + pow((a->points[g].z - cloud->points[i].z), 2));
122 }
123 }
124 }
125 if (f > d) {
126 d = f;
127 a->points[e].x = cloud->points[i].x;
128 a->points[e].y = cloud->points[i].y;
129 a->points[e].z = cloud->points[i].z;
130 }
131 }
132
133 }
134 }
135
136 /**
137 * get measurement of plane
138 * @param PointCloud a - plane
139 * @param float f1 - width or height
140 * @param float f2 - width or height
141 */
142 void getSide(pcl::PointCloud<pcl::PointXYZ>::Ptr &a, float &f1, float &f2) {
143
144 float s[3];
145
146 for (int t = 0; t < 3; t++) {
147 float dd = 0.0f;
148
149 pcl::PointXYZ p1 = a->points[0];
150
151 if (p1.x != 0 && p1.y != 0 && p1.z != 0) {
152
153 for (size_t j = 1; j < 4; j++) {
154 pcl::PointXYZ p2 = a->points[j];
155
156 if (p2.x != 0 && p2.y != 0 && p2.z != 0) {
157
158 float d = sqrt(pow((p1.x - p2.x), 2) + pow((p1.y - p2.y), 2) + pow((p1.z - p2.z), 2)) * METERSTOINCH;
159
160 for (int tt = 0; tt < t; tt++) {
161 if (s[tt] == d) {
162 d = -1;
163 }
164 }
165
166 if (d > dd) {
167 s[t] = d;
168 dd = d;
169 }
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400170
171
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400172 }
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400173
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400174 }
175 }
176 }
177
178 f1 = s[1];
179 f2 = s[2];
180 }
181
182 /**
183 * Get could of box include technical lines and point
184 * @todo normalize box
185 * @param PointCloud cloud - output cloud
186 */
187 void getCloud(pcl::PointCloud<pcl::PointXYZRGB>::Ptr &cloud) {
188
189 if (p < 2) {
190 return;
191 }
192
193 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_line(new pcl::PointCloud<pcl::PointXYZRGB>);
194 pcl::PointCloud<pcl::PointXYZRGB>::Ptr workCloudRGB = *workCloudRGB_ptr;
195
196 cloud->points.resize(0);
197
198 for (size_t i = 0; i < workCloudRGB->size(); i++) {
199 cloud->points.push_back(workCloudRGB->points[i]);
200 }
201
202 // normalize here
203 for (size_t e = 0; e < 3; e++) {
204
205 pcl::PointCloud<pcl::PointXYZ>::Ptr a = *a_ptr[e];
206
207
208 if (m[e].x != 0 && m[e].y != 0 && m[e].z != 0) {
209
210 getSide(a, side[e][0], side[e][1]);
211
212 for (size_t g = 0; g < 4; g++) {
213 if (a->points[g].x != 0 && a->points[g].y != 0 && a->points[g].z != 0) {
214 drawLine(cloud_line, m[e], a->points[g]);
215 *cloud += *cloud_line;
216 }
217 }
218 }
219
220 }
221
222 }
223
224 /**
225 * Output box dimensions
226 */
227 void getBox() {
228
229 float box[3];
230
231 float s[3][2];
232 for (int t = 0; t < 3; t++) {
233 box[t] = 0.0f;
234 s[t][0] = round(side[t][0]);
235 s[t][1] = round(side[t][1]);
236 }
237
238 if (s[0][0] == 0 || s[0][1] == 0 || s[1][0] == 0 || s[1][1] == 0) {
239 return;
240 }
241
242 if (s[2][0] != 0 && s[2][1] != 0) {
243 // 3 side
244 for (int a = 0; a < 2; a++) {
245 float aa = s[0][a];
246 float ab = (a == 0 ? s[0][1] : s[0][0]);
247
248 for (int b = 0; b < 2; b++) {
249 float ba = s[1][b];
250 float bb = (b == 0 ? s[1][1] : s[1][0]);
251
252 if (aa == ba) {
253 for (int c = 0; c < 2; c++) {
254 float ca = s[2][c];
255 float cb = (c == 0 ? s[2][1] : s[2][0]);
256
257 if (bb == ca && cb == ab) {
258 box[0] = aa; // ba
259 box[1] = bb; // ca
260 box[2] = cb; // ab
261 }
262 }
263 }
264 }
265 }
266 } else {
267 // 2 side
268 for (int a = 0; a < 2; a++) {
269 float aa = s[0][a];
270 float ab = (a == 0 ? s[0][1] : s[0][0]);
271
272 for (int b = 0; b < 2; b++) {
273 float ba = s[1][b];
274 float bb = (b == 0 ? s[1][1] : s[1][0]);
275
276 if (aa == ba) {
277 box[0] = aa; // ba
278 box[1] = ab;
279 box[2] = bb;
280 }
281 }
282 }
283
284 }
285
286 std::cout << "BOX: " << box[0] << "x" << box[1] << "x" << box[2]
287 << " Side1: " << s[0][0] << "x" << s[0][1] << ", "
288 << " Side2: " << s[1][0] << "x" << s[1][1] << ", "
289 << " Side3: " << s[2][0] << "x" << s[2][1] << std::endl;
290
291 }
292
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400293};
294
295
296// 3D Box scan class
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -0400297
298class _3dBoxScan {
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400299private:
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400300
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400301 // background
302 int background_capture;
303 pcl::PointCloud<pcl::PointXYZ>::Ptr *background_cloud_ptr;
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400304
Dmitriy Kozmenko02a9bd32013-05-22 10:01:16 -0400305 /**
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400306 * Get plane from PointCloud and save into box class
307 * @param PointCloud in
308 * @param box b
Dmitriy Kozmenko02a9bd32013-05-22 10:01:16 -0400309 */
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400310 void subtracting(pcl::PointCloud<pcl::PointXYZ>::Ptr &in, box &b) {
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400311
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400312 pcl::PointCloud<pcl::PointXYZ>::Ptr workCloud(new pcl::PointCloud<pcl::PointXYZ>);
313 pcl::PointCloud<pcl::PointXYZ>::Ptr workCloudDiff(new pcl::PointCloud<pcl::PointXYZ>);
314 pcl::PointCloud<pcl::PointXYZ>::Ptr ransac_planeCloud(new pcl::PointCloud<pcl::PointXYZ>);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400315
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400316 int c = 0;
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400317
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400318 do {
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400319
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400320 // get plane
321 std::vector<int> ransac_inliers;
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400322 pcl::SampleConsensusModelPlane<pcl::PointXYZ>::Ptr model_p(new pcl::SampleConsensusModelPlane<pcl::PointXYZ> (in));
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400323
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400324 pcl::RandomSampleConsensus<pcl::PointXYZ> ransac(model_p);
325 ransac.setDistanceThreshold(0.0045);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400326 ransac.computeModel();
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400327 ransac.getInliers(ransac_inliers);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400328
329 pcl::copyPointCloud<pcl::PointXYZ>(*in, ransac_inliers, *ransac_planeCloud);
330
331 if (ransac_planeCloud->size() == 0) {
332 break;
333 }
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400334
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400335 // get segment
336 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
337 tree->setInputCloud(ransac_planeCloud);
338
339 std::vector<pcl::PointIndices> cluster_indices;
340 pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
341 ec.setClusterTolerance(CLUSTERTOLETANCE); // 1cm
342 ec.setMinClusterSize(MINClUSTERSIZE);
343 ec.setMaxClusterSize(MAXCLUSTERSIZE);
344 ec.setSearchMethod(tree);
345 ec.setInputCloud(ransac_planeCloud);
346 ec.extract(cluster_indices);
347
348 workCloud->resize(1);
349 for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin(); it != cluster_indices.end(); ++it) {
350 for (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); pit++)
351 workCloud->points.push_back(ransac_planeCloud->points[*pit]);
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400352 }
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400353
354 if (workCloud->size() < 1000) {
355 break;
356 }
357
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400358 b.addPlane(workCloud);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400359
Dmitriy Kozmenko0e117db2013-06-27 14:41:51 -0400360 diffCloud(workCloud, in, workCloudDiff);
Dmitriy Kozmenko0e117db2013-06-27 14:41:51 -0400361 copyCloud(workCloudDiff, in);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400362
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400363 c++;
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400364
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400365 } while (c < 3);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400366
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400367 return;
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400368 }
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400369
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400370
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400371public:
372
373 // Cloud viewer
374 pcl::visualization::CloudViewer viewer;
375
376 _3dBoxScan() : viewer("PCL OpenNI Viewer") {
377 srand(time(NULL));
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400378 background_capture = 0;
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400379 viewer.registerKeyboardCallback(&_3dBoxScan::keyboardEventOccurred, *this, 0);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400380 background_cloud_ptr = new pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400381 }
382
383 void keyboardEventOccurred(const pcl::visualization::KeyboardEvent &event, void *) {
384
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400385 if (event.getKeySym() == "b" && event.keyDown()) {
386 background_capture = BACKGROUNDCAPTURE;
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400387 }
388
389 }
390
391 void cloud_cb_(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr &raw_cloud) {
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400392
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400393 pcl::PointXYZRGB p;
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400394
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400395 pcl::PointCloud<pcl::PointXYZ>::Ptr background_cloud = *background_cloud_ptr;
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400396 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_near(new pcl::PointCloud<pcl::PointXYZ>);
397 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_visual(new pcl::PointCloud<pcl::PointXYZRGB>);
398
Dmitriy Kozmenko0e117db2013-06-27 14:41:51 -0400399 //copyCloud(raw_cloud, cloud_near);
400 copyCloud(raw_cloud, cloud_near, MAXDISTANCE);
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400401
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400402 if (background_capture > 0) {
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400403
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400404 // capture background
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400405
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400406 if (background_capture == BACKGROUNDCAPTURE) {
407 copyCloud(cloud_near, background_cloud);
408 } else {
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400409 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_diff(new pcl::PointCloud<pcl::PointXYZ>);
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400410 diffCloud(background_cloud, cloud_near, cloud_diff);
411 *background_cloud += *cloud_diff;
412 }
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400413
414 copyCloud(background_cloud, cloud_visual, RGBCOLOR_LIGHTGRAY);
415
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400416 background_capture--;
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400417
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400418 } else if (background_cloud->size() > 0) {
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400419
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400420 // working
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400421
422 box b;
423
424 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_diff(new pcl::PointCloud<pcl::PointXYZ>);
425
426 diffCloud(background_cloud, cloud_near, cloud_diff);
427 subtracting(cloud_diff, b);
428
429 b.getCloud(cloud_visual);
430
431 b.getBox();
432
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400433 } else {
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400434
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400435 // splash
436
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400437 copyCloud(cloud_near, cloud_visual, RGBCOLOR_GRAY);
438
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400439 }
440
441 if (!viewer.wasStopped()) {
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400442 viewer.showCloud(cloud_visual);
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400443 }
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400444
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400445 return;
446 }
447
Dmitriy Kozmenko55aaa212013-06-21 15:15:44 -0400448 // run
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400449
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -0400450 void run() {
451 pcl::Grabber* interface = new pcl::OpenNIGrabber();
452
453 boost::function<void (const pcl::PointCloud<pcl::PointXYZ>::ConstPtr&) > f =
454 boost::bind(&_3dBoxScan::cloud_cb_, this, _1);
455
456 interface->registerCallback(f);
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -0400457 interface->start();
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -0400458 while (!viewer.wasStopped()) {
459 boost::this_thread::sleep(boost::posix_time::seconds(1));
460 }
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -0400461 interface->stop();
462 }
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400463
Dmitriy Kozmenkof2296692013-05-22 09:40:50 -0400464};
465
Dmitriy Kozmenkoaccd0c82013-05-22 11:41:18 -0400466// main
Dmitriy Kozmenkodf449ef2013-07-01 14:57:05 -0400467
468int main() {
469 _3dBoxScan v;
470 v.run();
471 return 0;
472}