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