mr-edd.co.uk :: horsing around with the C++ programming language

jpegxx

jpegxx is a C++ library for reading and writing JPEG encoded images that wraps the IJG C library.

It allows you to read and write images to any source or sink, which may be described by standard C++ iterators or streams. By using the provided iterator adaptors, you can use your own image container objects.

Recent activity

Code

Clone the repository using mercurial:

> hg clone http://bitbucket.org/edd/jpegxx

Or get a zip file of the code.

Quick start

To load an image from a file:

#include <jpegxx/read.hpp>

std::vector<unsigned char> raster;
imagexx::raster_details d = 
    jpegxx::read_image("image.jpg", back_inserter(raster));

std::size_t width = d.width();
std::size_t height = d.height();
imagexx::pixel_format f = d.type();
double width_mm = d.width_mm(); // in millimeters
double height_mm = d.height_mm(); // in millimeters

To save an image to a file:

#include <jpegxx/write.hpp>

imagexx::raster_details d(imagexx::rgb, width, height);
jpegxx::write_image(d, "image.jpg", raster.begin(), raster.end());

These examples show reading and writing with a std::vector used for raster storage. You can use the provided iterator adaptors to have the library integrate with your own image container objects.

You can read and write to memory, or any location that can be exposed via a standard C++ a stream or via iterators.

Further reading

Comments

Kim Strid

[23/08/2007 at 02:59:00]

I've been looking for quite some time now for an easy solution to read and write jpeg-files.
All solutions I've found has either been poorly documented or not documented at all. And they have seemed really hard to understand.
Well, then I found jpegxx. I't took me about an hour from that I found this site until I had read a jpeg picture from one spot on the HDD and written it to another spot...

This has really been a life (or rather time) saver! So: my sincerest thanks for a very nice piece of software, that is actually has a easy to understand documentation!

I use DevC++ with the MinGW compiler. I built the library using DevC++. I just created a new project that I placed it in the same directory as the 'src' directory and added all the needed files and search paths to the project. At the first run the compiler complained about not finding the file 'jpeglib.h'. After I copied the file to 'src/ijg' the library compiled with no problem.
I thought it was worth mentioning, in case somebody else wants to use the same approach.

Thanks again for a great and user friendly product!

/Kim Strid , Sweden

Edd

[23/08/2007 at 18:23:00]

Hi Kim!

I’m very pleased to hear that you’ve had success with the library :)

When compiling it "manually", you should make sure that the following directories are in your compiler's header search paths:

- src/ijg
- include
- include/jpegxx/ijg

It sounds like the last one was possibly missing? Then when it comes to actually *using* the library in your own projects, you compiler will only need to know about the 'include' directory.

But the way you worked around the problem shouldn't cause any problems :)

Edd

Alex

[29/09/2007 at 17:33:00]

Hi,

nice lib, may be a bit too low-level, tough. ;-)

It would be great to add `image’ class (possibly a class template) to hold the pixel data and details, like this:

template<typename pixel>
class image
{
public:
    image(char const* filename);

    void read(char const* filename);
    void write(char const* filename);

    pixel& pixel(size_t x, size_t y);  // or get_pixel/set_pixel method pair
    ...
};

So, the client code does not take burden of dealing with low-level lib interface. The client code to read an image would be like this:

jpegxx::image image("myimage.jpg");

or this:

jpegxx::image image;
image.read("myimage.jpg");

Having such a class for an image processing library just seems natural, IMHO.

Some more subjective notes:

1. You will need to add rgb_pixel and grayscale_pixel classes for different color models.
2. Reading grayscale into image<rgb_pixel> might throw or it might convert color space silently. The choice could be left to the client code, with a reasonable default.
3. I would rename the lib namespace to jpeg instead of jpegxx. :-)
4. You already have doxygen-style comments in the code. Adding a properly setup Doxyfile to the zip would be very helpful.


Cheers,
Alex

Edd

[30/09/2007 at 01:47:00]

Hi Alex!

Thanks for your comments!

I really didn’t want to add my own image class to the library. In fact a key goal of the library was to be entirely independent of any image container. I like the composability this approach affords. I feel the library is as "high level" as it could be without compromising flexibility. It's certainly a heck of a lot nicer to use than the IJG C interface, anyway, IMHO :)

There are plenty of other libraries out there that can read JPEG images in to a "proprietary" container object, which may or may not suit one's needs depending on the project. In my experience, they usually don't quite gel with what I want to do. And in writing an image class I'm second guessing the client's needs.

Do they want the data stored pixel-by-pixel, or in a planar fashion? Which operations should the image support? Resize? Gaussian blur? Edge detect? HDR? Anything else? Which existing imaging libraries should the class be "compatible" with? Is the image for off-screen manipulation or will it be loaded directly in to a windowing system context of some kind? Peoples needs vary wildly. I don't want to encourage people to use an inappropriate container that propagates out in to their code.

Besides it's only about 20 lines of code to create a basic image class with pixels accessible using 2D coordinates in the usual way. You can pluck one out of the example code if you like! Perhaps I should write a post on how to create a simple image container?

The namespace issue: I originally used "jpeg" for the namespace identifier, but I feel the name should match that of the library. You can always do this if you really want:

namespace jpeg = jpegxx;

Also, I've only ever really used the default doxygen configuration. What settings/switches would you suggest that I put in to the Doxyfile?

I also got your message about the comment markup syntax. I'll try and put a little instructions box up soon -- Thanks!

Alex

[30/09/2007 at 17:11:00]

Hi Edd!

> I really didn’t want to add my own image class to the library.

Adding the image class doesn't mean client code would not be able to use the lower-level interface like jpegxx::read_image(). Adding it, however, could be convenient for the users who do not want to deal with image details and pixel storage directly. And with the use of templates, it could be made very flexible to fit various people demands. ;-)

> Which operations should the image support?

I didn't really mean that image class should support _any_ image processing operations. That I've proposed is just a convenient wrapper around raster_details and pixel buffer with a simple 2D pixel access.

> namespace jpeg = jpegxx;

Yes, I realize this, however it's kinda matter of taste...

> What settings/switches would you suggest that I put in to the Doxyfile?

Actually, I've proposed that you add a Doxyfile since I was trying it out with the default generated one, but no luck. A very quick view into manual didn't revealed how to deal with sub-folders, so I just gave up. May be you could find out more if you try. I am, personally, just not _that_ interested in your otherwise nice lib right now, so I can live w/o docs at hand. :-)

Edd

[30/09/2007 at 21:11:00]

Hey Alex!

If I were to provide was a basic image class, the client would simply end up copying the data in to a more flexible container anyway, I would assume. This would certainly be the case in my projects. It just seems rather pointless to have a redundant middle stage when the image can be placed directly inside an image object of one's choice. It only takes 10 lines of code to write a composer object for use with jpegxx::pixel_compose_iterator.

Having a middle-man image container is less general and less efficient, so I'm unwilling to add it, I'm afraid. I think we'll just have to agree to disagree, here! But again, perhaps I'll make a post about how to use jpegxx to interface with some of the more popular image containers.

When I get a moment I will investigate creating a better Doxyfile (which I could probably put to good use in other projects, too). However, the Doxygen comments are only really for people that want to tinker with the guts of the library. This wiki and the example programs should be the primary source of client documentation. So if there's something that is unclear or missing, please let me know so that I can make improvements :)

Andrea Carboney

[10/09/2008 at 18:15:00]

hello,
first my compliments for the excellent code that you are offering.

I was looking at jpegxx to see if there is a straight way to do a memory to memory encoder/decoder with jpegxx.

Consider that I have a memory buffer containing raw RGB values (something like a unsigned char* rawimage). I would like to encode that image into JPEG obtaining another raw buffer (getting from the encoder the lenght in bytes of the encoded image.

On the other side I would like to do exactly the opposite: reconstructing a raw image from its encoded representation.

I have already done that for another project using the Iig code in the background and using a C++ wrapper (jpeg-c++). I remember that I had to define a specialized memory destination (a sink) to manage the memory writings and readings.

Well, it worked just fine .. but now I am considering to reimplement the codec using jpegxx.
If possible I’d like some tips on how to implement the mem2mem thing.

Don’t need it step by step, but maybe few hints will be very helpful.

Thank for the work you are doing and for your attention.

best
andrea

Edd

[10/09/2008 at 20:55:00]

Hi Andrea,

That's certainly possible. For example, to take a bunch of rgb triples and write them as a JPEG to another memory buffer, you can do:

const unsigned char *rgb_triples = ...;
jpegxx::raster_details d(jpegxx::raster_details::rgb, w, h); // w and h are your image dims

std::vector<unsigned char> memblock;
jpegxx::write_image(d, rgb_triples, rgb_triples + 3 * w * h, back_inserter(memblock));

// memblock now contains the encoded data

To convert the JPEG encoded block back in to an unencoded array of rgb triples again, you can do the reverse with jpegxx::read_image():

const unsigned char *encoded_block = ...;
const std::size_t sz = ...; // # bytes in encoded_block
std::vector<unsigned char> rgb_triples;

jpegxx::raster_details d = jpegxx::read_image(encoded_block, encoded_block + sz, back_inserter(rgb_triples));
// d contains info on the size and type of jpeg image loaded.
// Note that it could be a greyscale image (not rgb triples).
// In this case, you'll have to do a post conversion step.

Andrea Carboney

[10/09/2008 at 23:20:00]

Wow, good to know!!
Are you implementing the jpeg compression from scratch?

Maybe, in this way I can avoid the pimpl I was introducing to separate what was the ijg implementation.

I have spent more time on the docs and the sources and I was converging to the solution you provided.
Now I am travelling, so will test the library when possible.

Thank for your quick support!

cheers

Edd

[10/09/2008 at 23:35:00]

Hi Andrea,

I haven't implemented my own JPEG (de)compressor. JPEG compression is provided by the IJG C library. It would be a big job to implement it from scratch!

You'll notice that the jpegxx source code comes with the IJG library (with some minor tweaks by me to make it play nice with C++ compilers). But the IJG stuff has been pre-pimpl’d for you already (if you get the newer code from the Mercurial repo)!

You should find that the libs work fine on OS X, as my primary development setup has been OS X and gcc 4.0 for the last 6 months or so. Any feedback you have is appreciated, of course.

Edd

Andrea Carboney

[17/09/2008 at 07:02:00]

Just for you to know, I have done a wrapper for jpegxx that runs just fine on MacOSX.
I am using opencv for image acquisition and the memory to memory coding and encoding
works as expected!

I am using the new source from mercurial.

Thank you!

Edd

[17/09/2008 at 21:06:00]

Great! Glad to hear it’s being put to good use :)

aaslannn

[15/04/2009 at 17:24:00]

Hi Edd,

Firstly, thanks for your effort on this project. I am currently working on a real time image streaming application. I tried your mem2mem example and created a vector with a jpeg encoded image in it. However, when I save the image to a file by iterating the each element in the vector, I get the image but its blueish. The reason is that opencv uses bgr palette but jpeg compression only gets rgb data. Opencv has a function to convert bgr2rgb but as I am trying to do a streaming application, I do not want to spend much time on the conversion. Is there any way to simplify this issue in the jpegxx library?

thanks in advance.

Morgue

[17/11/2009 at 12:46:00]

Hi,

The jpegxx library seems to be the perfect tool to solve my problem (memory to memory coding), but my program throws an exceoption when I try to use it:

terminate called after throwing an instance of 'jpegxx::compression_failure'
what(): The JPEG codec failed to compress the raster
Aborted (core dumped)

My code:

#include <imagexx/raster_details.hpp>
#include <imagexx/iterators/pixel_iterator.hpp>
#include <jpegxx/write.hpp>

using imagexx::rgb_forwarder;
using imagexx::pixel_decompose_iterator;

class Image {
public:
    Image(int height, int width) : height(height), width(width) {}
    int getHeight() const { return height; }
    int getWidth() const { return width; }
    int getPixel(int x, int y) const { return 0; }
protected:
    int width, height;
};

class PixelDecomposer
{
public:
    PixelDecomposer(const PixelDecomposer& pd) : image(pd.image), i(pd.i) {}
    PixelDecomposer(const Image& image) : image(&image), i(0) {}
    unsigned char get_red() { return image->getPixel(i%image->getWidth(), i/image->getWidth()) >> 16 & 0xFF; }
    unsigned char get_green() { return image->getPixel(i%image->getWidth(), i/image->getWidth()) >> 8 & 0xFF; }
    unsigned char get_blue() { return image->getPixel(i%image->getWidth(), i/image->getWidth()) & 0xFF; }
    void next_pixel() { i++; }
    bool operator== (const PixelDecomposer &other) const { return i==other.i; }
protected:
    const Image * image;
    int i;
};

int main()
{
    Image image(100,100);

    PixelDecomposer pixeldecomposer(image);

    imagexx::raster_details details(imagexx::rgb, image.getWidth(), image.getHeight());
    pixel_decompose_iterator<PixelDecomposer, rgb_forwarder> b(pixeldecomposer, details);
    pixel_decompose_iterator<PixelDecomposer, rgb_forwarder> e;
    jpegxx::write_image(details, “my_image.jpg”, b, e);
}

(I know it's not really useful, but I try to build it up incrementally.)

Could you help me, why does not it work? I compiled with G++ 3.4.5 for x86_64 architecture.

Thanks in advance

Edd

[18/11/2009 at 20:10:00]

Hi Morgue,

I tried your code on my machine here, but I can't replicate the behavior you're seeing. So here are some things to try:

1. Could you wrap the write_image call in a try/catch block in order to catch an std::exception? The .what() might be helpful in diagnosing the problem.

2. Could you try using your system's libjpeg? The JPEG code that comes with the jpegxx distribution is configured ok for all the system's I use, but that may not be the case for yours. I've never tried the code on a 64 bit target before.

3. Perhaps you could try using a vector with 100*100*3 elements and passing that data to write_image instead of using pixel_decompose_iterator. That would help to identify whether it's a problem with the iterator or the "algorithm".

In fact, it would be great if you could file the answers to these questions in a bug report here: http://bitbucket.org/edd/jpegxx/issues/.

Thanks!

Morgue

[19/11/2009 at 09:31:00]

Hi,

Thank you for your reply. I have tried your suggestions, and I have filed an issue. I think the problem is that libjpeg is not configured for 64 bit systems.

But when linking against my own system's libjepg library the program works as expected, so now I can continue development.

Thanks,
Morgue

Edd

[20/11/2009 at 01:00:00]

Perfect! Thanks very much. I'm glad you've found a workaround.

When I get a moment, I'll try to pin down what might be the problem with the IJG library I provide in the jpegxx distribution.

(optional)
(optional)
(required, hint)

Links can be added like [this one -> http://www.mr-edd.co.uk], to my homepage.
Phrases and blocks of code can be enclosed in {{{triple braces}}}.
Any HTML markup will be escaped.