Les instructions pour libjpeg-turbo ici décrivent l'API TurboJPEG ainsi: "Cette API enveloppe libjpeg-turbo et fournit une interface facile à utiliser pour compresser et décompresser des images JPEG en mémoire". Très bien, mais existe-t-il des exemples solides d'utilisation de cette API? Je cherche juste à décompresser un jpeg assez vanille en mémoire.
J'ai trouvé quelques bits tels que https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c , qui semble utiliser l'API TurboJPEG, mais est là des exemples plus solides/variés?
La source de libjpeg-turbo est bien documentée, donc cela aide.
À la fin, j'ai utilisé une combinaison de code aléatoire trouvé sur Internet (par exemple https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c ) et le .c et fichiers d'en-tête pour libjeg-turbo, qui sont bien documentés. This l'API officielle est également une bonne source d'informations.
Ok, je sais que vous avez déjà résolu votre problème, mais comme certaines personnes, tout comme moi, pourraient rechercher un exemple simple, je partagerai ce que j'ai créé. Il s'agit d'un exemple de compression et de décompression d'une image RVB. Sinon, je pense que la documentation API de TurboJPEG est assez facile à comprendre!
Compression:
#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);
Après cela, vous avez l'image compressée dans _compressedImage. Pour décompresser, vous devez procéder comme suit:
Décompression:
#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);
Quelques pensées aléatoires:
Je viens de revenir sur ce point alors que j'écris ma thèse de baccalauréat, et j'ai remarqué que si vous exécutez la compression en boucle, il est préférable de stocker la plus grande taille du tampon JPEG pour ne pas avoir à en allouer un nouveau à chaque tour. Fondamentalement, au lieu de faire:
long unsigned int _jpegSize = 0;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
nous ajouterions une variable objet, tenant la taille de la mémoire allouée long unsigned int _jpegBufferSize = 0;
et avant chaque cycle de compression, nous redéfinissons le jpegSize à cette valeur:
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;
après la compression, on comparerait la taille de la mémoire avec le jpegSize réel et le définirait au jpegSize s'il est supérieur à la taille de la mémoire précédente.
J'ai fini par utiliser le code ci-dessous comme exemple de travail pour l'encodage et le décodage JPEG. Le meilleur exemple que je puisse trouver, c'est autonome qui initialise une image fictive et sort l'image encodée dans un fichier local.
Le code ci-dessous est [~ # ~] pas [~ # ~] le mien, le crédit revient à https://sourceforge.net/ p/libjpeg-turbo/discussion/1086868/thread/e402d36f/# 8722 . Le poster ici à nouveau pour aider quiconque trouve qu'il est difficile de faire fonctionner libjpeg turbo.
#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;
}
Voici un fragment de code que j'utilise pour charger des jpeg à partir de la mémoire. Peut-être que cela nécessitera un peu de correction, car je l'ai extrait de différents fichiers de mon projet. Il chargera les images en niveaux de gris et RVB (bpp sera défini sur 1 ou sur 3).
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;
}