20

The two functions in openCV cvLoadImage and cvSaveImage accept file path's as arguments.

For example, when saving a image it's cvSaveImage("/tmp/output.jpg", dstIpl) and it writes on the disk.

Is there any way to feed this a buffer already in memory? So instead of a disk write, the output image will be in memory.

I would also like to know this for both cvSaveImage and cvLoadImage (read and write to memory buffers). Thanks!


My goal is to store the Encoded (jpeg) version of the file in Memory. Same goes to cvLoadImage, I want to load a jpeg that's in memory in to the IplImage format.

The Unknown
  • 19,224
  • 29
  • 77
  • 93
  • 1
    You want to write the image to memory? But dstIpl already is in memory, what exactly are you hoping to accomplish? You can access the image data with dstIpl->imageData or ->data or something. – mpen May 07 '09 at 00:53
  • Likewise, you could manipulate the iplImage's data buffer to load an image that's already in memory... just has to be in BGR format. – mpen May 07 '09 at 00:55
  • I think the original poster wants to encode the image but save a disk read/write. – M456 May 07 '09 at 01:05
  • 1
    This kind of thing is especially useful when you want to do things like embed a compressed image into a larger binary. For example, PDF files can have embedded PNG and JPG images. A program that directly creates PDF files would want to avoid having to create temporary PNG or JPG files on disk, then transfer them into the PDF stream. – Mr Fooz May 07 '09 at 01:20
  • 1
    To clear the confusion: Yes, I want to store the encoded (ex. jpeg) version of the file in Memory instead of writing to disk. – The Unknown May 07 '09 at 01:39

6 Answers6

19

This worked for me

// decode jpg (or other image from a pointer)
// imageBuf contains the jpg image
    cv::Mat imgbuf = cv::Mat(480, 640, CV_8U, imageBuf);
    cv::Mat imgMat = cv::imdecode(imgbuf, CV_LOAD_IMAGE_COLOR);
// imgMat is the decoded image

// encode image into jpg
    cv::vector<uchar> buf;
    cv::imencode(".jpg", imgMat, buf, std::vector<int>() );
// encoded image is now in buf (a vector)
    imageBuf = (unsigned char *) realloc(imageBuf, buf.size());
    memcpy(imageBuf, &buf[0], buf.size());
//  size of imageBuf is buf.size();

I was asked about a C version instead of C++:

#include <opencv/cv.h>
#include <opencv/highgui.h>

int
main(int argc, char **argv)
{
    char *cvwin = "camimg";

    cvNamedWindow(cvwin, CV_WINDOW_AUTOSIZE);

    // setup code, initialization, etc ...
    [ ... ]

    while (1) {      
        // getImage was my routine for getting a jpeg from a camera
        char *img = getImage(fp);
        CvMat mat;

   // substitute 640/480 with your image width, height 
        cvInitMatHeader(&mat, 640, 480, CV_8UC3, img, 0);
        IplImage *cvImg = cvDecodeImage(&mat, CV_LOAD_IMAGE_COLOR);
        cvShowImage(cvwin, cvImg);
        cvReleaseImage(&cvImg);
        if (27 == cvWaitKey(1))         // exit when user hits 'ESC' key
        break;
    }

    cvDestroyWindow(cvwin);
}
codeDr
  • 1,535
  • 17
  • 20
  • is it not the intention that imdecode should decode the image, so why do imageBuf need to be placed in a cv::mat ? I am having problems where i have a unsigned char * containing raw data i loaded over the wire (its a tiff image), but after doign imdecode on the raw data the Mat is having its mat.data == 0 and no errors given. – Poul K. Sørensen Feb 08 '14 at 22:01
  • @pksorensen imageBuf needs to be put into a Mat so that it can be accepted as a parameter to imdecode() unless there's C++ magic that can turn it into InputArray that imdecode() wants. Reasons for not decoding your TIFF image would be (1) the Mat passed in to imgdecode rows*cols was less than 1, (2) TIFF support was not compiled into your OpenCV library or (3) that the TIFF decoder did not recognize your image buffer as a valid TIFF image. – codeDr Feb 06 '15 at 13:51
15

There are a couple of undocumented functions in the SVN version of the libary:

CV_IMPL CvMat* cvEncodeImage( const char* ext, 
                              const CvArr* arr, const int* _params )

CV_IMPL IplImage* cvDecodeImage( const CvMat* _buf, int iscolor )

Latest check in message states that they are for native encoding/decoding for bmp, png, ppm and tiff (encoding only).

Alternatively you could use a standard image encoding library (e.g. libjpeg) and manipulate the data in the IplImage to match the input structure of the encoding library.

M456
  • 5,547
  • 2
  • 19
  • 14
  • 10
    PLEASE don't rely on undocumented functions, you'll create a maintenance nightmare for yourself. The reason they are not documented is because the designers **don't expect them to remain stable over time**. They could disappear with the next version! – j_random_hacker May 09 '09 at 20:34
  • 3
    I think that warning is implied with any development version. Also, documentation has always been an issue with OpenCV many of the stable functions are still undocumented. – M456 May 12 '09 at 00:17
1

I'm assuming you're working in linux. From libjpeg.doc:

The rough outline of a JPEG compression operation is:
Allocate and initialize a JPEG compression object
Specify the destination for the compressed data (eg, a file)
Set parameters for compression, including image size & colorspace

jpeg_start_compress(...);
while (scan lines remain to be written)
jpeg_write_scanlines(...);

jpeg_finish_compress(...);
Release the JPEG compression object

The real trick for doing what you want to do is providing a custom "data destination (or source) manager" which is defined in jpeglib.h:

struct jpeg_destination_mgr {
  JOCTET * next_output_byte;    /* => next byte to write in buffer */
  size_t free_in_buffer;        /* # of byte spaces remaining in buffer */

  JMETHOD(void, init_destination, (j_compress_ptr cinfo));
  JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo));
  JMETHOD(void, term_destination, (j_compress_ptr cinfo));
};

Basically set that up so your source and/or destination are the memory buffers you want, and you should be good to go.

As an aside, this post could be a lot better but the libjpeg62 documentation is, quite frankly, superb. Just apt-get libjpeg62-dev and read libjpeg.doc and look at example.c. If you run into problems and can't get something to work, just post again and I'm sure someone will be able to help.

jdt141
  • 4,993
  • 6
  • 35
  • 36
0

Here's an example in Delphi. It converts a 24bit bitmap for use with OpenCV

function BmpToPIplImageEx(Bmp: TBitmap): pIplImage;
Var
  i: Integer;
  offset: LongInt;
  dataByte: PByteArray;  
Begin
  Assert(Bmp.PixelFormat = pf24bit, 'PixelFormat must be 24bit');
  Result := cvCreateImageHeader(cvSize(Bmp.Width, Bmp.Height), IPL_DEPTH_8U, 3);
  cvCreateData(Result);
  for i := 0 to Bmp.height - 1 do
  Begin        
    offset   := longint(Result.imageData) + Result.WidthStep * i;
    dataByte := PByteArray(offset);    
    CopyMemory(dataByte, Bmp.Scanline[i], Result.WidthStep);
  End;
End;
Meh Nada
  • 219
  • 2
  • 6
0

All you need to load files from the memory buffer is a different src manager (libjpeg). I have tested the following code in Ubuntu 8.10.

/******************************** First define mem buffer function bodies **************/
<pre>
/*
 * memsrc.c
 *
 * Copyright (C) 1994-1996, Thomas G. Lane.
 * This file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README file.
 *
 * This file contains decompression data source routines for the case of
 * reading JPEG data from a memory buffer that is preloaded with the entire
 * JPEG file.  This would not seem especially useful at first sight, but
 * a number of people have asked for it.
 * This is really just a stripped-down version of jdatasrc.c.  Comparison
 * of this code with jdatasrc.c may be helpful in seeing how to make
 * custom source managers for other purposes.
 */

/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
//include "jinclude.h"
include "jpeglib.h"
include "jerror.h"


/* Expanded data source object for memory input */

typedef struct {
  struct jpeg_source_mgr pub;   /* public fields */

  JOCTET eoi_buffer[2];     /* a place to put a dummy EOI */
} my_source_mgr;

typedef my_source_mgr * my_src_ptr;


/*
 * Initialize source --- called by jpeg_read_header
 * before any data is actually read.
 */

METHODDEF(void)
init_source (j_decompress_ptr cinfo)
{
  /* No work, since jpeg_memory_src set up the buffer pointer and count.
   * Indeed, if we want to read multiple JPEG images from one buffer,
   * this *must* not do anything to the pointer.
   */
}


/*
 * Fill the input buffer --- called whenever buffer is emptied.
 *
 * In this application, this routine should never be called; if it is called,
 * the decompressor has overrun the end of the input buffer, implying we
 * supplied an incomplete or corrupt JPEG datastream.  A simple error exit
 * might be the most appropriate response.
 *
 * But what we choose to do in this code is to supply dummy EOI markers
 * in order to force the decompressor to finish processing and supply
 * some sort of output image, no matter how corrupted.
 */

METHODDEF(boolean)
fill_input_buffer (j_decompress_ptr cinfo)
{
  my_src_ptr src = (my_src_ptr) cinfo->src;

  WARNMS(cinfo, JWRN_JPEG_EOF);

  /* Create a fake EOI marker */
  src->eoi_buffer[0] = (JOCTET) 0xFF;
  src->eoi_buffer[1] = (JOCTET) JPEG_EOI;
  src->pub.next_input_byte = src->eoi_buffer;
  src->pub.bytes_in_buffer = 2;

  return TRUE;
}


/*
 * Skip data --- used to skip over a potentially large amount of
 * uninteresting data (such as an APPn marker).
 *
 * If we overrun the end of the buffer, we let fill_input_buffer deal with
 * it.  An extremely large skip could cause some time-wasting here, but
 * it really isn't supposed to happen ... and the decompressor will never
 * skip more than 64K anyway.
 */

METHODDEF(void)
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
  my_src_ptr src = (my_src_ptr) cinfo->src;

  if (num_bytes > 0) {
    while (num_bytes > (long) src->pub.bytes_in_buffer) {
      num_bytes -= (long) src->pub.bytes_in_buffer;
      (void) fill_input_buffer(cinfo);
      /* note we assume that fill_input_buffer will never return FALSE,
       * so suspension need not be handled.
       */
    }
    src->pub.next_input_byte += (size_t) num_bytes;
    src->pub.bytes_in_buffer -= (size_t) num_bytes;
  }
}


/*
 * An additional method that can be provided by data source modules is the
 * resync_to_restart method for error recovery in the presence of RST markers.
 * For the moment, this source module just uses the default resync method
 * provided by the JPEG library.  That method assumes that no backtracking
 * is possible.
 */


/*
 * Terminate source --- called by jpeg_finish_decompress
 * after all data has been read.  Often a no-op.
 *
 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
 * application must deal with any cleanup that should happen even
 * for error exit.
 */

METHODDEF(void)
term_source (j_decompress_ptr cinfo)
{
  /* no work necessary here */
}


/*
 * Prepare for input from a memory buffer.
 */

GLOBAL(void)
jpeg_memory_src (j_decompress_ptr cinfo, const JOCTET * buffer, size_t bufsize)
{
  my_src_ptr src;

  /* The source object is made permanent so that a series of JPEG images
   * can be read from a single buffer by calling jpeg_memory_src
   * only before the first one.
   * This makes it unsafe to use this manager and a different source
   * manager serially with the same JPEG object.  Caveat programmer.
   */
  if (cinfo->src == NULL) { /* first time for this JPEG object? */
    cinfo->src = (struct jpeg_source_mgr *)
      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
                  SIZEOF(my_source_mgr));
  }

  src = (my_src_ptr) cinfo->src;
  src->pub.init_source = init_source;
  src->pub.fill_input_buffer = fill_input_buffer;
  src->pub.skip_input_data = skip_input_data;
  src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
  src->pub.term_source = term_source;

  src->pub.next_input_byte = buffer;
  src->pub.bytes_in_buffer = bufsize;
}

Then the usage is pretty simple. You may need to replace SIZEOF() with sizeof(). Find a standard decompression example. Just replace "jpeg_stdio_src" with "jpeg_memory_src". Hope that helps!

enthusiasticgeek
  • 2,640
  • 46
  • 53
0

This is an indirect answer...

In the past, I've directly used libpng and libjpeg directly to do this. They have a low-level enough API that you can use memory buffers instead of file buffers for reading and writing.

Mr Fooz
  • 109,094
  • 6
  • 73
  • 101
  • If you downvote, please leave a comment explaining why this answer is wrong. That way it can be improved. – Mr Fooz Dec 09 '13 at 19:56