1 /*
2 ===============================================================================
3 
4   FILE:  field_point10.hpp
5 
6   CONTENTS:
7 
8 
9   PROGRAMMERS:
10 
11     martin.isenburg@rapidlasso.com  -  http://rapidlasso.com
12     uday.karan@gmail.com - Hobu, Inc.
13 
14   COPYRIGHT:
15 
16     (c) 2007-2014, martin isenburg, rapidlasso - tools to catch reality
17     (c) 2014, Uday Verma, Hobu, Inc.
18 
19     This is free software; you can redistribute and/or modify it under the
20     terms of the GNU Lesser General Licence as published by the Free Software
21     Foundation. See the COPYING file for more information.
22 
23     This software is distributed WITHOUT ANY WARRANTY and without even the
24     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
25 
26   CHANGE HISTORY:
27 
28 ===============================================================================
29 */
30 
31 #include "../las.hpp"
32 
33 namespace lazperf
34 {
35 namespace detail
36 {
37 
38 namespace
39 {
40     // for LAS files with the return (r) and the number (n) of
41     // returns field correctly populated the mapping should really
42     // be only the following.
43     //  { 15, 15, 15, 15, 15, 15, 15, 15 },
44     //  { 15,  0, 15, 15, 15, 15, 15, 15 },
45     //  { 15,  1,  2, 15, 15, 15, 15, 15 },
46     //  { 15,  3,  4,  5, 15, 15, 15, 15 },
47     //  { 15,  6,  7,  8,  9, 15, 15, 15 },
48     //  { 15, 10, 11, 12, 13, 14, 15, 15 },
49     //  { 15, 15, 15, 15, 15, 15, 15, 15 },
50     //  { 15, 15, 15, 15, 15, 15, 15, 15 }
51     // however, some files start the numbering of r and n with 0,
52     // only have return counts r, or only have number of return
53     // counts n, or mix up the position of r and n. we therefore
54     // "complete" the table to also map those "undesired" r & n
55     // combinations to different contexts
56     const unsigned char number_return_map[8][8] =
57     {
58         { 15, 14, 13, 12, 11, 10,  9,  8 },
59         { 14,  0,  1,  3,  6, 10, 10,  9 },
60         { 13,  1,  2,  4,  7, 11, 11, 10 },
61         { 12,  3,  4,  5,  8, 12, 12, 11 },
62         { 11,  6,  7,  8,  9, 13, 13, 12 },
63         { 10, 10, 11, 12, 13, 14, 14, 13 },
64         {  9, 10, 11, 12, 13, 14, 15, 14 },
65         {  8,  9, 10, 11, 12, 13, 14, 15 }
66     };
67 
68     // for LAS files with the return (r) and the number (n) of
69     // returns field correctly populated the mapping should really
70     // be only the following.
71     //  {  0,  7,  7,  7,  7,  7,  7,  7 },
72     //  {  7,  0,  7,  7,  7,  7,  7,  7 },
73     //  {  7,  1,  0,  7,  7,  7,  7,  7 },
74     //  {  7,  2,  1,  0,  7,  7,  7,  7 },
75     //  {  7,  3,  2,  1,  0,  7,  7,  7 },
76     //  {  7,  4,  3,  2,  1,  0,  7,  7 },
77     //  {  7,  5,  4,  3,  2,  1,  0,  7 },
78     //  {  7,  6,  5,  4,  3,  2,  1,  0 }
79     // however, some files start the numbering of r and n with 0,
80     // only have return counts r, or only have number of return
81     // counts n, or mix up the position of r and n. we therefore
82     // "complete" the table to also map those "undesired" r & n
83     // combinations to different contexts
84     const unsigned char number_return_level[8][8] =
85     {
86         {  0,  1,  2,  3,  4,  5,  6,  7 },
87         {  1,  0,  1,  2,  3,  4,  5,  6 },
88         {  2,  1,  0,  1,  2,  3,  4,  5 },
89         {  3,  2,  1,  0,  1,  2,  3,  4 },
90         {  4,  3,  2,  1,  0,  1,  2,  3 },
91         {  5,  4,  3,  2,  1,  0,  1,  2 },
92         {  6,  5,  4,  3,  2,  1,  0,  1 },
93         {  7,  6,  5,  4,  3,  2,  1,  0 }
94     };
95 
96 
changed_values(const las::point10 & this_val,const las::point10 & last,unsigned short last_intensity)97     int changed_values(const las::point10& this_val, const las::point10& last,
98         unsigned short last_intensity)
99     {
100         // This logic here constructs a 5-bit changed value which is basically a bit map of
101         // what has changed since the last point, not considering the x, y and z values
102         int bitfields_changed = (
103             (last.return_number ^ this_val.return_number) |
104             (last.number_of_returns_of_given_pulse ^ this_val.number_of_returns_of_given_pulse) |
105             (last.scan_direction_flag ^ this_val.scan_direction_flag) |
106             (last.edge_of_flight_line ^ this_val.edge_of_flight_line)) != 0;
107 
108         // last intensity is not checked with last point, but the passed in last intensity value
109         int intensity_changed = (last_intensity ^ this_val.intensity) != 0;
110         int classification_changed = (last.classification ^ this_val.classification) != 0;
111         int scan_angle_rank_changed = (last.scan_angle_rank ^ this_val.scan_angle_rank) != 0;
112         int user_data_changed = (last.user_data ^ this_val.user_data) != 0;
113         int point_source_changed = (last.point_source_ID ^ this_val.point_source_ID) != 0;
114 
115         return (bitfields_changed << 5) |
116                (intensity_changed << 4) |
117                (classification_changed << 3) |
118                (scan_angle_rank_changed << 2) |
119                (user_data_changed << 1) |
120                (point_source_changed);
121     }
122 } // unnamed namespace
123 
Point10Base()124 Point10Base::Point10Base() : m_changed_values(64), have_last_(false)
125 {
126     last_intensity.fill(0);
127 
128     m_scan_angle_rank[0] = new models::arithmetic(256);
129     m_scan_angle_rank[1] = new models::arithmetic(256);
130 
131     last_height.fill(0);
132 
133     for (int i = 0; i < 256; i ++) {
134         m_bit_byte[i] = new models::arithmetic(256);
135         m_classification[i] = new models::arithmetic(256);
136         m_user_data[i] = new models::arithmetic(256);
137     }
138 }
139 
~Point10Base()140 Point10Base::~Point10Base()
141 {
142     delete m_scan_angle_rank[0];
143     delete m_scan_angle_rank[1];
144 
145     for (int i = 0 ; i < 256 ; i ++)
146     {
147         delete m_bit_byte[i];
148         delete m_classification[i];
149         delete m_user_data[i];
150     }
151 }
152 
153 // COMPRESSOR
154 
Point10Compressor(encoders::arithmetic<OutCbStream> & enc)155 Point10Compressor::Point10Compressor(encoders::arithmetic<OutCbStream>& enc) : enc_(enc),
156     ic_intensity(16, 4), ic_point_source_ID(16), ic_dx(32, 2), ic_dy(32, 22), ic_z(32, 20),
157     compressors_inited_(false)
158 {}
159 
init()160 void Point10Compressor::init()
161 {
162     ic_intensity.init();
163     ic_point_source_ID.init();
164     ic_dx.init();
165     ic_dy.init();
166     ic_z.init();
167 }
168 
compress(const char * buf)169 const char *Point10Compressor::compress(const char *buf)
170 {
171     las::point10 this_val(buf);
172 
173     if (!compressors_inited_)
174     {
175         init();
176         compressors_inited_ = true;
177     }
178 
179     // don't have the first data yet, just push it to our have last stuff and move on
180     if (!have_last_)
181     {
182         have_last_ = true;
183         last_ = this_val;
184 
185         // write this out to the encoder as it is
186         enc_.getOutStream().putBytes((const unsigned char*)buf, sizeof(las::point10));
187         return buf + sizeof(las::point10);
188     }
189 
190     // this is not the first point we're trying to compress, do crazy things
191     unsigned int r = this_val.return_number,
192                  n = this_val.number_of_returns_of_given_pulse,
193                  m = number_return_map[n][r],
194                  l = number_return_level[n][r];
195 
196     unsigned int k_bits;
197     int median, diff;
198 
199     // compress which other values have changed
200     int changed_values = detail::changed_values(this_val, last_, last_intensity[m]);
201     enc_.encodeSymbol(m_changed_values, changed_values);
202 
203     // if any of the bit fields changed, compress them
204     if (changed_values & (1 << 5))
205     {
206         unsigned char b = this_val.from_bitfields();
207         unsigned char last_b = last_.from_bitfields();
208         enc_.encodeSymbol(*m_bit_byte[last_b], b);
209     }
210 
211     // if the intensity changed, compress it
212     if (changed_values & (1 << 4))
213     {
214         ic_intensity.compress(enc_, last_intensity[m], this_val.intensity, (m < 3 ? m : 3));
215         last_intensity[m] = this_val.intensity;
216     }
217 
218     // if the classification has changed, compress it
219     if (changed_values & (1 << 3))
220     {
221         enc_.encodeSymbol(*m_classification[last_.classification],
222             this_val.classification);
223     }
224 
225     // if the scan angle rank has changed, compress it
226     if (changed_values & (1 << 2))
227     {
228         enc_.encodeSymbol(*m_scan_angle_rank[this_val.scan_direction_flag],
229             uint8_t(this_val.scan_angle_rank - last_.scan_angle_rank));
230     }
231 
232     // encode user data if changed
233     if (changed_values & (1 << 1))
234     {
235         enc_.encodeSymbol(*m_user_data[last_.user_data], this_val.user_data);
236     }
237 
238     // if the point source id was changed, compress it
239     if (changed_values & 1)
240     {
241         ic_point_source_ID.compress(enc_, last_.point_source_ID, this_val.point_source_ID, 0);
242     }
243 
244     // compress x coordinate
245     median = last_x_diff_median5[m].get();
246     diff = this_val.x - last_.x;
247     ic_dx.compress(enc_, median, diff, n == 1);
248     last_x_diff_median5[m].add(diff);
249 
250     // compress y coordinate
251     k_bits = ic_dx.getK();
252     median = last_y_diff_median5[m].get();
253     diff = this_val.y - last_.y;
254     ic_dy.compress(enc_, median, diff, (n==1) + ( k_bits < 20 ? utils::clearBit<0>(k_bits) : 20));
255     last_y_diff_median5[m].add(diff);
256 
257     // compress z coordinate
258     k_bits = (ic_dx.getK() + ic_dy.getK()) / 2;
259     ic_z.compress(enc_, last_height[l], this_val.z,
260         (n==1) + (k_bits < 18 ? utils::clearBit<0>(k_bits) : 18));
261     last_height[l] = this_val.z;
262     last_ = this_val;
263     return buf + sizeof(las::point10);
264 }
265 
266 // DECOMPRESSOR
267 
Point10Decompressor(decoders::arithmetic<InCbStream> & decoder)268 Point10Decompressor::Point10Decompressor(decoders::arithmetic<InCbStream>& decoder) : dec_(decoder),
269     ic_intensity(16, 4), ic_point_source_ID(16), ic_dx(32, 2), ic_dy(32, 22), ic_z(32, 20),
270     decompressors_inited_(false)
271 {}
272 
init()273 void Point10Decompressor::init()
274 {
275     ic_intensity.init();
276     ic_point_source_ID.init();
277     ic_dx.init();
278     ic_dy.init();
279     ic_z.init();
280 }
281 
decompress(char * buf)282 char *Point10Decompressor::decompress(char *buf)
283 {
284     if (!decompressors_inited_)
285     {
286         init();
287         decompressors_inited_ = true;
288     }
289 
290     // don't have the first data yet, read the whole point out of the stream
291     if (!have_last_)
292     {
293         have_last_ = true;
294         dec_.getInStream().getBytes((unsigned char*)buf, sizeof(las::point10));
295         // decode this value
296         last_.unpack(buf);
297         last_.intensity = 0;
298         return buf + sizeof(las::point10);
299     }
300 
301     unsigned int r, n, m, l, k_bits;
302     int median, diff;
303 
304     // decompress which other values have changed
305     int changed_values = dec_.decodeSymbol(m_changed_values);
306     if (changed_values)
307     {
308         // there was some change in one of the fields (other than x, y and z)
309 
310         // decode bit fields if they have changed
311         if (changed_values & (1 << 5))
312         {
313             unsigned char b = last_.from_bitfields();
314             b = (unsigned char)dec_.decodeSymbol(*m_bit_byte[b]);
315             last_.to_bitfields(b);
316         }
317 
318         r = last_.return_number;
319         n = last_.number_of_returns_of_given_pulse;
320         m = number_return_map[n][r];
321         l = number_return_level[n][r];
322 
323         // decompress the intensity if it has changed
324         if (changed_values & (1 << 4))
325         {
326             last_.intensity = static_cast<unsigned short>(
327                 ic_intensity.decompress(dec_, last_intensity[m], (m < 3 ? m : 3)));
328             last_intensity[m] = last_.intensity;
329         }
330         else
331             last_.intensity = last_intensity[m];
332 
333         // decompress the classification ... if it has changed
334         if (changed_values & (1 << 3)) {
335             last_.classification =
336                 (unsigned char)dec_.decodeSymbol(*m_classification[last_.classification]);
337         }
338 
339         // decompress the scan angle rank if needed
340         if (changed_values & (1 << 2))
341         {
342             int val = dec_.decodeSymbol(*m_scan_angle_rank[last_.scan_direction_flag]);
343             last_.scan_angle_rank = uint8_t(val + last_.scan_angle_rank);
344         }
345 
346         // decompress the user data
347         if (changed_values & (1 << 1))
348         {
349             last_.user_data = (unsigned char)dec_.decodeSymbol(*m_user_data[last_.user_data]);
350         }
351 
352         // decompress the point source ID
353         if (changed_values & 1)
354         {
355             last_.point_source_ID = (unsigned short)ic_point_source_ID.decompress(dec_,
356                 last_.point_source_ID, 0);
357         }
358     }
359     else
360     {
361         r = last_.return_number;
362         n = last_.number_of_returns_of_given_pulse;
363         m = number_return_map[n][r];
364         l = number_return_level[n][r];
365     }
366 
367     // decompress x coordinate
368     median = last_x_diff_median5[m].get();
369     diff = ic_dx.decompress(dec_, median, n==1);
370     last_.x += diff;
371     last_x_diff_median5[m].add(diff);
372 
373     // decompress y coordinate
374     median = last_y_diff_median5[m].get();
375     k_bits = ic_dx.getK();
376     diff = ic_dy.decompress(dec_, median, (n==1) + (k_bits < 20 ? utils::clearBit<0>(k_bits) : 20));
377     last_.y += diff;
378     last_y_diff_median5[m].add(diff);
379 
380     // decompress z coordinate
381     k_bits = (ic_dx.getK() + ic_dy.getK()) / 2;
382     last_.z = ic_z.decompress(dec_, last_height[l],
383         (n==1) + (k_bits < 18 ? utils::clearBit<0>(k_bits) : 18));
384     last_height[l] = last_.z;
385 
386     last_.pack(buf);
387     return buf + sizeof(las::point10);
388 }
389 
390 } // namespace detail
391 } // namespace lazperf
392