Ejemplos o tutoriales de uso de TurboJPEG de libjpeg-turbo

Las instrucciones para libjpeg-turbo aquí describen la API de TurboJPEG: “Esta API envuelve libjpeg-turbo y proporciona una interfaz fácil de usar para comprimir y descomprimir imágenes JPEG en la memoria”. Genial, pero ¿hay algunos ejemplos sólidos de uso de esta API disponibles? Solo busco descomprimir un jpeg bastante vainilla en la memoria.

He encontrado algunos bits como https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c , que parece estar utilizando la API de TurboJPEG, pero ¿hay más ejemplos sólidos / variados?

La fuente de libjpeg-turbo está bien documentada, por lo que ayuda.

Ok, sé que ya resolviste tu problema, pero como algunas personas, como yo, podrían estar buscando un ejemplo simple, compartiré lo que creé. Es un ejemplo, comprimir y descomprimir una imagen RGB. De lo contrario, creo que la documentación API de TurboJPEG es bastante fácil de entender.

Compresión:

#include  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); 

Después de eso tienes la imagen comprimida en _compressedImage. Para descomprimir tienes que hacer lo siguiente:

Descompresión:

 #include  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); 

Algunos pensamientos al azar:

Acabo de volver sobre esto mientras escribo mi tesis de licenciatura, y me di cuenta de que si ejecuta la compresión en un bucle, es preferible almacenar el tamaño más grande del búfer JPEG para no tener que asignar una nueva cada turno. Básicamente, en lugar de hacer:

 long unsigned int _jpegSize = 0; tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB, &_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY, TJFLAG_FASTDCT); 

añadiríamos una variable de objeto, manteniendo el tamaño de la memoria asignada long unsigned int _jpegBufferSize = 0; y antes de cada ronda de compresión, estableceríamos el jpegSize en ese valor:

 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; 

después de la compresión, se compararía el tamaño de la memoria con el tamaño real de jpegSize y se establecería en jpegSize si es más alto que el tamaño de la memoria anterior.

Al final, utilicé una combinación de código aleatorio que se encuentra en Internet (p. Ej., https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c ) y los archivos .c y header para libjeg-turbo, que están bien documentados. Esta API oficial es también una buena fuente de información.

Terminé usando el siguiente código como ejemplo de trabajo tanto para la encoding como para la deencoding JPEG. El mejor ejemplo que puedo encontrar es que es independiente que inicializa una imagen ficticia y genera la imagen codificada en un archivo local.

El código de abajo NO es mío, el crédito va a https://sourceforge.net/p/libjpeg-turbo/discussion/1086868/thread/e402d36f/#8722 . Al publicarlo aquí de nuevo para ayudar a cualquier persona, es difícil que funcione libjpeg turbo.

 #include "turbojpeg.h" #include  #include  #include  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; } 

Aquí hay un fragmento de código que utilizo para cargar archivos JPEG desde la memoria. Tal vez requiera un poco de corrección, porque lo extraje de diferentes archivos en mi proyecto. Cargará imágenes tanto en escala de grises como rgb (bpp se configurará en 1 o en 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; }