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 &parameter,
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 &parameters,
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 &parameters,
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