1 /*
2 ===============================================================================
3
4 FILE: laszip.cpp
5
6 CONTENTS:
7 Comparison with LASzip
8
9 PROGRAMMERS:
10
11 uday.karan@gmail.com - Hobu, Inc.
12
13 COPYRIGHT:
14
15 (c) 2014, Uday Verma, Hobu, Inc.
16
17 This is free software; you can redistribute and/or modify it under the
18 terms of the GNU Lesser General Licence as published by the Free Software
19 Foundation. See the COPYING file for more information.
20
21 This software is distributed WITHOUT ANY WARRANTY and without even the
22 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23
24 CHANGE HISTORY:
25
26 ===============================================================================
27 */
28
29
30 #include <iostream>
31 #include <stdint.h>
32 #include <string>
33 #include <string.h>
34 #include <sstream>
35 #include <stdio.h>
36 #include <vector>
37
38
39 #include <laszip/laszip.hpp>
40 #include <laszip/lasunzipper.hpp>
41 #include <laszip/laszipper.hpp>
42
43 #include "common/common.hpp"
44
45 #include "io.hpp"
46
47 // Each library will decode autzen.las without doing any IO to write out points
48 // The timing will not include time required to initialize encoding etc.
49 float bench_laszip();
50 float bench_lazperf();
51
main()52 int main() {
53 std::vector<float> laztimes, lazperftimes;
54 // run one kind of test at a time, to give them maximum locality of reference benefit
55
56 std::cout << "Benchmarking laszip: " << std::endl;
57 for (int i = 0 ; i < 5 ; i ++) {
58 std::cout << (i+1) << ": ";
59
60 if (i >= 2) {
61 std::cout << " Capturing..." << std::endl;
62 laztimes.push_back(bench_laszip());
63 }
64 else {
65 std::cout << " Warming up..." << std::endl;
66 bench_laszip();
67 }
68 }
69
70 std::cout << "Benchmarking lazperf: " << std::endl;
71 for (int i = 0 ; i < 5 ; i ++) {
72 std::cout << (i+1) << ": ";
73
74 if (i >= 2) {
75 std::cout << " Capturing..." << std::endl;
76 lazperftimes.push_back(bench_lazperf());
77 }
78 else {
79 std::cout << " Warming up..." << std::endl;
80 bench_lazperf();
81 }
82 }
83
84
85 printf("\n\n");
86 printf("%10s%10s%10s\n", "Index", "LASzip", "lazperf");
87 for (size_t i = 0 ; i < 3 ; i ++) {
88 printf("%10zu%10.6f%10.6f\n", i, laztimes[i], lazperftimes[i]);
89 }
90
91 return 0;
92 }
93
94 template<class T>
read_array_field(uint8_t * & src,T * dest,std::size_t count)95 static inline void read_array_field(uint8_t*& src, T* dest, std::size_t count) {
96 memcpy((uint8_t*)dest, (uint8_t*)(T*)src, sizeof(T)*count);
97 src += sizeof(T) * count;
98 return;
99 }
100
101 template <typename T>
read_n(T & dest,FILE * src,size_t const & num)102 static inline size_t read_n(T& dest, FILE* src, size_t const& num) {
103 return fread(dest, 1, num, src);
104 }
105
106 template<class T>
read_field(uint8_t * & src)107 static inline T read_field(uint8_t*& src) {
108 T tmp = *(T*)(void*)src;
109 src += sizeof(T);
110 return tmp;
111 }
112
113
114 class VLR {
115 public:
116 uint16_t reserved;
117 std::string userId;
118 uint16_t recordId;
119 uint16_t size;
120 std::string description;
121 uint8_t* data;
122 enum
123 {
124 eHeaderSize = 54,
125 eUserIdSize = 16,
126 eDescriptionSize = 32
127 };
128
129
VLR()130 VLR()
131 : reserved(0)
132 , userId("")
133 , recordId(0)
134 , size(0)
135 , description("")
136 , data(0) {}
~VLR()137 ~VLR()
138 {
139 delete[] data;
140 }
read(FILE * fp)141 inline void read(FILE* fp) {
142 // assumes the stream is already positioned to the beginning
143
144 {
145 uint8_t* buf1 = new uint8_t[eHeaderSize];
146 size_t numRead = read_n(buf1, fp, eHeaderSize);
147 if (numRead != eHeaderSize)
148 {
149 std::ostringstream oss;
150 oss << " read size was invalid, not " << eHeaderSize << std::endl;
151 }
152 uint8_t* p1 = buf1;
153
154 reserved = read_field<uint16_t>(p1);
155
156 uint8_t userId_data[eUserIdSize];
157 read_array_field(p1, userId_data, eUserIdSize);
158 userId = std::string((const char*)&userId_data);
159
160 recordId = read_field<uint16_t>(p1);
161 size = read_field<uint16_t>(p1);
162
163 uint8_t description_data[eDescriptionSize];
164 read_array_field(p1, description_data, eDescriptionSize);
165 description = std::string( (const char*)&description_data);
166
167 delete[] buf1;
168 }
169
170 data = new uint8_t[size];
171 {
172 read_n(data, fp, size);
173 }
174
175 return;
176 }
177 };
178
179 // LASzip stuff
readVLRs(FILE * fp,int count)180 std::vector<VLR*> readVLRs(FILE* fp, int count)
181 {
182 std::vector<VLR*> output;
183
184 for (int i = 0; i < count; ++i)
185 {
186 // std::cout << "Reading vlr #:" << i << std::endl;
187 VLR* vlr = new VLR;
188 vlr->read(fp);
189 output.push_back(vlr);
190 }
191 return output;
192 }
193
getLASzipVLR(std::vector<VLR * > const & vlrs)194 VLR* getLASzipVLR(std::vector<VLR*> const& vlrs)
195 {
196 std::string userId("laszip encoded");
197 uint16_t recordId(22204);
198
199 for(size_t i = 0; i < vlrs.size(); ++i)
200 {
201 VLR* vlr = vlrs[i];
202 std::string const& uid = vlr->userId;
203 uint16_t rid = vlr->recordId;
204 //
205 // std::cout << "VLR recordId: " << rid << std::endl;
206 // std::cout << "VLR userid: '" << uid <<"'"<< std::endl;
207 // std::cout << "uid size" << uid.size() << std::endl;
208 //
209 // std::cout << "uid equal: " << boost::iequals(uid, userId) << std::endl;
210 // std::cout << "rid equal: " << (rid == recordId) << std::endl;
211
212 if (uid == userId && rid == recordId)
213 return vlr;
214 }
215 return 0;
216 }
217
218 class LASHeader
219 {
220 public:
221 uint32_t point_count;
222 uint32_t vlr_count;
223 uint16_t header_size;
224 uint32_t data_offset;
225 uint8_t point_format_id;
226 uint16_t point_record_length;
227 double scale[3];
228 double offset[3];
229 double maxs[3];
230 double mins[3];
231
LASHeader()232 LASHeader()
233 : point_count (0)
234 , vlr_count (0)
235 , header_size(0)
236 , data_offset(0)
237 , point_format_id(0)
238 , point_record_length(0)
239 {
240 for (int i = 0; i < 3; ++i)
241 {
242 scale[i] = 0.0;
243 offset[i] = 0.0;
244 mins[i] = 0.0;
245 maxs[i] = 0.0;
246 }
247 }
248 };
249
250 /// The Instance class. One of these exists for each instance of your NaCl
251 /// module on the web page. The browser will ask the Module object to create
252 /// a new Instance for each occurrence of the <embed> tag that has these
253 /// attributes:
254 /// src="hello_tutorial.nmf"
255 /// type="application/x-pnacl"
256 /// To communicate with the browser, you must override HandleMessage() to
257 /// receive messages from the browser, and use PostMessage() to send messages
258 /// back to the browser. Note that this interface is asynchronous.
259 class LASzipInstance {
260 public:
261 /// The constructor creates the plugin-side instance.
262 /// @param[in] instance the handle to the browser-side plugin instance.
LASzipInstance()263 explicit LASzipInstance() :
264 fp_(0)
265 , bDidReadHeader_(false)
266 , pointIndex_(0)
267 , point_(0)
268 , bytes_(0)
269 {
270 }
271
~LASzipInstance()272 virtual ~LASzipInstance()
273 {
274 }
275
open(const std::string & filename)276 bool open(const std::string& filename)
277 {
278 fp_ = fopen(filename.c_str(), "r");
279 if (!fp_)
280 {
281 return false;
282 }
283
284 char magic[4] = {'\0'};
285 int numRead = fread(&magic, 1, 4, fp_);
286 if (numRead != 4)
287 {
288 return false;
289 }
290 if (magic[0] != 'L' &&
291 magic[1] != 'A' &&
292 magic[2] != 'S' &&
293 magic[3] != 'F'
294 )
295 {
296 return false;
297 }
298
299 fseek(fp_, 0, SEEK_SET);
300
301 readHeader(header_);
302
303 return true;
304 }
305
readHeader(LASHeader & header)306 bool readHeader(LASHeader& header)
307 {
308 fseek(fp_, 32*3 + 11, SEEK_SET);
309 size_t result = fread(&header.point_count, 4, 1, fp_);
310 if (result != 1)
311 {
312 return false;
313 }
314
315 fseek(fp_, 32*3, SEEK_SET);
316 result = fread(&header.data_offset, 4, 1, fp_);
317 if (result != 1)
318 {
319 return false;
320 }
321
322 fseek(fp_, 32*3+4, SEEK_SET);
323 result = fread(&header.vlr_count, 4, 1, fp_);
324 if (result != 1)
325 {
326 return false;
327 }
328
329
330 fseek(fp_, 32*3-2, SEEK_SET);
331 result = fread(&header.header_size, 2, 1, fp_);
332 if (result != 1)
333 {
334 return false;
335 }
336
337 fseek(fp_, 32*3 + 8, SEEK_SET);
338 result = fread(&header.point_format_id, 1, 1, fp_);
339 if (result != 1)
340 {
341 return false;
342 }
343
344 uint8_t compression_bit_7 = (header.point_format_id & 0x80) >> 7;
345 uint8_t compression_bit_6 = (header.point_format_id & 0x40) >> 6;
346
347 if (!compression_bit_7 && !compression_bit_6 )
348 {
349 return false;
350 }
351 if (compression_bit_7 && compression_bit_6)
352 {
353 return false;
354 }
355
356 header.point_format_id = header.point_format_id & 0x3f;
357
358 fseek(fp_, 32*3 + 8+1, SEEK_SET);
359 result = fread(&header.point_record_length, 2, 1, fp_);
360 if (result != 1)
361 {
362 return false;
363 }
364
365
366 size_t start = 32*3 + 35;
367 fseek(fp_, start, SEEK_SET);
368
369 result = fread(&header.scale, 8, 3, fp_ );
370 if (result != 3)
371 {
372 return false;
373 }
374
375
376 result = fread(&header.offset, 8, 3, fp_ );
377 if (result != 3)
378 {
379 return false;
380 }
381
382 result = fread(&header.maxs[0], 8, 1, fp_ );
383 if (result != 1)
384 {
385 return false;
386 }
387
388 result = fread(&header.mins[0], 8, 1, fp_ );
389 if (result != 1)
390 {
391 return false;
392 }
393 result = fread(&header.maxs[1], 8, 1, fp_ );
394 if (result != 1)
395 {
396 return false;
397 }
398 result = fread(&header.mins[1], 8, 1, fp_ );
399 if (result != 1)
400 {
401 return false;
402 }
403
404 result = fread(&header.maxs[2], 8, 1, fp_ );
405 if (result != 1)
406 {
407 return false;
408 }
409 result = fread(&header.mins[2], 8, 1, fp_ );
410 if (result != 1)
411 {
412 return false;
413 }
414
415
416 fseek(fp_, header_.header_size, SEEK_SET);
417 std::vector<VLR*> vlrs = readVLRs(fp_, header_.vlr_count);
418 VLR* zvlr = getLASzipVLR(vlrs);
419
420 if (!zvlr)
421 {
422 return false;
423 }
424 bool stat = zip_.unpack(&(zvlr->data[0]), zvlr->size);
425 if (!stat)
426 {
427 return false;
428 }
429
430 fseek(fp_, header_.data_offset, SEEK_SET);
431 stat = unzipper_.open(fp_, &zip_);
432 if (!stat)
433 {
434 return false;
435 }
436
437 for (size_t i = 0; i < vlrs.size(); ++i)
438 {
439 delete vlrs[i];
440 }
441 unsigned int point_offset(0);
442 point_ = new unsigned char*[zip_.num_items];
443 uint32_t point_size(0);
444 for (unsigned i = 0; i < zip_.num_items; i++)
445 {
446 point_size += zip_.items[i].size;
447 }
448
449 bytes_ = new uint8_t[ point_size ];
450
451 for (unsigned i = 0; i < zip_.num_items; i++)
452 {
453 point_[i] = &(bytes_[point_offset]);
454 point_offset += zip_.items[i].size;
455 }
456
457 return true;
458 }
459
close()460 void close() {
461 fclose(fp_);
462 header_ = LASHeader();
463
464 zip_ = LASzip();
465 unzipper_ = LASunzipper();
466 fp_ = NULL;
467 bDidReadHeader_ = 0;
468 pointIndex_ = 0;
469
470
471 delete[] point_; point_ = 0;
472
473 delete[] bytes_; bytes_ = 0;
474 }
475
476
readPoint()477 void readPoint()
478 {
479 unzipper_.read(point_);
480 pointIndex_++;
481 }
482
483 bool bCreatedFS_;
484 LASzip zip_;
485 LASunzipper unzipper_;
486 FILE* fp_;
487 LASHeader header_;
488 bool bDidReadHeader_;
489 uint32_t pointIndex_;
490 unsigned char** point_;
491 unsigned char* bytes_;
492 };
493
494
bench_laszip()495 float bench_laszip() {
496 float t;
497
498 LASzipInstance l;
499 l.open("test/raw-sets/autzen.laz");
500
501 unsigned int limit = l.header_.point_count;
502
503 auto start = common::tick();
504 while(l.pointIndex_ != limit)
505 l.readPoint();
506
507 t = common::since(start);
508 l.close();
509
510 return t;
511 }
512
513
bench_lazperf()514 float bench_lazperf() {
515 std::ifstream file("test/raw-sets/autzen.laz", std::ios::binary);
516 laszip::io::reader::file f(file);
517 float t;
518
519 size_t limit = f.get_header().point_count;
520 char buf[256]; // a buffer large enough to hold our point
521
522
523 auto start = common::tick();
524 for (size_t i = 0 ; i < limit ; i ++)
525 f.readPoint(buf);
526
527 t = common::since(start);
528
529 file.close();
530 return t;
531 }
532