ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FilePNG.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <Logging.h>
9 #include <png.h>
10 
11 #include "cloudViewer/core/Dtype.h"
13 
14 namespace cloudViewer {
15 namespace t {
16 namespace io {
17 
19  int quality,
20  png_image &pngimage) {
21  pngimage.width = image.GetCols();
22  pngimage.height = image.GetRows();
23  pngimage.format = pngimage.flags = 0;
24 
25  if (image.GetDtype() == core::UInt16) {
26  pngimage.format |= PNG_FORMAT_FLAG_LINEAR;
27  }
28  if (image.GetChannels() >= 3) {
29  pngimage.format |= PNG_FORMAT_FLAG_COLOR;
30  }
31  if (image.GetChannels() == 4) {
32  pngimage.format |= PNG_FORMAT_FLAG_ALPHA;
33  }
34  if (quality <= 2) {
35  pngimage.flags |= PNG_IMAGE_FLAG_FAST;
36  }
37 }
38 
39 bool ReadImageFromPNG(const std::string &filename, geometry::Image &image) {
40  png_image pngimage;
41  memset(&pngimage, 0, sizeof(pngimage));
42  pngimage.version = PNG_IMAGE_VERSION;
43  if (png_image_begin_read_from_file(&pngimage, filename.c_str()) == 0) {
44  utility::LogWarning("Read PNG failed: unable to parse header.");
45  image.Clear();
46  return false;
47  }
48 
49  // Clear colormap flag if necessary to ensure libpng expands the color
50  // indexed pixels to full color
51  if (pngimage.format & PNG_FORMAT_FLAG_COLORMAP) {
52  pngimage.format &= ~PNG_FORMAT_FLAG_COLORMAP;
53  }
54  if (pngimage.format & PNG_FORMAT_FLAG_LINEAR) {
55  image.Reset(pngimage.height, pngimage.width,
56  PNG_IMAGE_SAMPLE_CHANNELS(pngimage.format), core::UInt16,
57  image.GetDevice());
58  } else {
59  image.Reset(pngimage.height, pngimage.width,
60  PNG_IMAGE_SAMPLE_CHANNELS(pngimage.format), core::UInt8,
61  image.GetDevice());
62  }
63 
64  if (png_image_finish_read(&pngimage, NULL, image.GetDataPtr(), 0, NULL) ==
65  0) {
66  utility::LogWarning("Read PNG failed: unable to read file: {}",
67  filename);
68  utility::LogWarning("PNG error: {}", pngimage.message);
69  image.Clear();
70  return false;
71  }
72  return true;
73 }
74 
75 bool WriteImageToPNG(const std::string &filename,
76  const geometry::Image &image,
77  int quality) {
78  if (image.IsEmpty()) {
79  utility::LogWarning("Write PNG failed: image has no data.");
80  return false;
81  }
82  if (image.GetDtype() != core::Bool && image.GetDtype() != core::UInt8 &&
83  image.GetDtype() != core::UInt16) {
84  utility::LogWarning("Write PNG failed: unsupported image data.");
85  return false;
86  }
87  if (quality == kCloudViewerImageIODefaultQuality) // Set default quality
88  {
89  quality = 6;
90  }
91  if (quality < 0 || quality > 9) {
93  "Write PNG failed: quality ({}) must be in the range [0,9]",
94  quality);
95  return false;
96  }
97  png_image pngimage;
98  memset(&pngimage, 0, sizeof(pngimage));
99  pngimage.version = PNG_IMAGE_VERSION;
100  SetPNGImageFromImage(image, quality, pngimage);
101  if (png_image_write_to_file(&pngimage, filename.c_str(), 0,
102  image.GetDataPtr(), 0, NULL) == 0) {
103  utility::LogWarning("Write PNG failed: unable to write file: {}",
104  filename);
105  return false;
106  }
107  return true;
108 }
109 
110 bool WriteImageToPNGInMemory(std::vector<uint8_t> &buffer,
111  const t::geometry::Image &image,
112  int quality) {
113  if (image.IsEmpty()) {
114  utility::LogWarning("Write PNG failed: image has no data.");
115  return false;
116  }
117  if (image.GetDtype() != core::UInt8 && image.GetDtype() != core::UInt16) {
118  utility::LogWarning("Write PNG failed: unsupported image data.");
119  return false;
120  }
121  if (quality == kCloudViewerImageIODefaultQuality) // Set default quality
122  {
123  quality = 6;
124  }
125  if (quality < 0 || quality > 9) {
127  "Write PNG failed: quality ({}) must be in the range [0,9]",
128  quality);
129  return false;
130  }
131  png_image pngimage;
132  memset(&pngimage, 0, sizeof(pngimage));
133  pngimage.version = PNG_IMAGE_VERSION;
134  SetPNGImageFromImage(image, quality, pngimage);
135 
136  // Compute bytes required
137  size_t mem_bytes = 0;
138  if (png_image_write_to_memory(&pngimage, nullptr, &mem_bytes, 0,
139  image.GetDataPtr(), 0, nullptr) == 0) {
141  "Could not compute bytes needed for encoding to PNG in "
142  "memory.");
143  return false;
144  }
145  buffer.resize(mem_bytes);
146  if (png_image_write_to_memory(&pngimage, &buffer[0], &mem_bytes, 0,
147  image.GetDataPtr(), 0, nullptr) == 0) {
148  utility::LogWarning("Unable to encode to PNG in memory.");
149  return false;
150  }
151  return true;
152 }
153 
154 } // namespace io
155 } // namespace t
156 } // namespace cloudViewer
std::string filename
std::shared_ptr< core::Tensor > image
#define NULL
The Image class stores image with customizable rows, cols, channels, dtype and device.
Definition: Image.h:29
#define LogWarning(...)
Definition: Logging.h:72
const Dtype Bool
Definition: Dtype.cpp:52
const Dtype UInt8
Definition: Dtype.cpp:48
const Dtype UInt16
Definition: Dtype.cpp:49
bool WriteImageToPNGInMemory(std::vector< uint8_t > &buffer, const t::geometry::Image &image, int quality)
Definition: FilePNG.cpp:110
bool ReadImageFromPNG(const std::string &filename, geometry::Image &image)
Definition: FilePNG.cpp:39
constexpr int kCloudViewerImageIODefaultQuality
Definition: ImageIO.h:33
bool WriteImageToPNG(const std::string &filename, const geometry::Image &image, int quality)
Definition: FilePNG.cpp:75
static void SetPNGImageFromImage(const geometry::Image &image, int quality, png_image &pngimage)
Definition: FilePNG.cpp:18
Generic file read and write utility for python interface.