How to replace file_stream by byte_stream?

1,246 views
Skip to first unread message

Alexey K.

unread,
Dec 22, 2014, 1:05:35 PM12/22/14
to open...@googlegroups.com
Could somebody help me in replacing file_stream by reading/writing from/to memory?
I took the code from opj_decompress.c:

    opj_stream_t *l_stream = NULL;                /* Stream */
    opj_codec_t* l_codec = NULL;                  /* Handle to a decompressor */

    l_stream = opj_stream_create_default_
file_stream(parameters.infile,1);
    l_codec = opj_create_decompress(OPJ_CODEC_J2K);

    /* Setup the decoder decoding parameters using user parameters */
    if ( !opj_setup_decoder(l_codec, &parameters) ){
        .................
        return EXIT_FAILURE;
    }

    /* Read the main header of the codestream and if necessary the JP2 boxes*/
    if(! opj_read_header(l_stream, l_codec, &image)){
        .................
        return EXIT_FAILURE;
    }

The opj_stream structure is not mentioned in http://www.openjpeg.org/libdoc/annotated.html

Dan Bloomberg

unread,
Dec 22, 2014, 3:36:46 PM12/22/14
to open...@googlegroups.com
Alexy,

leptonica took the approach to use the gnu runtime functions fmemopen() and open_memstream() to convert a file stream based function into a memory based one.  These functions are not supported on platforms such as windows; the fallback for such platforms is to write to a temporary file and read back into memory.

To do this, we had to restore the stream read/write from version 2.0, because it has been removed in 2.1 (there's a big openjpeg thread on this decision).

You'll find the details in leptonica-1.71, which can be downloaded from leptonica.org.

  -- Dan

--
You are subscribed to the mailing-list of the OpenJPEG project (www.openjpeg.org)
To post: email to open...@googlegroups.com
To unsubscribe: email to openjpeg+u...@googlegroups.com
For more options: visit http://groups.google.com/group/openjpeg
OpenJPEG is mainly supported by :
* UCL Image and Signal Processing Group (http://sites.uclouvain.be/ispgroup)
* IntoPIX (www.intopix.com)

szuk...@arcor.de

unread,
Dec 22, 2014, 7:08:05 PM12/22/14
to open...@googlegroups.com
On Mon, 22 Dec 2014 10:05:35 -0800 (PST), Alexey K. wrote:

>Could somebody help me in replacing file_stream by reading/writing
>from/to memory?

And on Mon, 22 Dec 2014 12:36:14 -0800, Dan Bloomberg wrote:

>leptonica took the approach to use the gnu runtime functions
>fmemopen() and open_memstream() to convert a file stream based
>function into a memory based one.

The answer of Dan Bloomberg shows that the question is a little
bit confusing.

Do you want - using OpenJPEG - to DECODE a JPEG2000 image from
BUFFER instead of from FILE?

winfried

Alexey K.

unread,
Dec 23, 2014, 4:50:44 AM12/23/14
to open...@googlegroups.com

> Do you want - using OpenJPEG - to DECODE a JPEG2000 image fromBUFFER instead of from FILE?

Yes, I want to ENCODE/DECODE a JPEG2000 image from BUFFER instead of from FILE, using OpenJPEG
(in order to read/write images, encapsulated in DICOM structure).

I have found an example, dated 2010, but it seems not up-to-date:

On Mon, 27 Sep 2010 21:44:52 +0200, florian...@fh-bielefeld.de, wrote:

  reader = fopen("PATH/TO/example.j2k", "rb");
  fseek(reader, 0, SEEK_END);
  buf_len = ftell(reader);
  fseek(reader, 0, SEEK_SET);
  buf = (unsigned char*) malloc(buf_len);
  fread(buf, 1, buf_len, reader);
  fclose(reader);

  opj_set_default_decoder_parameters(&parameters);

  dinfo = opj_create_decompress(FORMAT);
 
  opj_setup_decoder(dinfo, &parameters);

  cio = opj_cio_open((opj_common_ptr)dinfo, buf, buf_len);

  image = opj_decode(dinfo, cio);







 

Mario Emmenlauer

unread,
Dec 23, 2014, 5:54:39 AM12/23/14
to open...@googlegroups.com

Hi all,

I would also be interested in decoding a JP2 image from memory, so
two thumbs up for this thread! I looked at fmemopen on Linux, but it
was not directly obvious how to get it to work with openjpeg.

Cheers,

Mario




On 23.12.2014 10:50, Alexey K. wrote:
>
>> Do you want - using OpenJPEG - to DECODE a JPEG2000 image fromBUFFER instead of from FILE?
>
> Yes, I want to ENCODE/DECODE a JPEG2000 image from BUFFER instead of from FILE, using OpenJPEG
> (in order to read/write images, encapsulated in DICOM structure).
>
> I have found an example <https://groups.google.com/d/msg/openjpeg/_emwinx2FEs/A8y04HHvw-UJ>, dated 2010, but it seems not up-to-date:
>
> On Mon, 27 Sep 2010 21:44:52 +0200, florian...@fh-bielefeld.de, wrote:
>
> reader = fopen("PATH/TO/example.j2k", "rb");
> fseek(reader, 0, SEEK_END);
> buf_len = ftell(reader);
> fseek(reader, 0, SEEK_SET);
> buf = (unsigned char*) malloc(buf_len);
> fread(buf, 1, buf_len, reader);
> fclose(reader);
>
> opj_set_default_decoder_parameters(&parameters);
>
> dinfo = opj_create_decompress(FORMAT);
>
> opj_setup_decoder(dinfo, &parameters);
>
> cio = opj_cio_open((opj_common_ptr)dinfo, buf, buf_len);
>
> image = opj_decode(dinfo, cio);
>
>
>
>
>
>
>
>
>
> --
> You are subscribed to the mailing-list of the OpenJPEG project (www.openjpeg.org)
> To post: email to open...@googlegroups.com
> To unsubscribe: email to openjpeg+u...@googlegroups.com
> For more options: visit http://groups.google.com/group/openjpeg
> OpenJPEG is mainly supported by :
> * UCL Image and Signal Processing Group (http://sites.uclouvain.be/ispgroup)
> * IntoPIX (www.intopix.com)

--
A: Yes.
> Q: Are you sure?
>> A: Because it reverses the logical flow of conversation.
>>> Q: Why is top posting annoying in email?

Mario Emmenlauer BioDataAnalysis Mobil: +49-(0)151-68108489
Balanstrasse 43 mailto: mario.emmenlauer * unibas.ch
D-81669 München http://www.biodataanalysis.de/

m.da...@gmail.com

unread,
Dec 23, 2014, 6:11:10 AM12/23/14
to open...@googlegroups.com
Hi Alexey,

Here's how an input stream could look like. I did not add all necessary checks & type checking (assuming 64 bits here) :
First, a few definitions :

typedef struct

{

OPJ_UINT8* pData;

OPJ_SIZE_T dataSize;

OPJ_SIZE_T offset;

}opj_input_memory_stream;


static OPJ_SIZE_T opj_input_memory_stream_read(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data)

{

opj_input_memory_stream* l_stream = (opj_input_memory_stream*)p_user_data;

OPJ_SIZE_T l_nb_bytes_read = p_nb_bytes;

if (l_stream->offset >= l_stream->dataSize) {

return (OPJ_SIZE_T)-1;

}

if (p_nb_bytes > (l_stream->dataSize - l_stream->offset)) {

l_nb_bytes_read = l_stream->dataSize - l_stream->offset;

}

memcpy(p_buffer, &(l_stream->pData[l_stream->offset]), l_nb_bytes_read);

l_stream->offset += l_nb_bytes_read;

return l_nb_bytes_read;

}

static OPJ_OFF_T opj_input_memory_stream_skip(OPJ_OFF_T p_nb_bytes, void * p_user_data)

{

opj_input_memory_stream* l_stream = (opj_input_memory_stream*)p_user_data;


if (p_nb_bytes < 0) {

return -1;

}

l_stream->offset += (OPJ_SIZE_T)p_nb_bytes;

return p_nb_bytes;

}

static OPJ_BOOL opj_input_memory_stream_seek(OPJ_OFF_T p_nb_bytes, void * p_user_data)

{

opj_input_memory_stream* l_stream = (opj_input_memory_stream*)p_user_data;

if (p_nb_bytes < 0) {

return OPJ_FALSE;

}

l_stream->offset = (OPJ_SIZE_T)p_nb_bytes;

return OPJ_TRUE;

}

static void opj_input_memory_stream_free (void * p_user_data)

{

opj_input_memory_stream* l_stream = (opj_input_memory_stream*)p_user_data;

if ((l_stream != NULL) && (l_stream->pData != NULL)) {

free(l_stream->pData);

l_stream->pData = NULL;

}

}


I modified opj_decompress with this. Now to use it :

/* read the input file and put it in memory */

/* ---------------------------------------- */

{

FILE* input = fopen(parameters.infile, "rb");

fseeko(input, 0, SEEK_END);

l_mem_stream.dataSize = ftello(input);

fseeko(input, 0, SEEK_SET);

l_mem_stream.offset = 0U;

l_mem_stream.pData = (OPJ_UINT8*)malloc(l_mem_stream.dataSize);

fread(l_mem_stream.pData, 1U, l_mem_stream.dataSize, input);

fclose(input);

}

l_stream = opj_stream_default_create(1);

if (!l_stream){

fprintf(stderr, "ERROR -> failed to create the stream from the file %s\n", parameters.infile);

destroy_parameters(&parameters);

return EXIT_FAILURE;

}

opj_stream_set_read_function(l_stream, opj_input_memory_stream_read);

opj_stream_set_seek_function(l_stream, opj_input_memory_stream_seek);

opj_stream_set_skip_function(l_stream, opj_input_memory_stream_skip);

opj_stream_set_user_data(l_stream, &l_mem_stream, opj_input_memory_stream_free);

opj_stream_set_user_data_length(l_stream, l_mem_stream.dataSize);


/*l_stream = opj_stream_create_default_file_stream(parameters.infile,1);

if (!l_stream){

fprintf(stderr, "ERROR -> failed to create the stream from the file %s\n", parameters.infile);

destroy_parameters(&parameters);

return EXIT_FAILURE;

}*/


The same as to be done for output streams but this gives you, I hope, an idea of what has to be done. 

Alexey K.

unread,
Dec 23, 2014, 9:19:08 AM12/23/14
to open...@googlegroups.com


 23.12.2014 , 15:11:10 UTC+4 user m.da...@gmail.com  kindly wrote:
Hi Alexey,

Here's how an input stream could look like. I did not add all necessary checks & type checking (assuming 64 bits here) :
.......................................

The same as to be done for output streams but this gives you, I hope, an idea of what has to be done. 


Many thanks,  now it's clear.

szuk...@arcor.de

unread,
Dec 23, 2014, 9:32:48 AM12/23/14
to open...@googlegroups.com
On Tue, 23 Dec 2014 01:50:44 -0800 (PST), Alexey K. wrote:

>Yes, I want to ENCODE/DECODE a JPEG2000 image from BUFFER
>instead of from FILE, using OpenJPEG

Ok. The attached file contains code that I use a long time
on LINUX/Win7 64-Bit and Debian PPC 32-Bit.

The 'JP2_decode()' is a sketch.

winfried
byte_stream.c.gz

Matthew Woehlke

unread,
Dec 23, 2014, 2:28:53 PM12/23/14
to open...@googlegroups.com
On 2014-12-23 05:54, Mario Emmenlauer wrote:
> I would also be interested in decoding a JP2 image from memory, so
> two thumbs up for this thread! I looked at fmemopen on Linux, but it
> was not directly obvious how to get it to work with openjpeg.

Yes, please. Being able to decode from (*and encode to*) memory is
generally interesting. It's not at all unusual to have resource files
that internally contain compressed imagery; being able to decode these
directly without using a temporary file is extremely desirable. It's not
even inconceivable that someone may want to encode and decode J2K in a
manner that never actually touches a disk (not counting swap).

IMHO good image codecs should work first on memory, and may *optionally*
provide file I/O as a convenience.

--
Matthew

Mathieu Malaterre

unread,
Dec 24, 2014, 3:13:36 AM12/24/14
to openjpeg
On Tue, Dec 23, 2014 at 8:28 PM, Matthew Woehlke
<mw_t...@users.sourceforge.net> wrote:
[...]
> IMHO good image codecs should work first on memory, and may *optionally*
> provide file I/O as a convenience.

-sigh- while this comment may apply to some other codec, it does not
apply to JPEG 2000. JPEG 2000 is not so much about compression but
rather about multi-scale and/or multi-resolution properties.
Another way to say it, if you have the time to copy the full JP2 from
disk/network into main memory in your app, then you shouldn't be using
JP2 at all.

Matthew Woehlke

unread,
Dec 24, 2014, 12:10:14 PM12/24/14
to open...@googlegroups.com
On 2014-12-24 03:13, Mathieu Malaterre wrote:
> On Tue, Dec 23, 2014 at 8:28 PM, Matthew Woehlke wrote:
> [...]
>> IMHO good image codecs should work first on memory, and may *optionally*
>> provide file I/O as a convenience.
>
> -sigh- while this comment may apply to some other codec, it does not
> apply to JPEG 2000. JPEG 2000 is not so much about compression but
> rather about multi-scale and/or multi-resolution properties.
> Another way to say it, if you have the time to copy the full JP2 from
> disk/network into main memory in your app, then you shouldn't be using
> JP2 at all.

Well, can you decode an image that is part of a file containing many
images? What about such a file that has been mmap'd?

Maybe J2K has reasonable justification for being a file API first, but
are those reasons good enough why it *shouldn't* work on memory? (And
obviously I am not the only one that thinks it should...)

--
Matthew

Bob Friesenhahn

unread,
Dec 24, 2014, 12:21:24 PM12/24/14
to open...@googlegroups.com
On Wed, 24 Dec 2014, Matthew Woehlke wrote:
>
> Maybe J2K has reasonable justification for being a file API first, but
> are those reasons good enough why it *shouldn't* work on memory? (And
> obviously I am not the only one that thinks it should...)

Most of the image format libraries used by ImageMagick and
GraphicsMagick do provide a way where the user can interject its own
file-like I/O routines which can then use a file, memory mapped file,
pipe, memory, or whatever they like, as long as they provide the
necessary service according to the interface definition. These
interfaces are used whenever they are available since there is a
desire to be as flexible as possible and avoid creating unnecessary
temporary files.

Bob
--
Bob Friesenhahn
bfri...@simple.dallas.tx.us, http://www.simplesystems.org/users/bfriesen/
GraphicsMagick Maintainer, http://www.GraphicsMagick.org/

m.da...@gmail.com

unread,
Dec 26, 2014, 9:34:52 AM12/26/14
to open...@googlegroups.com
Hi,

> Maybe J2K has reasonable justification for being a file API first, but 
> are those reasons good enough why it *shouldn't* work on memory? (And 
> obviously I am not the only one that thinks it should...) 

Well, what Bob is saying about GraphicsMagick & the libraries used by it applies to OpenJPEG.
The "file" API provided by OpenJPEG is a convenience API implementing those file-like I/O routines but can work with memory / pipe / ... with the proper implementation without using temporary files as demonstrated in the sample I provided a few days ago.

That being said, I think a convenience API shall also be provided for memory only operation. This wouldn't be hard to do & I guess I can provide a patch for that early January when I'll be back to work.

Regards,
Matthieu

   

Mario Emmenlauer

unread,
Dec 30, 2014, 4:04:08 AM12/30/14
to open...@googlegroups.com

Hi Matthieu,
This would be highly appreciated, two thumbs up!

Thanks already and all the best,

Mario



--
Mario Emmenlauer BioDataAnalysis Mobile: +49-(0)151-68108489
Balanstrasse 43 mailto: mario.emmenlauer * unibas.ch
D-81669 Munich http://www.biodataanalysis.de/

Dan Bloomberg

unread,
Dec 30, 2014, 12:04:15 PM12/30/14
to open...@googlegroups.com
Matthieu, I second Mario's appreciation for a memory-based interface.

For clients of imaging libraries like leptonica that link libraries like openjpeg for I/O to various compressed formats, it is most useful to the clients to have both file stream and memory interfaces available for reading and writing.

If the compression library provides either stream or memory I/O, the other can be implemented.  But for optimal portability, the memory API is the preferred one, because the stream interface is then trivial to implement. 

A good example of a memory-based I/O API is webp, which provides this clean interface:

    reading:   WebPDecodeRGBAInto(encoded_data, encoded_size, raster_data, raster_size, stride); 
                              (raster_data is pre-allocated, of size raster_size and with stride bytes/raster-line)

    writing:  encoded_size = WebPEncodeRGBA(raster_data, w, h, stride, quality, &encoded_data);


  -- Dan

Mario Emmenlauer

unread,
Apr 18, 2015, 3:49:09 PM4/18/15
to open...@googlegroups.com

Hi Guys,

I was just curious if there has been ever any progress on this issue?

Cheers,

Mario



On 30.12.2014 10:04, Mario Emmenlauer wrote:
>
> Hi Matthieu,
>
> On 26.12.2014 15:34, m.da...@gmail.com wrote:
>>> Maybe J2K has reasonable justification for being a file API first, but
>>> are those reasons good enough why it *shouldn't* work on memory? (And
>>> obviously I am not the only one that thinks it should...)
>>
>> Well, what Bob is saying about GraphicsMagick & the libraries used by it applies
>> to OpenJPEG.
>> The "file" API provided by OpenJPEG is a convenience API implementing those
>> file-like I/O routines but can work with memory / pipe / ... with the proper
>> implementation without using temporary files as demonstrated in the sample I
>> provided a few days ago.
>>
>> That being said, I think a convenience API shall also be provided for memory
>> only operation. This wouldn't be hard to do & I guess I can provide a patch for
>> that early January when I'll be back to work.
>
> This would be highly appreciated, two thumbs up!
>
> Thanks already and all the best,
>
> Mario
>
>
>

--
Mario Emmenlauer BioDataAnalysis Mobil: +49-(0)151-68108489
Balanstrasse 43 mailto: mario.emmenlauer * unibas.ch
D-81669 München http://www.biodataanalysis.de/

Leonardo M. Ramé

unread,
Apr 30, 2015, 10:39:13 AM4/30/15
to open...@googlegroups.com
Great for decoding, but do you have an implementation of opj_stream_set_write_function?. I need it to Encode a memory stream.

Leonardo.

Bruce Barton

unread,
Jan 31, 2016, 11:29:42 AM1/31/16
to OpenJPEG
I know this is an old question, but I found it and m.da... code helped me come up with these for OpenJpg2.1:

// These routines are added to use memory instead of a file for input and output.

//Structure need to treat memory as a stream.

typedef struct

{

OPJ_UINT8* pData; //Our data.

OPJ_SIZE_T dataSize; //How big is our data.

OPJ_SIZE_T offset; //Where are we currently in our data.

}opj_memory_stream;

//This will read from our memory to the buffer.

static OPJ_SIZE_T opj_memory_stream_read(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data)

{

opj_memory_stream* l_memory_stream = (opj_memory_stream*)p_user_data;//Our data.

OPJ_SIZE_T l_nb_bytes_read = p_nb_bytes;//Amount to move to buffer.

//Check if the current offset is outside our data buffer.

if (l_memory_stream->offset >= l_memory_stream->dataSize) return (OPJ_SIZE_T)-1;

//Check if we are reading more than we have.

if (p_nb_bytes > (l_memory_stream->dataSize - l_memory_stream->offset))

l_nb_bytes_read = l_memory_stream->dataSize - l_memory_stream->offset;//Read all we have.

//Copy the data to the internal buffer.

memcpy(p_buffer, &(l_memory_stream->pData[l_memory_stream->offset]), l_nb_bytes_read);

l_memory_stream->offset += l_nb_bytes_read;//Update the pointer to the new location.

return l_nb_bytes_read;

}

//This will write from the buffer to our memory.

static OPJ_SIZE_T opj_memory_stream_write(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data)

{

opj_memory_stream* l_memory_stream = (opj_memory_stream*)p_user_data;//Our data.

OPJ_SIZE_T l_nb_bytes_write = p_nb_bytes;//Amount to move to buffer.

//Check if the current offset is outside our data buffer.

if (l_memory_stream->offset >= l_memory_stream->dataSize) return (OPJ_SIZE_T)-1;

//Check if we are write more than we have space for.

if (p_nb_bytes > (l_memory_stream->dataSize - l_memory_stream->offset))

l_nb_bytes_write = l_memory_stream->dataSize - l_memory_stream->offset;//Write the remaining space.

//Copy the data from the internal buffer.

memcpy(&(l_memory_stream->pData[l_memory_stream->offset]), p_buffer, l_nb_bytes_write);

l_memory_stream->offset += l_nb_bytes_write;//Update the pointer to the new location.

return l_nb_bytes_write;

}

//Moves the pointer forward, but never more than we have.

static OPJ_OFF_T opj_memory_stream_skip(OPJ_OFF_T p_nb_bytes, void * p_user_data)

{

opj_memory_stream* l_memory_stream = (opj_memory_stream*)p_user_data;

OPJ_SIZE_T l_nb_bytes;


if (p_nb_bytes < 0) return -1;//No skipping backwards.

l_nb_bytes = (OPJ_SIZE_T)p_nb_bytes;//Allowed because it is positive.

// Do not allow jumping past the end.

if (l_nb_bytes >l_memory_stream->dataSize - l_memory_stream->offset)

l_nb_bytes = l_memory_stream->dataSize - l_memory_stream->offset;//Jump the max.

//Make the jump.

l_memory_stream->offset += l_nb_bytes;

//Returm how far we jumped.

return l_nb_bytes;

}

//Sets the pointer to anywhere in the memory.

static OPJ_BOOL opj_memory_stream_seek(OPJ_OFF_T p_nb_bytes, void * p_user_data)

{

opj_memory_stream* l_memory_stream = (opj_memory_stream*)p_user_data;


if (p_nb_bytes < 0) return OPJ_FALSE;//No before the buffer.

if (p_nb_bytes >(OPJ_OFF_T)l_memory_stream->dataSize) return OPJ_FALSE;//No after the buffer.

l_memory_stream->offset = (OPJ_SIZE_T)p_nb_bytes;//Move to new position.

return OPJ_TRUE;

}

//The system needs a routine to do when finished, the name tells you what I want it to do.

static void opj_memory_stream_do_nothing(void * p_user_data)

{

OPJ_ARG_NOT_USED(p_user_data);

}

//Create a stream to use memory as the input or output.

opj_stream_t* opj_stream_create_default_memory_stream(opj_memory_stream* p_memoryStream, OPJ_BOOL p_is_read_stream)

{

opj_stream_t* l_stream;


if (!(l_stream = opj_stream_default_create(p_is_read_stream))) return (NULL);

//Set how to work with the frame buffer.

if(p_is_read_stream)

opj_stream_set_read_function(l_stream, opj_memory_stream_read);

else

opj_stream_set_write_function(l_stream, opj_memory_stream_write);

opj_stream_set_seek_function(l_stream, opj_memory_stream_seek);

opj_stream_set_skip_function(l_stream, opj_memory_stream_skip);

opj_stream_set_user_data(l_stream, p_memoryStream, opj_memory_stream_do_nothing);

opj_stream_set_user_data_length(l_stream, p_memoryStream->dataSize);

return l_stream;

}


To see how I use these with DICOM images, google SueliX and DICOM.  Look at my code in jpegconv.cpp in the OpenJpeg2 section.
As for JPEG2000 and DICOM.  The image stream should be encapsulated should not have a header, all needed information is in the dicim tags, according to the standard. Should is the key word here. You should always check for a header and skip it.  I have also seen it not encapsulated and the 0xFFD9 cut off the end (NOVARAD).

Bruce

tlal...@gmail.com

unread,
Jun 27, 2016, 9:07:38 AM6/27/16
to OpenJPEG
Thanks for the code Bruce, works perfectly!
Reply all
Reply to author
Forward
0 new messages