Gibt es eine Möglichkeit, JPEG mit einer bestimmten Bitrate zu kodieren?
Zurzeit verwende ich imagemagicks convert
:
convert Lenna-gray-100.jpeg -quality 1.1111 test.jpeg
Die Bitrate steigt mit der Qualität, aber sie ist nicht linear. Ich möchte die Bitrate explizit steuern. Sie muss nicht exakt sein, aber ich möchte, dass sie einigermaßen genau ist (innerhalb von, sagen wir, 0,1 bpp der angegebenen Einstellung).
Gibt es einen Encoder, mit dem Bilder mit einer bestimmten Bitrate kodiert werden können? Es muss nicht imagemagick sein, ich nehme alles, was funktioniert (vorzugsweise unter Linux).
Ein dummer Weg, dies zu tun, wäre das Herumspielen mit Bruchwerten in der -quality
Parameter, bis etwas in der Nähe der Zielbitrate herauskommt, aber ich hoffe auf eine elegantere Lösung.
EDIT :
Also habe ich mich gelangweilt und beschlossen, die Dinge auf die schnelle (aber dumme) Art zu erledigen.
Zunächst ein Diagramm von imagemagick's -quality
gegenüber der Bitrate:
Übrigens, hier ist das Bild, das ich verwendet habe:
Die Änderung der Bitrate ist also bei niedrigeren Qualitätswerten recht gut, wird aber ab etwa 80 grob.
Hier ist ein Beispielcode zur Kodierung eines Bildes mit einer bestimmten Zielbitrate. Ich habe OpenCV verwendet, weil es eine speicherinterne JPEG-Kodierung ermöglicht (kein I/O notwendig). Ursprünglich wollte ich das mit Python nachbauen, aber leider stellen die Python OpenCV Wrapper die In-Memory Kodierungsfunktionalität nicht zur Verfügung. Also habe ich es in C++ geschrieben.
Schließlich dachte ich an eine lineare Interpolation der Qualität, um näher an die Zielbitrate heranzukommen, aber da cv::imencode
nur ganzzahlige Parameter akzeptiert, ist es nicht möglich, eine nicht-ganzzahlige JPEG-Qualität einzustellen. Die Qualitätsskala zwischen OpenCV und imagemagick scheint auch etwas unterschiedlich zu sein, so dass der interpolierte Qualitätsparameter von OpenCV und die Verwendung in imagemagick's convert
hat nicht gut funktioniert.
Dies bedeutet, dass die Ausgangsbitrate nicht mit der Zielbitrate übereinstimmt, insbesondere bei höheren Bitraten ( > 1). Aber sie ist nahe dran.
Kann jemand etwas Besseres vorschlagen?
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;
}
Kompilieren und ausführen mit:
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