Die Anleitung für libjpeg-turbo hier beschreibt die TurboJPEG-API folgendermaßen: “Diese API umschließt libjpeg-turbo und bietet eine einfach zu verwendende Schnittstelle zum Komprimieren und Dekomprimieren von JPEG-Bildern im Speicher”. Großartig, aber gibt es einige solide Beispiele für die Verwendung dieser API? Ich möchte nur ein ziemlich einfaches JPEG im Speicher dekomprimieren.
Ich habe ein paar Bits gefunden, wie z https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.cdas anscheinend die TurboJPEG-API verwendet, aber gibt es solidere/variablere Beispiele?
Die Quelle für libjpeg-turbo ist gut dokumentiert, das hilft also.
Ok, ich weiß, dass Sie Ihr Problem bereits gelöst haben, aber da einige Leute, genau wie ich, nach einem einfachen Beispiel suchen könnten, werde ich teilen, was ich erstellt habe. Es ist ein Beispiel für das Komprimieren und Dekomprimieren eines RGB-Bildes. Ansonsten denke ich, dass die API-Dokumentation von TurboJPEG ist ganz einfach zu verstehen!
Kompression:
#include <turbojpeg.h>
const int JPEG_QUALITY = 75;
const int COLOR_COMPONENTS = 3;
int _width = 1920;
int _height = 1080;
long unsigned int _jpegSize = 0;
unsigned char* _compressedImage = NULL; //!< Memory is allocated by tjCompress2 if _jpegSize == 0
unsigned char buffer[_width*_height*COLOR_COMPONENTS]; //!< Contains the uncompressed image
tjhandle _jpegCompressor = tjInitCompress();
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
tjDestroy(_jpegCompressor);
//to free the memory allocated by TurboJPEG (either by tjAlloc(),
//or by the Compress/Decompress) after you are done working on it:
tjFree(&_compressedImage);
Danach haben Sie das komprimierte Bild in _compressedImage. Zum Dekomprimieren müssen Sie Folgendes tun:
Dekompression:
#include <turbojpeg.h>
long unsigned int _jpegSize; //!< _jpegSize from above
unsigned char* _compressedImage; //!< _compressedImage from above
int jpegSubsamp, width, height;
unsigned char buffer[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image
tjhandle _jpegDecompressor = tjInitDecompress();
tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, &height, &jpegSubsamp);
tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, buffer, width, 0/*pitch*/, height, TJPF_RGB, TJFLAG_FASTDCT);
tjDestroy(_jpegDecompressor);
Einige zufällige Gedanken:
Ich bin gerade beim Schreiben meiner Bachelorarbeit darauf zurückgekommen und habe festgestellt, dass es besser ist, wenn Sie die Komprimierung in einer Schleife ausführen, die größte Größe des JPEG-Puffers zu speichern, um nicht jede Runde einen neuen zuweisen zu müssen. Grundsätzlich, anstatt zu tun:
long unsigned int _jpegSize = 0;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
Wir würden eine Objektvariable hinzufügen, die die Größe des zugewiesenen Speichers enthält long unsigned int _jpegBufferSize = 0;
und vor jeder Komprimierungsrunde würden wir die jpegSize auf diesen Wert zurücksetzen:
long unsigned int jpegSize = _jpegBufferSize;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
_jpegBufferSize = _jpegBufferSize >= jpegSize? _jpegBufferSize : jpegSize;
Nach der Komprimierung würde man die Speichergröße mit der tatsächlichen jpegSize vergleichen und auf die jpegSize setzen, wenn sie größer als die vorherige Speichergröße ist.
Am Ende habe ich den folgenden Code als Arbeitsbeispiel für die JPEG-Codierung und -Decodierung verwendet. Das beste Beispiel, das ich finden kann, ist in sich geschlossen, das ein Dummy-Bild initialisiert und das codierte Bild in eine lokale Datei ausgibt.
Der folgende Code ist NICHT meine eigene, Kredit geht an https://sourceforge.net/p/libjpeg-turbo/discussion/1086868/thread/e402d36f/#8722 . Ich poste es hier noch einmal, um jemandem zu helfen, der findet, dass es schwierig ist, libjpeg turbo zum Laufen zu bringen.
#include "turbojpeg.h"
#include <iostream>
#include <string.h>
#include <errno.h>
using namespace std;
int main(void)
{
unsigned char *srcBuf; //passed in as a param containing pixel data in RGB pixel interleaved format
tjhandle handle = tjInitCompress();
if(handle == NULL)
{
const char *err = (const char *) tjGetErrorStr();
cerr << "TJ Error: " << err << " UNABLE TO INIT TJ Compressor Object\n";
return -1;
}
int jpegQual =92;
int width = 128;
int height = 128;
int nbands = 3;
int flags = 0;
unsigned char* jpegBuf = NULL;
int pitch = width * nbands;
int pixelFormat = TJPF_GRAY;
int jpegSubsamp = TJSAMP_GRAY;
if(nbands == 3)
{
pixelFormat = TJPF_RGB;
jpegSubsamp = TJSAMP_411;
}
unsigned long jpegSize = 0;
srcBuf = new unsigned char[width * height * nbands];
for(int j = 0; j < height; j++)
{
for(int i = 0; i < width; i++)
{
srcBuf[(j * width + i) * nbands + 0] = (i) % 256;
srcBuf[(j * width + i) * nbands + 1] = (j) % 256;
srcBuf[(j * width + i) * nbands + 2] = (j + i) % 256;
}
}
int tj_stat = tjCompress2( handle, srcBuf, width, pitch, height,
pixelFormat, &(jpegBuf), &jpegSize, jpegSubsamp, jpegQual, flags);
if(tj_stat != 0)
{
const char *err = (const char *) tjGetErrorStr();
cerr << "TurboJPEG Error: " << err << " UNABLE TO COMPRESS JPEG IMAGE\n";
tjDestroy(handle);
handle = NULL;
return -1;
}
FILE *file = fopen("out.jpg", "wb");
if (!file) {
cerr << "Could not open JPEG file: " << strerror(errno);
return -1;
}
if (fwrite(jpegBuf, jpegSize, 1, file) < 1) {
cerr << "Could not write JPEG file: " << strerror(errno);
return -1;
}
fclose(file);
//write out the compress date to the image file
//cleanup
int tjstat = tjDestroy(handle); //should deallocate data buffer
handle = 0;
}
Am Ende habe ich eine Kombination aus zufälligem Code verwendet, der im Internet gefunden wurde (z https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c) und die .c- und Header-Dateien für libjeg-turbo, die gut dokumentiert sind.
Dies Die offizielle API ist ebenfalls eine gute Informationsquelle.
Hier ist ein Codefragment, mit dem ich JPEGs aus dem Speicher lade. Vielleicht muss es ein wenig repariert werden, da ich es aus verschiedenen Dateien in meinem Projekt extrahiert habe. Es werden sowohl Graustufen- als auch RGB-Bilder geladen (bpp wird entweder auf 1 oder auf 3 gesetzt).
struct Image
{
int bpp;
int width;
int height;
unsigned char* data;
};
struct jerror_mgr
{
jpeg_error_mgr base;
jmp_buf jmp;
};
METHODDEF(void) jerror_exit(j_common_ptr jinfo)
{
jerror_mgr* err = (jerror_mgr*)jinfo->err;
longjmp(err->jmp, 1);
}
METHODDEF(void) joutput_message(j_common_ptr)
{
}
bool Image_LoadJpeg(Image* image, unsigned char* img_data, unsigned int img_size)
{
jpeg_decompress_struct jinfo;
jerror_mgr jerr;
jinfo.err = jpeg_std_error(&jerr.base);
jerr.base.error_exit = jerror_exit;
jerr.base.output_message = joutput_message;
jpeg_create_decompress(&jinfo);
image->data = NULL;
if (setjmp(jerr.jmp)) goto bail;
jpeg_mem_src(&jinfo, img_data, img_size);
if (jpeg_read_header(&jinfo, TRUE) != JPEG_HEADER_OK) goto bail;
jinfo.dct_method = JDCT_FLOAT; // change this to JDCT_ISLOW on Android/iOS
if (!jpeg_start_decompress(&jinfo)) goto bail;
if (jinfo.num_components != 1 && jinfo.num_components != 3) goto bail;
image->data = new (std::nothrow) unsigned char [jinfo.output_width * jinfo.output_height * jinfo.output_components];
if (!image->data) goto bail;
{
JSAMPROW ptr = image->data;
while (jinfo.output_scanline < jinfo.output_height)
{
if (jpeg_read_scanlines(&jinfo, &ptr, 1) != 1) goto bail;
ptr += jinfo.output_width * jinfo.output_components;
}
}
if (!jpeg_finish_decompress(&jinfo)) goto bail;
image->bpp = jinfo.output_components;
image->width = jinfo.output_width;
image->height = jinfo.output_height;
jpeg_destroy_decompress(&jinfo);
return true;
bail:
jpeg_destroy_decompress(&jinfo);
if (image->data) delete [] data;
return false;
}