Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify bitrate for JPEG compression?

Is there a way to encode JPEG at a specific bitrate?

Presently, I'm using imagemagick's convert:

convert Lenna-gray-100.jpeg -quality 1.1111 test.jpeg

Bitrate increases with quality, but it's non-linear. I want to control the bitrate explicitly. It doesn't have to be exact, but I want it reasonably close (within, say 0.1 bpp of the specified setting).

Is there any encoder there that allows images to be encoded at a particular bit-rate? It doesn't have to be imagemagick, I'll take whatever works (preferably on Linux).

A dumb way to do this would be to play around with fractional values to the -quality parameter until something close to the target bitrate comes out, but I'm hoping for a more elegant solution.

EDIT:

So I got bored and decided to do things the quick (but stupid) way.

First, here's a graph of imagemagick's -quality vs bitrate:

alt text

BTW, here's the image I used:

alt text

So the change in bitrate is quite fine for lower quality values, but becomes coarse after about 80.

Here's some sample code to encode an image at some target bitrate. I used OpenCV cause it allows for in-memory JPEG encoding (no I/O necessary). While I originally was going to mock this up with Python, unfortunately the Python OpenCV wrappers don't expose the in-memory encoding functionality. So I wrote it in C++.

Lastly, I was thinking of using linear interpolation on the quality to get closer to the target bitrate, but since cv::imencode only accepts integer parameters, it's not possible to set a non-integer JPEG quality. The quality scale between OpenCV and imagemagick seems to differ somewhat as well, so taking the interpolated quality parameter from OpenCV and using in imagemagick's convert didn't work well.

This means that the output bitrate isn't equal to the target bitrate, especially at higher bitrates ( > 1). But it's close.

Can anyone suggest something better?

Code:

#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#include <assert.h>
#include <vector>

using cv::Mat;
using std::vector;

#define IMENCODE_FMT   ".jpeg"
#define QUALITY_UBOUND 101
#define BITS_PER_BYTE  8

int
main(int argc, char **argv)
{
    if (argc != 4)
    {
        fprintf(stderr, "usage: %s in.png out.jpeg bpp\n", argv[0]);
        return 1;
    }

    char *fname_in = argv[1];
    char *fname_out = argv[2];
    float target;
    sscanf(argv[3], "%f", &target);

    Mat orig = cv::imread(fname_in);
    int pixels = orig.size().width * orig.size().height * orig.channels();

    vector<unsigned char> buf;
    vector<int> params = vector<int>(2);
    params[0] = CV_IMWRITE_JPEG_QUALITY;
    int q;
    double bpp = 0.0;

    for (q = 1; q < QUALITY_UBOUND; ++q)
    {
        params[1] = q;
        cv::imencode(IMENCODE_FMT, orig, buf, params);
        bpp = (double)buf.size() * BITS_PER_BYTE / pixels;
        if (bpp > target)
            break;
    }

    cv::imwrite(fname_out, orig, params);
    printf("wrote %s at %d%% quality, %.2fbpp\n", fname_out, q, bpp);

    return 0;
}

Compile and run using:

g++ -c -Wall -ggdb -I../c -I../blur `pkg-config --cflags opencv` -Wno-write-strings jpeg-bitrate.cpp -o jpeg-bitrate.o
g++ -I../c `pkg-config --cflags opencv` `pkg-config --libs opencv` -lboost_filesystem jpeg-bitrate.o -o jpeg-bitrate.out
rm jpeg-bitrate.o
misha@misha-desktop:~/co/cpp$ ./jpeg-bitrate.out Lenna-gray.png test.jpeg 0.53
wrote test.jpeg at 88% quality, 0.55bpp
like image 756
mpenkov Avatar asked Jan 13 '11 06:01

mpenkov


1 Answers

I know a lot of work exist on controlling the output bitrate of a JPEG encoder (e.g. 1st paper ; 2nd paper), and that such controls exist in JPEG2000. Unfortunately, I'm not sure any kind of bitrate control is standardized for JPEG, or implemented in common libraries. You may have to code your own method, using some kind of binary search for instance...

But again, I may be mistaken - and if so, I'd love to hear about such a library.

Just out of curiosity, what language are you using ?

like image 181
BlueCookie Avatar answered Sep 24 '22 23:09

BlueCookie