Main MRPT website > C++ reference for MRPT 1.9.9
CImage_JPEG_streams.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "img-precomp.h" // Precompiled headers
11 
12 #include <mrpt/img/CImage.h>
13 #include <mrpt/io/CStream.h>
14 
15 // Universal include for all versions of OpenCV
16 #include <mrpt/otherlibs/do_opencv_includes.h>
17 
18 using namespace mrpt;
19 using namespace mrpt::img;
20 
21 // ---------------------------------------------------------------------------------------
22 // START OF JPEG FUNCTIONS PART
23 // ---------------------------------------------------------------------------------------
24 /* Expanded data destination object for stdio output */
25 
26 //#undef INT32
27 #undef FAR
28 #define XMD_H
29 
30 #include <stdio.h>
31 
32 #include <jpeglib.h>
33 #define mrpt_jpeg_source_mgr jpeg_source_mgr
34 
35 using mrpt::io::CStream;
36 
37 typedef struct
38 {
39  struct jpeg_destination_mgr pub; /* public fields */
40 
41  CStream* out; /* target stream */
42  JOCTET* buffer; /* start of buffer */
44 
46 
47 #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
48 
49 /*
50  * Initialize destination --- called by jpeg_start_compress
51  * before any data is actually written.
52  */
53 
54 METHODDEF(void)
56 {
57  mrpt_dest_ptr dest = (mrpt_dest_ptr)cinfo->dest;
58 
59  /* Allocate the output buffer --- it will be released when done with image
60  */
61  dest->buffer = (JOCTET*)(*cinfo->mem->alloc_small)(
62  (j_common_ptr)cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof(JOCTET));
63 
64  dest->pub.next_output_byte = dest->buffer;
66 }
67 
68 /*
69  * Empty the output buffer --- called whenever buffer fills up.
70  *
71  * In typical applications, this should write the entire output buffer
72  * (ignoring the current state of next_output_byte & free_in_buffer),
73  * reset the pointer & count to the start of the buffer, and return TRUE
74  * indicating that the buffer has been dumped.
75  *
76  * In applications that need to be able to suspend compression due to output
77  * overrun, a FALSE return indicates that the buffer cannot be emptied now.
78  * In this situation, the compressor will return to its caller (possibly with
79  * an indication that it has not accepted all the supplied scanlines). The
80  * application should resume compression after it has made more room in the
81  * output buffer. Note that there are substantial restrictions on the use of
82  * suspension --- see the documentation.
83  *
84  * When suspending, the compressor will back up to a convenient restart point
85  * (typically the start of the current MCU). next_output_byte & free_in_buffer
86  * indicate where the restart point will be if the current call returns FALSE.
87  * Data beyond this point will be regenerated after resumption, so do not
88  * write it out when emptying the buffer externally.
89  */
90 
91 METHODDEF(boolean)
93 {
94  mrpt_dest_ptr dest = (mrpt_dest_ptr)cinfo->dest;
95 
96  dest->out->Write(dest->buffer, OUTPUT_BUF_SIZE);
97 
98  dest->pub.next_output_byte = dest->buffer;
100 
101  return TRUE;
102 }
103 
104 /*
105  * Terminate destination --- called by jpeg_finish_compress
106  * after all data has been written. Usually needs to flush buffer.
107  *
108  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
109  * application must deal with any cleanup that should happen even
110  * for error exit.
111  */
112 
113 METHODDEF(void)
115 {
116  mrpt_dest_ptr dest = (mrpt_dest_ptr)cinfo->dest;
117  size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
118 
119  /* Write any data remaining in the buffer */
120  if (datacount > 0) dest->out->Write(dest->buffer, (int)datacount);
121 }
122 
123 GLOBAL(void)
125 {
126  mrpt_dest_ptr dest;
127 
128  /* The destination object is made permanent so that multiple JPEG images
129  * can be written to the same file without re-executing jpeg_stdio_dest.
130  * This makes it dangerous to use this manager and a different destination
131  * manager serially with the same JPEG object, because their private object
132  * sizes may be different. Caveat programmer.
133  */
134  if (cinfo->dest == nullptr)
135  { /* first time for this JPEG object? */
136  cinfo->dest = (jpeg_destination_mgr*)(*cinfo->mem->alloc_small)(
138  }
139 
140  dest = (mrpt_dest_ptr)cinfo->dest;
141  dest->pub.init_destination = init_destination;
142  dest->pub.empty_output_buffer = empty_output_buffer;
143  dest->pub.term_destination = term_destination;
144  dest->out = out;
145 }
146 
147 // -------------------------------------------------------------
148 
149 /* Expanded data source object for stdio input */
150 
151 typedef struct
152 {
153  mrpt_jpeg_source_mgr pub; /* public fields */
154  CStream* in; /* source stream */
155  JOCTET* buffer; /* start of buffer */
156  boolean start_of_file; /* have we gotten any data yet? */
157 } my_source_mgr;
158 
160 
161 #define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
162 
163 /*
164  * Initialize source --- called by jpeg_read_header
165  * before any data is actually read.
166  */
167 
168 METHODDEF(void)
170 {
171  my_src_ptr src = (my_src_ptr)cinfo->src;
172 
173  /* We reset the empty-input-file flag for each image,
174  * but we don't clear the input buffer.
175  * This is correct behavior for reading a series of images from one source.
176  */
178 }
179 
180 /*
181  * Fill the input buffer --- called whenever buffer is emptied.
182  *
183  * In typical applications, this should read fresh data into the buffer
184  * (ignoring the current state of next_input_byte & bytes_in_buffer),
185  * reset the pointer & count to the start of the buffer, and return TRUE
186  * indicating that the buffer has been reloaded. It is not necessary to
187  * fill the buffer entirely, only to obtain at least one more byte.
188  *
189  * There is no such thing as an EOF return. If the end of the file has been
190  * reached, the routine has a choice of ERREXIT() or inserting fake data into
191  * the buffer. In most cases, generating a warning message and inserting a
192  * fake EOI marker is the best course of action --- this will allow the
193  * decompressor to output however much of the image is there. However,
194  * the resulting error message is misleading if the real problem is an empty
195  * input file, so we handle that case specially.
196  *
197  * In applications that need to be able to suspend compression due to input
198  * not being available yet, a FALSE return indicates that no more data can be
199  * obtained right now, but more may be forthcoming later. In this situation,
200  * the decompressor will return to its caller (with an indication of the
201  * number of scanlines it has read, if any). The application should resume
202  * decompression after it has loaded more data into the input buffer. Note
203  * that there are substantial restrictions on the use of suspension --- see
204  * the documentation.
205  *
206  * When suspending, the decompressor will back up to a convenient restart point
207  * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
208  * indicate where the restart point will be if the current call returns FALSE.
209  * Data beyond this point must be rescanned after resumption, so move it to
210  * the front of the buffer rather than discarding it.
211  */
212 
213 METHODDEF(boolean)
215 {
216  my_src_ptr src = (my_src_ptr)cinfo->src;
217  size_t nbytes;
218 
219  nbytes = src->in->Read(src->buffer, INPUT_BUF_SIZE);
220 
221  if (nbytes <= 0)
222  {
223  if (src->start_of_file) /* Treat empty input file as fatal error */
224  {
225  THROW_EXCEPTION("Error looking for JPEG start data!");
226  }
227 
228  /* Insert a fake EOI marker */
229  src->buffer[0] = (JOCTET)0xFF;
230  src->buffer[1] = (JOCTET)JPEG_EOI;
231  nbytes = 2;
232  }
233 
234  src->pub.next_input_byte = src->buffer;
235  src->pub.bytes_in_buffer = nbytes;
236  src->start_of_file = FALSE;
237 
238  return TRUE;
239 }
240 
241 /*
242  * Skip data --- used to skip over a potentially large amount of
243  * uninteresting data (such as an APPn marker).
244  *
245  * Writers of suspendable-input applications must note that skip_input_data
246  * is not granted the right to give a suspension return. If the skip extends
247  * beyond the data currently in the buffer, the buffer can be marked empty so
248  * that the next read will cause a fill_input_buffer call that can suspend.
249  * Arranging for additional bytes to be discarded before reloading the input
250  * buffer is the application writer's problem.
251  */
252 
253 METHODDEF(void)
254 skip_input_data(j_decompress_ptr cinfo, long num_bytes)
255 {
256  my_src_ptr src = (my_src_ptr)cinfo->src;
257 
258  /* Just a dumb implementation for now. Could use fseek() except
259  * it doesn't work on pipes. Not clear that being smart is worth
260  * any trouble anyway --- large skips are infrequent.
261  */
262  if (num_bytes > 0)
263  {
264  while (num_bytes > (long)src->pub.bytes_in_buffer)
265  {
266  num_bytes -= (long)src->pub.bytes_in_buffer;
267  (void)fill_input_buffer(cinfo);
268  /* note we assume that fill_input_buffer will never return FALSE,
269  * so suspension need not be handled.
270  */
271  }
272  src->pub.next_input_byte += (size_t)num_bytes;
273  src->pub.bytes_in_buffer -= (size_t)num_bytes;
274  }
275 }
276 
277 /*
278  * An additional method that can be provided by data source modules is the
279  * resync_to_restart method for error recovery in the presence of RST markers.
280  * For the moment, this source module just uses the default resync method
281  * provided by the JPEG library. That method assumes that no backtracking
282  * is possible.
283  */
284 
285 /*
286  * Terminate source --- called by jpeg_finish_decompress
287  * after all data has been read. Often a no-op.
288  *
289  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
290  * application must deal with any cleanup that should happen even
291  * for error exit.
292  */
293 
294 METHODDEF(void)
296 {
297  MRPT_UNUSED_PARAM(cinfo);
298  /* no work necessary here */
299 }
300 
301 /*
302  * Prepare for input from a stdio stream.
303  * The caller must have already opened the stream, and is responsible
304  * for closing it after finishing decompression.
305  */
306 
307 GLOBAL(void)
309 {
310  my_src_ptr src;
311 
312  /* The source object and input buffer are made permanent so that a series
313  * of JPEG images can be read from the same file by calling jpeg_stdio_src
314  * only before the first one. (If we discarded the buffer at the end of
315  * one image, we'd likely lose the start of the next one.)
316  * This makes it unsafe to use this manager and a different source
317  * manager serially with the same JPEG object. Caveat programmer.
318  */
319  if (cinfo->src == nullptr)
320  { /* first time for this JPEG object? */
321  cinfo->src = (mrpt_jpeg_source_mgr*)(*cinfo->mem->alloc_small)(
322  (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr));
323  src = (my_src_ptr)cinfo->src;
324  src->buffer = (JOCTET*)(*cinfo->mem->alloc_small)(
326  INPUT_BUF_SIZE * sizeof(JOCTET));
327  }
328 
329  src = (my_src_ptr)cinfo->src;
330  src->pub.init_source = init_source;
331  src->pub.fill_input_buffer = fill_input_buffer;
332  src->pub.skip_input_data = skip_input_data;
333  src->pub.resync_to_restart =
334  jpeg_resync_to_restart; /* use default method */
335  src->pub.term_source = term_source;
336  src->in = in;
337  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
338  src->pub.next_input_byte = nullptr; /* until buffer loaded */
339 }
340 
341 // ---------------------------------------------------------------------------------------
342 // END OF JPEG FUNCTIONS PART
343 // ---------------------------------------------------------------------------------------
344 
345 /*---------------------------------------------------------------
346  saveToStreamAsJPEG
347  ---------------------------------------------------------------*/
348 void CImage::saveToStreamAsJPEG(CStream& out, const int jpeg_quality) const
349 {
350 #if MRPT_HAS_OPENCV
351  MRPT_START
352 
353  makeSureImageIsLoaded(); // For delayed loaded images stored externally
354 
355  struct jpeg_compress_struct cinfo;
356  struct jpeg_error_mgr jerr;
357 
358  const IplImage* ipl = static_cast<const IplImage*>(img);
359 
360  const unsigned int nCols = ipl->width;
361  const unsigned int nRows = ipl->height;
362  const bool is_color = (ipl->nChannels == 3);
363 
364  // Some previous verification:
365  ASSERT_(nCols >= 1 && nRows >= 1);
366  ASSERT_(ipl);
367  ASSERT_(ipl->nChannels == 1 || ipl->nChannels == 3);
368 
369  // 1) Initialization of the JPEG compresion object:
370  // --------------------------------------------------
371  cinfo.err = jpeg_std_error(&jerr);
372  jpeg_create_compress(&cinfo);
373 
374  // 2) Set the destination of jpeg data:
375  // --------------------------------------------------
376  jpeg_stdio_dest(&cinfo, &out);
377 
378  // 3) Set parameters for compression:
379  // --------------------------------------------------
380  cinfo.image_width = nCols;
381  cinfo.image_height = nRows;
382  cinfo.input_components = is_color ? 3 : 1;
383  cinfo.in_color_space = is_color ? JCS_RGB : JCS_GRAYSCALE;
384 
385  jpeg_set_defaults(&cinfo);
386  /* Make optional parameter settings here */
387  /* Now you can set any non-default parameters you wish to.
388  * Here we just illustrate the use of quality (quantization table) scaling:
389  */
390  jpeg_set_quality(
391  &cinfo, jpeg_quality /* quality per cent */,
392  TRUE /* limit to baseline-JPEG values */);
393 
394  // 4) Start:
395  // --------------------------------------------------
396  jpeg_start_compress(&cinfo, TRUE);
397 
398  // 5) Write scan lines:
399  // --------------------------------------------------
400  if (is_color)
401  {
402  JSAMPROW row_pointer[1]; /* pointer to a single row */
403  row_pointer[0] = (JSAMPROW) new char[ipl->widthStep];
404 
405  for (unsigned int row = 0; row < nRows; row++)
406  {
407  // Flip RGB bytes order!
408  char* src;
409  if (ipl->origin == 0)
410  src = &ipl->imageData[row * ipl->widthStep];
411  else
412  src = &ipl->imageData[(nRows - 1 - row) * ipl->widthStep];
413  char* target = (char*)row_pointer[0];
414  for (unsigned int col = 0; col < nCols; col++)
415  {
416  target[0] = src[2];
417  target[1] = src[1];
418  target[2] = src[0];
419 
420  target += 3;
421  src += 3;
422  }
423 
424  if (1 != jpeg_write_scanlines(&cinfo, row_pointer, 1))
425  {
426  THROW_EXCEPTION("jpeg_write_scanlines: didn't work!!");
427  }
428  }
429 
430  delete[] row_pointer[0];
431  } // end "color"
432  else
433  { // Is grayscale:
434  JSAMPROW row_pointer[1]; /* pointer to a single row */
435 
436  for (unsigned int row = 0; row < nRows; row++)
437  {
438  if (ipl->origin == 0)
439  row_pointer[0] =
440  (JSAMPROW)&ipl->imageData[row * ipl->widthStep];
441  else
442  row_pointer[0] =
443  (JSAMPROW)&ipl
444  ->imageData[(nRows - 1 - row) * ipl->widthStep];
445 
446  // Gray scale:
447  if (1 != jpeg_write_scanlines(&cinfo, row_pointer, 1))
448  {
449  THROW_EXCEPTION("jpeg_write_scanlines: didn't work!!");
450  }
451  }
452  }
453 
454  // 6) Compress and finish:
455  // --------------------------------------------------
456  jpeg_finish_compress(&cinfo);
457  jpeg_destroy_compress(&cinfo);
458 
459  // DONE!
460  MRPT_END
461 #endif
462 }
463 
464 /*---------------------------------------------------------------
465  saveToStreamAsJPEG
466  ---------------------------------------------------------------*/
468 {
469 #if MRPT_HAS_OPENCV
470  MRPT_START
471 
472  struct jpeg_decompress_struct cinfo;
473  struct jpeg_error_mgr jerr;
474 
475  /* Step 1: allocate and initialize JPEG decompression object */
476 
477  /* We set up the normal JPEG error routines, then override error_exit. */
478  cinfo.err = jpeg_std_error(&jerr);
479 
480  /* Now we can initialize the JPEG decompression object. */
481  jpeg_create_decompress(&cinfo);
482 
483  /* Step 2: specify data source (eg, a file) */
484  jpeg_stdio_src(&cinfo, &in);
485 
486  /* Step 3: read file parameters with jpeg_read_header() */
487  jpeg_read_header(&cinfo, TRUE);
488 
489  /* Step 4: set parameters for decompression */
490 
491  /* Step 5: Start decompressor */
492  jpeg_start_decompress(&cinfo);
493 
494  /* We may need to do some setup of our own at this point before reading
495  * the data. After jpeg_start_decompress() we have the correct scaled
496  * output image dimensions available, as well as the output colormap
497  * if we asked for color quantization.
498  * In this example, we need to make an output work buffer of the right size.
499  */
500  /* JSAMPLEs per row in output buffer */
501  /* physical row width in output buffer */
502  const int row_stride = cinfo.output_width * cinfo.output_components;
503  /* Make a one-row-high sample array that will go away when done with image
504  */
505  /* Output row buffer */
506  JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)(
507  (j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
508 
509  // Resize the CImage now:
510  this->changeSize(
511  cinfo.output_width, cinfo.output_height, cinfo.out_color_components,
512  true);
513  IplImage* ipl = static_cast<IplImage*>(img);
514 
515  /* Step 6: while (scan lines remain to be read) */
516  /* jpeg_read_scanlines(...); */
517 
518  /* Here we use the library's state variable cinfo.output_scanline as the
519  * loop counter, so that we don't have to keep track ourselves.
520  */
521  const unsigned int nCols = cinfo.output_width;
522  const unsigned int nRows = cinfo.output_height;
523 
524  for (unsigned int row = 0; row < nRows; row++)
525  {
526  /* jpeg_read_scanlines expects an array of pointers to scanlines.
527  * Here the array is only one element long, but you could ask for
528  * more than one scanline at a time if that's more convenient.
529  */
530  jpeg_read_scanlines(&cinfo, buffer, 1);
531 
532  /* Copy into the CImage object */
533  if (isColor())
534  {
535  // Flip RGB bytes order!
536  char* target = &ipl->imageData[row * ipl->widthStep];
537  const char* src = (char*)buffer[0];
538  for (unsigned int col = 0; col < nCols; col++)
539  {
540  target[0] = src[2];
541  target[1] = src[1];
542  target[2] = src[0];
543 
544  target += 3;
545  src += 3;
546  }
547  }
548  else
549  {
550  // Gray scale:
551  memcpy(
552  &ipl->imageData[row * ipl->widthStep], buffer[0], row_stride);
553  }
554  }
555 
556  /* Step 7: Finish decompression */
557 
558  jpeg_finish_decompress(&cinfo);
559  /* We can ignore the return value since suspension is not possible
560  * with the stdio data source.
561  */
562 
563  /* Step 8: Release JPEG decompression object */
564 
565  /* This is an important step since it will release a good deal of memory. */
566  jpeg_destroy_decompress(&cinfo);
567 
568  // DONE!
569  MRPT_END
570 #endif
571 }
my_source_mgr * my_src_ptr
init_destination(j_compress_ptr cinfo)
void changeSize(unsigned int width, unsigned int height, TImageChannels nChannels, bool originTopLeft)
Resize the buffers in "img" to accomodate a new image size and/or format.
Definition: CImage.cpp:223
#define MRPT_START
Definition: exceptions.h:262
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
GLuint buffer
Definition: glext.h:3917
#define JPOOL_PERMANENT
Definition: mrpt_jpeglib.h:749
virtual size_t Read(void *Buffer, size_t Count)=0
Introduces a pure virtual method responsible for reading from the stream.
struct jpeg_common_struct * j_common_ptr
Definition: mrpt_jpeglib.h:258
JDIMENSION image_height
Definition: mrpt_jpeglib.h:277
void makeSureImageIsLoaded() const
Checks if the image is of type "external storage", and if so and not loaded yet, load it...
Definition: CImage.cpp:1928
JSAMPLE FAR * JSAMPROW
Definition: mrpt_jpeglib.h:60
mrpt::io::CStream CStream
Definition: utils/CStream.h:7
GLuint src
Definition: glext.h:7278
init_source(j_decompress_ptr cinfo)
fill_input_buffer(j_decompress_ptr cinfo)
This base class is used to provide a unified interface to files,memory buffers,..Please see the deriv...
Definition: io/CStream.h:30
mrpt_jpeg_source_mgr pub
J_COLOR_SPACE in_color_space
Definition: mrpt_jpeglib.h:279
#define OUTPUT_BUF_SIZE
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:113
jpeg_stdio_src(j_decompress_ptr cinfo, CStream *in)
term_source(j_decompress_ptr cinfo)
struct jpeg_destination_mgr pub
#define JPOOL_IMAGE
Definition: mrpt_jpeglib.h:750
void saveToStreamAsJPEG(mrpt::io::CStream &out, const int jpeg_quality=95) const
Save image to binary stream as a JPEG (.jpg) compressed format.
JSAMPROW * JSAMPARRAY
Definition: mrpt_jpeglib.h:61
term_destination(j_compress_ptr cinfo)
#define JPEG_EOI
void loadFromStreamAsJPEG(mrpt::io::CStream &in)
Reads the image from a binary stream containing a binary jpeg file.
#define jpeg_create_decompress(cinfo)
Definition: mrpt_jpeglib.h:893
#define mrpt_jpeg_source_mgr
jpeg_stdio_dest(j_compress_ptr cinfo, CStream *out)
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
bool isColor() const
Returns true if the image is RGB, false if it is grayscale.
Definition: CImage.cpp:906
virtual size_t Write(const void *Buffer, size_t Count)=0
Introduces a pure virtual method responsible for writing to the stream.
#define FALSE
Definition: xmlParser.h:231
GLenum GLenum GLvoid * row
Definition: glext.h:3576
#define MRPT_END
Definition: exceptions.h:266
GLuint in
Definition: glext.h:7274
#define INPUT_BUF_SIZE
mrpt_destination_mgr * mrpt_dest_ptr
#define TRUE
Definition: xmlParser.h:234
#define jpeg_create_compress(cinfo)
Definition: mrpt_jpeglib.h:889
empty_output_buffer(j_compress_ptr cinfo)
void memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) noexcept
An OS and compiler independent version of "memcpy".
Definition: os.cpp:356
skip_input_data(j_decompress_ptr cinfo, long num_bytes)
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.
Definition: common.h:186
void * img
The internal IplImage pointer to the actual image content.
Definition: img/CImage.h:1090
JDIMENSION image_width
Definition: mrpt_jpeglib.h:276



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at lun oct 28 00:14:14 CET 2019