ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FileJPG.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 // clang-format off
9 #include <cstddef>
10 #include <cstdio>
11 #include <jpeglib.h> // Include after cstddef to define size_t
12 // clang-format on
13 
14 #include <FileSystem.h>
15 #include <Logging.h>
16 
18 
19 namespace cloudViewer {
20 namespace t {
21 namespace io {
22 
23 namespace {
24 
27 void jpeg_error_throw(j_common_ptr p_cinfo) {
28  if (p_cinfo->is_decompressor)
29  jpeg_destroy_decompress(
30  reinterpret_cast<jpeg_decompress_struct *>(p_cinfo));
31  else
32  jpeg_destroy_compress(
33  reinterpret_cast<jpeg_compress_struct *>(p_cinfo));
34  char buffer[JMSG_LENGTH_MAX];
35  (*p_cinfo->err->format_message)(p_cinfo, buffer);
36  throw std::runtime_error(buffer);
37 }
38 
39 } // namespace
40 
41 bool ReadImageFromJPG(const std::string &filename, geometry::Image &image) {
42  struct jpeg_decompress_struct cinfo;
43  struct jpeg_error_mgr jerr;
44  FILE *file_in;
45  JSAMPARRAY buffer;
46 
47  if ((file_in = utility::filesystem::FOpen(filename, "rb")) == NULL) {
48  utility::LogWarning("Read JPG failed: unable to open file: {}",
49  filename);
50  image.Clear();
51  return false;
52  }
53 
54  try {
55  cinfo.err = jpeg_std_error(&jerr);
56  jerr.error_exit = jpeg_error_throw;
57  jpeg_create_decompress(&cinfo);
58  jpeg_stdio_src(&cinfo, file_in);
59  jpeg_read_header(&cinfo, TRUE);
60 
61  // We only support two channel types: gray, and RGB.
62  int num_of_channels = 3;
63  switch (cinfo.jpeg_color_space) {
64  case JCS_RGB:
65  case JCS_YCbCr:
66  cinfo.out_color_space = JCS_RGB;
67  cinfo.out_color_components = 3;
68  num_of_channels = 3;
69  break;
70  case JCS_GRAYSCALE:
71  cinfo.jpeg_color_space = JCS_GRAYSCALE;
72  cinfo.out_color_components = 1;
73  num_of_channels = 1;
74  break;
75  case JCS_CMYK:
76  case JCS_YCCK:
77  default:
79  "Read JPG failed: color space not supported.");
80  jpeg_destroy_decompress(&cinfo);
81  fclose(file_in);
82  image.Clear();
83  return false;
84  }
85  jpeg_start_decompress(&cinfo);
86  image.Clear();
87  image.Reset(cinfo.output_height, cinfo.output_width, num_of_channels,
88  core::UInt8, image.GetDevice());
89 
90  int row_stride = cinfo.output_width * cinfo.output_components;
91  buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE,
92  row_stride, 1);
93  uint8_t *pdata = static_cast<uint8_t *>(image.GetDataPtr());
94 
95  while (cinfo.output_scanline < cinfo.output_height) {
96  jpeg_read_scanlines(&cinfo, buffer, 1);
97  core::MemoryManager::MemcpyFromHost(pdata, image.GetDevice(),
98  buffer[0], row_stride * 1);
99  pdata += row_stride;
100  }
101  jpeg_finish_decompress(&cinfo);
102  jpeg_destroy_decompress(&cinfo);
103  fclose(file_in);
104  return true;
105  } catch (const std::runtime_error &err) {
106  fclose(file_in);
107  image.Clear();
108  utility::LogWarning("libjpeg error: {}", err.what());
109  return false;
110  }
111 }
112 
113 bool WriteImageToJPG(const std::string &filename,
114  const geometry::Image &image,
115  int quality /* = kCloudViewerImageIODefaultQuality*/) {
116  if (image.IsEmpty()) {
117  utility::LogWarning("Write JPG failed: image has no data.");
118  return false;
119  }
120  if (image.GetDtype() != core::UInt8 ||
121  (image.GetChannels() != 1 && image.GetChannels() != 3)) {
122  utility::LogWarning("Write JPG failed: unsupported image data.");
123  return false;
124  }
125  if (quality == kCloudViewerImageIODefaultQuality) // Set default quality
126  quality = 90;
127  if (quality < 0 || quality > 100) {
129  "Write JPG failed: image quality should be in the range "
130  "[0,100].");
131  return false;
132  }
133 
134  struct jpeg_compress_struct cinfo;
135  struct jpeg_error_mgr jerr;
136  FILE *file_out;
137  JSAMPROW row_pointer[1];
138 
139  if ((file_out = utility::filesystem::FOpen(filename, "wb")) == NULL) {
140  utility::LogWarning("Write JPG failed: unable to open file: {}",
141  filename);
142  return false;
143  }
144 
145  try {
146  cinfo.err = jpeg_std_error(&jerr);
147  jerr.error_exit = jpeg_error_throw;
148  jpeg_create_compress(&cinfo);
149  jpeg_stdio_dest(&cinfo, file_out);
150  cinfo.image_width = image.GetCols();
151  cinfo.image_height = image.GetRows();
152  cinfo.input_components = image.GetChannels();
153  cinfo.in_color_space =
154  (cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB);
155  jpeg_set_defaults(&cinfo);
156  jpeg_set_quality(&cinfo, quality, TRUE);
157  jpeg_start_compress(&cinfo, TRUE);
158  int row_stride = image.GetCols() * image.GetChannels();
159  const uint8_t *pdata = static_cast<const uint8_t *>(image.GetDataPtr());
160  std::vector<uint8_t> buffer(row_stride);
161  while (cinfo.next_scanline < cinfo.image_height) {
163  buffer.data(), pdata, image.GetDevice(), row_stride * 1);
164  row_pointer[0] = buffer.data();
165  jpeg_write_scanlines(&cinfo, row_pointer, 1);
166  pdata += row_stride;
167  }
168  jpeg_finish_compress(&cinfo);
169  fclose(file_out);
170  jpeg_destroy_compress(&cinfo);
171  return true;
172  } catch (const std::runtime_error &err) {
173  fclose(file_out);
174  utility::LogWarning(err.what());
175  return false;
176  }
177 }
178 
179 } // namespace io
180 } // namespace t
181 } // namespace cloudViewer
std::string filename
std::shared_ptr< core::Tensor > image
#define NULL
static void MemcpyFromHost(void *dst_ptr, const Device &dst_device, const void *host_ptr, size_t num_bytes)
Same as Memcpy, but with host (CPU:0) as default src_device.
static void MemcpyToHost(void *host_ptr, const void *src_ptr, const Device &src_device, size_t num_bytes)
Same as Memcpy, but with host (CPU:0) as default dst_device.
The Image class stores image with customizable rows, cols, channels, dtype and device.
Definition: Image.h:29
#define LogWarning(...)
Definition: Logging.h:72
#define TRUE
Definition: lsd.c:119
const Dtype UInt8
Definition: Dtype.cpp:48
constexpr int kCloudViewerImageIODefaultQuality
Definition: ImageIO.h:33
bool WriteImageToJPG(const std::string &filename, const geometry::Image &image, int quality)
Definition: FileJPG.cpp:113
bool ReadImageFromJPG(const std::string &filename, geometry::Image &image)
Definition: FileJPG.cpp:41
FILE * FOpen(const std::string &filename, const std::string &mode)
Definition: FileSystem.cpp:609
Generic file read and write utility for python interface.