1 /*
2 * Software License Agreement (BSD License)
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2012-, Open Perception, Inc.
6 *
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * * Neither the name of the copyright holder(s) nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37 #include <pcl/io/low_level_io.h>
38 #include <pcl/io/lzf_image_io.h>
39 #include <pcl/io/lzf.h>
40 #include <pcl/console/print.h>
41 #include <fcntl.h>
42 #include <cstring>
43 #include <boost/filesystem.hpp>
44 #include <boost/property_tree/ptree.hpp>
45 #include <boost/property_tree/xml_parser.hpp>
46
47 #define LZF_HEADER_SIZE 37
48
49
50 // The signature of boost::property_tree::xml_parser::write_xml() changed in Boost 1.56
51 // See https://github.com/PointCloudLibrary/pcl/issues/864
52 #include <boost/version.hpp>
53 #if (BOOST_VERSION >= 105600)
54 using xml_writer_settings = boost::property_tree::xml_writer_settings<std::string>;
55 #else
56 using xml_writer_settings = boost::property_tree::xml_writer_settings<char>;
57 #endif
58
59
60 //////////////////////////////////////////////////////////////////////////////
61 bool
saveImageBlob(const char * data,std::size_t data_size,const std::string & filename)62 pcl::io::LZFImageWriter::saveImageBlob (const char* data,
63 std::size_t data_size,
64 const std::string &filename)
65 {
66 #ifdef _WIN32
67 HANDLE h_native_file = CreateFile (filename.c_str (), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
68 if (h_native_file == INVALID_HANDLE_VALUE)
69 return (false);
70 HANDLE fm = CreateFileMapping (h_native_file, NULL, PAGE_READWRITE, 0, data_size, NULL);
71 char *map = static_cast<char*> (MapViewOfFile (fm, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, data_size));
72 CloseHandle (fm);
73 memcpy (&map[0], data, data_size);
74 UnmapViewOfFile (map);
75 CloseHandle (h_native_file);
76 #else
77 int fd = raw_open (filename.c_str (), O_RDWR | O_CREAT | O_TRUNC, static_cast<mode_t> (0600));
78 if (fd < 0)
79 return (false);
80
81 // Allocate disk space for the entire file to prevent bus errors.
82 if (io::raw_fallocate (fd, data_size) != 0)
83 {
84 raw_close (fd);
85 throw pcl::IOException ("[pcl::PCDWriter::writeBinary] Error during posix_fallocate ()!");
86 return (false);
87 }
88
89 char *map = static_cast<char*> (::mmap (nullptr, data_size, PROT_WRITE, MAP_SHARED, fd, 0));
90 if (map == reinterpret_cast<char*> (-1)) // MAP_FAILED
91 {
92 raw_close (fd);
93 return (false);
94 }
95
96 // Copy the data
97 memcpy (&map[0], data, data_size);
98
99 if (::munmap (map, (data_size)) == -1)
100 {
101 raw_close (fd);
102 return (false);
103 }
104 raw_close (fd);
105 #endif
106 return (true);
107 }
108
109 //////////////////////////////////////////////////////////////////////////////
110 std::uint32_t
compress(const char * input,std::uint32_t uncompressed_size,std::uint32_t width,std::uint32_t height,const std::string & image_type,char * output)111 pcl::io::LZFImageWriter::compress (const char* input,
112 std::uint32_t uncompressed_size,
113 std::uint32_t width,
114 std::uint32_t height,
115 const std::string &image_type,
116 char *output)
117 {
118 static const int header_size = LZF_HEADER_SIZE;
119 float finput_size = static_cast<float> (uncompressed_size);
120 unsigned int compressed_size = pcl::lzfCompress (input,
121 uncompressed_size,
122 &output[header_size],
123 std::uint32_t (finput_size * 1.5f));
124
125 std::uint32_t compressed_final_size = 0;
126 if (compressed_size)
127 {
128 // Copy the header first
129 const char header[] = "PCLZF";
130 memcpy (&output[0], &header[0], 5);
131 memcpy (&output[5], &width, sizeof (std::uint32_t));
132 memcpy (&output[9], &height, sizeof (std::uint32_t));
133 std::string itype = image_type;
134 // Cut or pad the string
135 if (itype.size () > 16)
136 {
137 PCL_WARN ("[pcl::io::LZFImageWriter::compress] Image type should be a string of maximum 16 characters! Cutting %s to %s.\n", image_type.c_str (), image_type.substr (0, 15).c_str ());
138 itype = itype.substr (0, 15);
139 }
140 if (itype.size () < 16)
141 itype.insert (itype.end (), 16 - itype.size (), ' ');
142
143 memcpy (&output[13], &itype[0], 16);
144 memcpy (&output[29], &compressed_size, sizeof (std::uint32_t));
145 memcpy (&output[33], &uncompressed_size, sizeof (std::uint32_t));
146 compressed_final_size = std::uint32_t (compressed_size + header_size);
147 }
148
149 return (compressed_final_size);
150 }
151
152 //////////////////////////////////////////////////////////////////////////////
153 bool
write(const char * data,std::uint32_t width,std::uint32_t height,const std::string & filename)154 pcl::io::LZFDepth16ImageWriter::write (const char* data,
155 std::uint32_t width, std::uint32_t height,
156 const std::string &filename)
157 {
158 // Prepare the compressed depth buffer
159 unsigned int depth_size = width * height * 2;
160 char* compressed_depth = static_cast<char*> (malloc (std::size_t (float (depth_size) * 1.5f + float (LZF_HEADER_SIZE))));
161
162 std::size_t compressed_size = compress (data,
163 depth_size,
164 width, height,
165 "depth16",
166 compressed_depth);
167 if (compressed_size == 0)
168 {
169 free (compressed_depth);
170 return (false);
171 }
172
173 // Save the actual image
174 saveImageBlob (compressed_depth, compressed_size, filename);
175 free (compressed_depth);
176 return (true);
177 }
178
179 //////////////////////////////////////////////////////////////////////////////
180 bool
writeParameter(const double & parameter,const std::string & tag,const std::string & filename)181 pcl::io::LZFImageWriter::writeParameter (const double ¶meter,
182 const std::string &tag,
183 const std::string &filename)
184 {
185 boost::property_tree::ptree pt;
186 try
187 {
188 boost::property_tree::xml_parser::read_xml (filename, pt, boost::property_tree::xml_parser::trim_whitespace);
189 }
190 catch (std::exception&)
191 {}
192
193 pt.put (tag, parameter);
194 write_xml (filename, pt, std::locale (), xml_writer_settings ('\t', 1));
195
196 return (true);
197 }
198
199 //////////////////////////////////////////////////////////////////////////////
200 bool
writeParameters(const pcl::io::CameraParameters & parameters,const std::string & filename)201 pcl::io::LZFDepth16ImageWriter::writeParameters (const pcl::io::CameraParameters ¶meters,
202 const std::string &filename)
203 {
204 boost::property_tree::ptree pt;
205 try
206 {
207 boost::property_tree::xml_parser::read_xml (filename, pt, boost::property_tree::xml_parser::trim_whitespace);
208 }
209 catch (std::exception&)
210 {}
211
212 pt.put ("depth.focal_length_x", parameters.focal_length_x);
213 pt.put ("depth.focal_length_y", parameters.focal_length_y);
214 pt.put ("depth.principal_point_x", parameters.principal_point_x);
215 pt.put ("depth.principal_point_y", parameters.principal_point_y);
216 pt.put ("depth.z_multiplication_factor", z_multiplication_factor_);
217 write_xml (filename, pt, std::locale (), xml_writer_settings ('\t', 1));
218
219 return (true);
220 }
221
222 //////////////////////////////////////////////////////////////////////////////
223 bool
write(const char * data,std::uint32_t width,std::uint32_t height,const std::string & filename)224 pcl::io::LZFRGB24ImageWriter::write (const char *data,
225 std::uint32_t width, std::uint32_t height,
226 const std::string &filename)
227 {
228 // Transform RGBRGB into RRGGBB for better compression
229 std::vector<char> rrggbb (width * height * 3);
230 int ptr1 = 0,
231 ptr2 = width * height,
232 ptr3 = 2 * width * height;
233 for (std::uint32_t i = 0; i < width * height; ++i, ++ptr1, ++ptr2, ++ptr3)
234 {
235 rrggbb[ptr1] = data[i * 3 + 0];
236 rrggbb[ptr2] = data[i * 3 + 1];
237 rrggbb[ptr3] = data[i * 3 + 2];
238 }
239
240 char* compressed_rgb = static_cast<char*> (malloc (std::size_t (float (rrggbb.size ()) * 1.5f + float (LZF_HEADER_SIZE))));
241 std::size_t compressed_size = compress (reinterpret_cast<const char*> (&rrggbb[0]),
242 std::uint32_t (rrggbb.size ()),
243 width, height,
244 "rgb24",
245 compressed_rgb);
246
247 if (compressed_size == 0)
248 {
249 free (compressed_rgb);
250 return (false);
251 }
252
253 // Save the actual image
254 saveImageBlob (compressed_rgb, compressed_size, filename);
255 free (compressed_rgb);
256 return (true);
257 }
258
259 //////////////////////////////////////////////////////////////////////////////
260 bool
writeParameters(const pcl::io::CameraParameters & parameters,const std::string & filename)261 pcl::io::LZFRGB24ImageWriter::writeParameters (const pcl::io::CameraParameters ¶meters,
262 const std::string &filename)
263 {
264 boost::property_tree::ptree pt;
265 try
266 {
267 boost::property_tree::xml_parser::read_xml (filename, pt, boost::property_tree::xml_parser::trim_whitespace);
268 }
269 catch (std::exception&)
270 {}
271
272 pt.put ("rgb.focal_length_x", parameters.focal_length_x);
273 pt.put ("rgb.focal_length_y", parameters.focal_length_y);
274 pt.put ("rgb.principal_point_x", parameters.principal_point_x);
275 pt.put ("rgb.principal_point_y", parameters.principal_point_y);
276 write_xml (filename, pt, std::locale (), xml_writer_settings ('\t', 1));
277
278 return (true);
279 }
280
281 //////////////////////////////////////////////////////////////////////////////
282 bool
write(const char * data,std::uint32_t width,std::uint32_t height,const std::string & filename)283 pcl::io::LZFYUV422ImageWriter::write (const char *data,
284 std::uint32_t width, std::uint32_t height,
285 const std::string &filename)
286 {
287 // Transform YUV422 into UUUYYYYYYVVV for better compression
288 std::vector<char> uuyyvv (width * height * 2);
289 int wh2 = width * height / 2,
290 ptr1 = 0, // u
291 ptr2 = wh2, // y
292 ptr3 = wh2 + width * height; // v
293 for (int i = 0; i < wh2; ++i, ++ptr1, ptr2 += 2, ++ptr3)
294 {
295 uuyyvv[ptr1] = data[i * 4 + 0]; // u
296 uuyyvv[ptr2 + 0] = data[i * 4 + 1]; // y
297 uuyyvv[ptr2 + 1] = data[i * 4 + 3]; // y
298 uuyyvv[ptr3] = data[i * 4 + 2]; // v
299 }
300
301 char* compressed_yuv = static_cast<char*> (malloc (std::size_t (float (uuyyvv.size ()) * 1.5f + float (LZF_HEADER_SIZE))));
302 std::size_t compressed_size = compress (reinterpret_cast<const char*> (&uuyyvv[0]),
303 std::uint32_t (uuyyvv.size ()),
304 width, height,
305 "yuv422",
306 compressed_yuv);
307
308 if (compressed_size == 0)
309 {
310 free (compressed_yuv);
311 return (false);
312 }
313
314 // Save the actual image
315 saveImageBlob (compressed_yuv, compressed_size, filename);
316 free (compressed_yuv);
317 return (true);
318 }
319
320 //////////////////////////////////////////////////////////////////////////////
321 bool
write(const char * data,std::uint32_t width,std::uint32_t height,const std::string & filename)322 pcl::io::LZFBayer8ImageWriter::write (const char *data,
323 std::uint32_t width, std::uint32_t height,
324 const std::string &filename)
325 {
326 unsigned int bayer_size = width * height;
327 char* compressed_bayer = static_cast<char*> (malloc (std::size_t (float (bayer_size) * 1.5f + float (LZF_HEADER_SIZE))));
328 std::size_t compressed_size = compress (data,
329 bayer_size,
330 width, height,
331 "bayer8",
332 compressed_bayer);
333
334 if (compressed_size == 0)
335 {
336 free (compressed_bayer);
337 return (false);
338 }
339
340 // Save the actual image
341 saveImageBlob (compressed_bayer, compressed_size, filename);
342 free (compressed_bayer);
343 return (true);
344 }
345
346 //////////////////////////////////////////////////////////////////////////////
347 //////////////////////////////////////////////////////////////////////////////
348 //////////////////////////////////////////////////////////////////////////////
LZFImageReader()349 pcl::io::LZFImageReader::LZFImageReader ()
350 : width_ ()
351 , height_ ()
352 , parameters_ ()
353 {
354 }
355
356 //////////////////////////////////////////////////////////////////////////////
357 bool
loadImageBlob(const std::string & filename,std::vector<char> & data,std::uint32_t & uncompressed_size)358 pcl::io::LZFImageReader::loadImageBlob (const std::string &filename,
359 std::vector<char> &data,
360 std::uint32_t &uncompressed_size)
361 {
362 if (filename.empty() || !boost::filesystem::exists (filename))
363 {
364 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Could not find file '%s'.\n", filename.c_str ());
365 return (false);
366 }
367 // Open for reading
368 int fd = raw_open (filename.c_str (), O_RDONLY);
369 if (fd == -1)
370 {
371 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Failure to open file %s\n", filename.c_str () );
372 return (false);
373 }
374
375 // Seek to the end of file to get the filesize
376 long data_size = raw_lseek (fd, 0, SEEK_END);
377 if (data_size < 0)
378 {
379 raw_close (fd);
380 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] lseek errno: %d strerror: %s\n", errno, strerror (errno));
381 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Error during lseek ()!\n");
382 return (false);
383 }
384 raw_lseek (fd, 0, SEEK_SET);
385
386 #ifdef _WIN32
387 // As we don't know the real size of data (compressed or not),
388 // we set dwMaximumSizeHigh = dwMaximumSizeLow = 0 so as to map the whole file
389 HANDLE fm = CreateFileMapping ((HANDLE) _get_osfhandle (fd), NULL, PAGE_READONLY, 0, 0, NULL);
390 // As we don't know the real size of data (compressed or not),
391 // we set dwNumberOfBytesToMap = 0 so as to map the whole file
392 char *map = static_cast<char*>(MapViewOfFile (fm, FILE_MAP_READ, 0, 0, 0));
393 if (map == NULL)
394 {
395 CloseHandle (fm);
396 raw_close (fd);
397 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Error mapping view of file, %s\n", filename.c_str ());
398 return (false);
399 }
400 #else
401 char *map = static_cast<char*> (::mmap (nullptr, data_size, PROT_READ, MAP_SHARED, fd, 0));
402 if (map == reinterpret_cast<char*> (-1)) // MAP_FAILED
403 {
404 raw_close (fd);
405 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Error preparing mmap for PCLZF file.\n");
406 return (false);
407 }
408 #endif
409
410 // Check the header identifier here
411 char header_string[5];
412 memcpy (&header_string, &map[0], 5); // PCLZF
413 if (std::string (header_string).substr (0, 5) != "PCLZF")
414 {
415 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Wrong signature header! Should be 'P'C'L'Z'F'.\n");
416 #ifdef _WIN32
417 UnmapViewOfFile (map);
418 CloseHandle (fm);
419 #else
420 ::munmap (map, data_size);
421 #endif
422 return (false);
423 }
424 memcpy (&width_, &map[5], sizeof (std::uint32_t));
425 memcpy (&height_, &map[9], sizeof (std::uint32_t));
426 char imgtype_string[16];
427 memcpy (&imgtype_string, &map[13], 16); // BAYER8, RGB24_, YUV422_, ...
428 image_type_identifier_ = std::string (imgtype_string).substr (0, 15);
429 image_type_identifier_.insert (image_type_identifier_.end (), 1, '\0');
430
431 static const int header_size = LZF_HEADER_SIZE;
432 std::uint32_t compressed_size;
433 memcpy (&compressed_size, &map[29], sizeof (std::uint32_t));
434
435 if (compressed_size + header_size != data_size)
436 {
437 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Number of bytes to decompress written in file (%u) differs from what it should be (%u)!\n", compressed_size, data_size - header_size);
438 #ifdef _WIN32
439 UnmapViewOfFile (map);
440 CloseHandle (fm);
441 #else
442 ::munmap (map, data_size);
443 #endif
444 return (false);
445 }
446
447 memcpy (&uncompressed_size, &map[33], sizeof (std::uint32_t));
448
449 data.resize (compressed_size);
450 memcpy (&data[0], &map[header_size], compressed_size);
451
452 #ifdef _WIN32
453 UnmapViewOfFile (map);
454 CloseHandle (fm);
455 #else
456 if (::munmap (map, data_size) == -1)
457 PCL_ERROR ("[pcl::io::LZFImageReader::loadImage] Munmap failure\n");
458 #endif
459 raw_close (fd);
460
461 return (true);
462 }
463
464 //////////////////////////////////////////////////////////////////////////////
465 bool
decompress(const std::vector<char> & input,std::vector<char> & output)466 pcl::io::LZFImageReader::decompress (const std::vector<char> &input,
467 std::vector<char> &output)
468 {
469 if (output.empty ())
470 {
471 PCL_ERROR ("[pcl::io::LZFImageReader::decompress] Output array needs to be preallocated! The correct uncompressed array value should have been stored during the compression.\n");
472 return (false);
473 }
474 unsigned int tmp_size = pcl::lzfDecompress (static_cast<const char*>(&input[0]),
475 std::uint32_t (input.size ()),
476 static_cast<char*>(&output[0]),
477 std::uint32_t (output.size ()));
478
479 if (tmp_size != output.size ())
480 {
481 PCL_WARN ("[pcl::io::LZFImageReader::decompress] Size of decompressed lzf data (%u) does not match the uncompressed size value (%u). Errno: %d\n", tmp_size, output.size (), errno);
482 return (false);
483 }
484 return (true);
485 }
486
487 //////////////////////////////////////////////////////////////////////////////
488 bool
readParameters(const std::string & filename)489 pcl::io::LZFImageReader::readParameters (const std::string &filename)
490 {
491 std::filebuf fb;
492 std::filebuf *f = fb.open (filename.c_str (), std::ios::in);
493 if (f == nullptr)
494 return (false);
495 std::istream is (&fb);
496 bool res = readParameters (is);
497 fb.close ();
498 return (res);
499 }
500
501 //////////////////////////////////////////////////////////////////////////////
502 bool
readParameters(std::istream & is)503 pcl::io::LZFRGB24ImageReader::readParameters (std::istream& is)
504 {
505 boost::property_tree::ptree pt;
506 read_xml (is, pt, boost::property_tree::xml_parser::trim_whitespace);
507
508 boost::optional<boost::property_tree::ptree&> tree = pt.get_child_optional ("rgb");
509 if (!tree)
510 return (false);
511
512 parameters_.focal_length_x = tree.get ().get<double>("focal_length_x");
513 parameters_.focal_length_y = tree.get ().get<double>("focal_length_y");
514 parameters_.principal_point_x = tree.get ().get<double>("principal_point_x");
515 parameters_.principal_point_y = tree.get ().get<double>("principal_point_y");
516 PCL_DEBUG ("[pcl::io::LZFRGB24ImageReader::readParameters] Read camera parameters (fx,fy,cx,cy): %g,%g,%g,%g.\n",
517 parameters_.focal_length_x, parameters_.focal_length_y,
518 parameters_.principal_point_x, parameters_.principal_point_y);
519 return (true);
520 }
521
522 //////////////////////////////////////////////////////////////////////////////
523 bool
readParameters(std::istream & is)524 pcl::io::LZFDepth16ImageReader::readParameters (std::istream& is)
525 {
526 boost::property_tree::ptree pt;
527 read_xml (is, pt, boost::property_tree::xml_parser::trim_whitespace);
528
529 boost::optional<boost::property_tree::ptree&> tree = pt.get_child_optional ("depth");
530 if (!tree)
531 return (false);
532
533 parameters_.focal_length_x = tree.get ().get<double>("focal_length_x");
534 parameters_.focal_length_y = tree.get ().get<double>("focal_length_y");
535 parameters_.principal_point_x = tree.get ().get<double>("principal_point_x");
536 parameters_.principal_point_y = tree.get ().get<double>("principal_point_y");
537 z_multiplication_factor_ = tree.get ().get<double>("z_multiplication_factor");
538 PCL_DEBUG ("[pcl::io::LZFDepth16ImageReader::readParameters] Read camera parameters (fx,fy,cx,cy): %g,%g,%g,%g.\n",
539 parameters_.focal_length_x, parameters_.focal_length_y,
540 parameters_.principal_point_x, parameters_.principal_point_y);
541 PCL_DEBUG ("[pcl::io::LZFDepth16ImageReader::readParameters] Multiplication factor: %g.\n", z_multiplication_factor_);
542 return (true);
543 }
544
545