Compressing IplImage to JPEG using libjpeg in OpenCV

后端 未结 4 2053
长发绾君心
长发绾君心 2020-12-09 07:05

So I have this problem. I have an IplImage that i want to compress to JPEG and do something with it. I use libjpeg. I found a lot of answers \"read through examples and docs

相关标签:
4条回答
  • 2020-12-09 07:22

    I got in-memory compression to work. See the following

    #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite’able size */
    
    /* Expanded data destination object for memory output */
    
    typedef struct {
    struct jpeg_destination_mgr pub; /* public fields */
    
    unsigned char ** outbuffer; /* target buffer */
    unsigned long * outsize;
    unsigned char * newbuffer; /* newly allocated buffer */
    JOCTET * buffer; /* start of buffer */
    size_t bufsize;
    } my_mem_destination_mgr;
    
    typedef my_mem_destination_mgr * my_mem_dest_ptr;
    
    void
    init_mem_destination (j_compress_ptr cinfo)
    {
    /* no work necessary here */
    }
    
    boolean
    empty_mem_output_buffer (j_compress_ptr cinfo)
    {
    size_t nextsize;
    JOCTET * nextbuffer;
    my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest;
    
    /* Try to allocate new buffer with double size */
    nextsize = dest->bufsize * 2;
    nextbuffer = (JOCTET *)malloc(nextsize);
    
    if (nextbuffer == NULL)
    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
    
    memcpy(nextbuffer, dest->buffer, dest->bufsize);
    
    if (dest->newbuffer != NULL)
    free(dest->newbuffer);
    
    dest->newbuffer = nextbuffer;
    
    dest->pub.next_output_byte = nextbuffer + dest->bufsize;
    dest->pub.free_in_buffer = dest->bufsize;
    
    dest->buffer = nextbuffer;
    dest->bufsize = nextsize;
    
    return TRUE;
    }
    
    void
    term_mem_destination (j_compress_ptr cinfo)
    {
    my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest;
    
    *dest->outbuffer = dest->buffer;
    *dest->outsize = dest->bufsize – dest->pub.free_in_buffer;
    }
    
    void
    jpeg_mem_dest (j_compress_ptr cinfo,
    unsigned char ** outbuffer, unsigned long * outsize)
    {
    my_mem_dest_ptr dest;
    
    if (outbuffer == NULL || outsize == NULL) /* sanity check */
    ERREXIT(cinfo, JERR_BUFFER_SIZE);
    
    /* The destination object is made permanent so that multiple JPEG images
    * can be written to the same buffer without re-executing jpeg_mem_dest.
    */
    if (cinfo->dest == NULL) { /* first time for this JPEG object? */
    cinfo->dest = (struct jpeg_destination_mgr *)
    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
    sizeof(my_mem_destination_mgr));
    }
    
    dest = (my_mem_dest_ptr) cinfo->dest;
    dest->pub.init_destination = init_mem_destination;
    dest->pub.empty_output_buffer = empty_mem_output_buffer;
    dest->pub.term_destination = term_mem_destination;
    dest->outbuffer = outbuffer;
    dest->outsize = outsize;
    dest->newbuffer = NULL;
    
    if (*outbuffer == NULL || *outsize == 0) {
    /* Allocate initial buffer */
    dest->newbuffer = *outbuffer = (unsigned char*)malloc(OUTPUT_BUF_SIZE);
    if (dest->newbuffer == NULL)
    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
    *outsize = OUTPUT_BUF_SIZE;
    }
    
    dest->pub.next_output_byte = dest->buffer = *outbuffer;
    dest->pub.free_in_buffer = dest->bufsize = *outsize;
    }
    //*******************************************************************************************
    

    To use this do something like this in the main

    /************/
    
    unsigned long outlen;
    unsigned char *outbuffer;
    
    jpeg_mem_dest (&cinfo,&outbuffer,&outlen );
    printf(“outlen is %lu\n”,(long unsigned int)outlen);
    
    0 讨论(0)
  • 2020-12-09 07:25

    I got it to work using the following code for memory to memory compression.

    #include <stdio.h>
    #include "jpeg/jpeglib.h"
    
        /*
    This a custom destination manager for jpeglib that
    enables the use of memory to memory compression.
    
    See IJG documentation for details.
    */
    typedef struct {
    struct jpeg_destination_mgr pub; /* base class */
    JOCTET* buffer; /* buffer start address */
    int bufsize; /* size of buffer */
    size_t datasize; /* final size of compressed data */
    int* outsize; /* user pointer to datasize */
    int errcount; /* counts up write errors due to
    buffer overruns */
    } memory_destination_mgr;
    
    typedef memory_destination_mgr* mem_dest_ptr;
    
    /* ------------------------------------------------------------- */
    /* MEMORY DESTINATION INTERFACE METHODS */
    /* ------------------------------------------------------------- */
    
    
    /* This function is called by the library before any data gets written */
    METHODDEF(void)
    init_destination (j_compress_ptr cinfo)
    {
    mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
    
    dest->pub.next_output_byte = dest->buffer; /* set destination buffer */
    dest->pub.free_in_buffer = dest->bufsize; /* input buffer size */
    dest->datasize = 0; /* reset output size */
    dest->errcount = 0; /* reset error count */
    }
    
    /* This function is called by the library if the buffer fills up
    
    I just reset destination pointer and buffer size here.
    Note that this behavior, while preventing seg faults
    will lead to invalid output streams as data is over-
    written.
    */
    METHODDEF(boolean)
    empty_output_buffer (j_compress_ptr cinfo)
    {
    mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
    dest->pub.next_output_byte = dest->buffer;
    dest->pub.free_in_buffer = dest->bufsize;
    ++dest->errcount; /* need to increase error count */
    
    return TRUE;
    }
    
    /* Usually the library wants to flush output here.
    
    I will calculate output buffer size here.
    Note that results become incorrect, once
    empty_output_buffer was called.
    This situation is notified by errcount.
    */
    METHODDEF(void)
    term_destination (j_compress_ptr cinfo)
    {
    mem_dest_ptr dest = (mem_dest_ptr)cinfo->dest;
    dest->datasize = dest->bufsize - dest->pub.free_in_buffer;
    if (dest->outsize) *dest->outsize += (int)dest->datasize;
    }
    
    /* Override the default destination manager initialization
    provided by jpeglib. Since we want to use memory-to-memory
    compression, we need to use our own destination manager.
    */
    GLOBAL(void)
    jpeg_memory_dest (j_compress_ptr cinfo, JOCTET* buffer, int bufsize, int* outsize)
    {
    mem_dest_ptr dest;
    
    /* first call for this instance - need to setup */
    if (cinfo->dest == 0) {
    cinfo->dest = (struct jpeg_destination_mgr *)
    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
    sizeof (memory_destination_mgr));
    }
    
    dest = (mem_dest_ptr) cinfo->dest;
    dest->bufsize = bufsize;
    dest->buffer = buffer;
    dest->outsize = outsize;
    /* set method callbacks */
    dest->pub.init_destination = init_destination;
    dest->pub.empty_output_buffer = empty_output_buffer;
    dest->pub.term_destination = term_destination;
    }
    
    /* ------------------------------------------------------------- */
    /* MEMORY SOURCE INTERFACE METHODS */
    /* ------------------------------------------------------------- */
    
    /* Called before data is read */
    METHODDEF(void)
    init_source (j_decompress_ptr dinfo)
    {
    /* nothing to do here, really. I mean. I'm not lazy or something, but...
    we're actually through here. */
    }
    
    /* Called if the decoder wants some bytes that we cannot provide... */
    METHODDEF(boolean)
    fill_input_buffer (j_decompress_ptr dinfo)
    {
    /* we can't do anything about this. This might happen if the provided
    buffer is either invalid with regards to its content or just a to
    small bufsize has been given. */
    
    /* fail. */
    return FALSE;
    }
    
    /* From IJG docs: "it's not clear that being smart is worth much trouble"
    So I save myself some trouble by ignoring this bit.
    */
    METHODDEF(void)
    skip_input_data (j_decompress_ptr dinfo, INT32 num_bytes)
    {
    /* There might be more data to skip than available in buffer.
    This clearly is an error, so screw this mess. */
    if ((size_t)num_bytes > dinfo->src->bytes_in_buffer) {
    dinfo->src->next_input_byte = 0; /* no buffer byte */
    dinfo->src->bytes_in_buffer = 0; /* no input left */
    } else {
    dinfo->src->next_input_byte += num_bytes;
    dinfo->src->bytes_in_buffer -= num_bytes;
    }
    }
    
    /* Finished with decompression */
    METHODDEF(void)
    term_source (j_decompress_ptr dinfo)
    {
    /* Again. Absolute laziness. Nothing to do here. Boring. */
    }
    
    GLOBAL(void)
    jpeg_memory_src (j_decompress_ptr dinfo, unsigned char* buffer, size_t size)
    {
    struct jpeg_source_mgr* src;
    
    /* first call for this instance - need to setup */
    if (dinfo->src == 0) {
    dinfo->src = (struct jpeg_source_mgr *)
    (*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
    sizeof (struct jpeg_source_mgr));
    }
    
    src = dinfo->src;
    src->next_input_byte = buffer;
    src->bytes_in_buffer = size;
    src->init_source = init_source;
    src->fill_input_buffer = fill_input_buffer;
    src->skip_input_data = skip_input_data;
    src->term_source = term_source;
    /* IJG recommend to use their function - as I don't know ****
    about how to do better, I follow this recommendation */
    src->resync_to_restart = jpeg_resync_to_restart;
    }
    

    And in your main compression function replace the jpeg_stdio_dest with

    int numBytes = 0; //size of jpeg after compression
    char * storage = new char[150000]; //storage buffer
    JOCTET *jpgbuff = (JOCTET*)storage; //JOCTET pointer to buffer
    jpeg_memory_dest(&cinfo,jpgbuff,150000,&numBytes);
    

    The 150000 is a static size buffer, you probably will have images that will exceed it, so allocate accordingly.

    0 讨论(0)
  • 2020-12-09 07:30

    I've been able to found a solution using the latest jpeglib available on their website. New methods in : jpeg_mem_dest(&cinfo, outbuffer, outlen);

    bool ipl2jpeg(IplImage *frame, unsigned char **outbuffer, long unsigned int *outlen) {
    unsigned char *outdata = (uchar *) frame->imageData;
    struct jpeg_compress_struct cinfo = {0};
    struct jpeg_error_mgr jerr;
    JSAMPROW row_ptr[1];
    int row_stride;
    
    *outbuffer = NULL;
    *outlen = 0;
    
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);
    jpeg_mem_dest(&cinfo, outbuffer, outlen);
    
    cinfo.image_width = frame->width;
    cinfo.image_height = frame->height;
    cinfo.input_components = frame->nChannels;
    cinfo.in_color_space = JCS_RGB;
    
    jpeg_set_defaults(&cinfo);
    jpeg_start_compress(&cinfo, TRUE);
    row_stride = frame->width * frame->nChannels;
    
    while (cinfo.next_scanline < cinfo.image_height) {
        row_ptr[0] = &outdata[cinfo.next_scanline * row_stride];
        jpeg_write_scanlines(&cinfo, row_ptr, 1);
    }
    
    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
    
    return true;
    
    }
    
    0 讨论(0)
  • 2020-12-09 07:43

    After fighting with libJpeg for 2 days (pointers, memory stepping, and pulling hair) I gave up and used the all favourite save-to-disk-load-to-memory approach, so if anybody is interested here is the method:

    char* convert2jpeg(IplImage* frame, int* frame_size) {
    
    FILE* infile = NULL;
    struct stat fileinfo_buf;
    
    if (cvSaveImage(name_buf, frame) < 0) {
        printf("\nCan't save image %s", name_buf);
        return NULL;
    }
    
    if (stat(name_buf, &fileinfo_buf) < 0) {
        printf("\nPLAYER [convert2jpeg] stat");
        return NULL;
    }
    
    *frame_size = fileinfo_buf.st_size;
    char* buffer = (char *) malloc(fileinfo_buf.st_size + 1);
    
    if ((infile = fopen(name_buf, "rb")) == NULL) {
        printf("\nPLAYER [convert2jpeg] fopen %s", name_buf);
        free(buffer);
        return NULL;
    }
    
    fread(buffer, fileinfo_buf.st_size, 1, infile);
    fclose(infile);
    
    return buffer;
    }
    

    I hope somebody finds this usefull. I wish somebody from OpenCV developers sees this thread and implements direct to buffer JPEG conversion in OpenCV and spares us the misery and 1 save/load to disk operation.

    0 讨论(0)
提交回复
热议问题