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