c++ - Ejemplos o tutoriales de uso de TurboJPEG de libjpeg-turbo
(4)
Al final, utilicé una combinación de código aleatorio que se encuentra en Internet (por ejemplo, https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c ) y los archivos de cabecera y .c para libjeg-turbo, que están bien documentados. This API oficial es también una buena fuente de información.
Las instrucciones para libjpeg-turbo here 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.
Aquí hay un fragmento de código que uso 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 en rgb (bpp se establecerá 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;
}
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 <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);
Después de eso tienes la imagen comprimida en _compressedImage. Para descomprimir tienes que hacer lo siguiente:
Descompresión:
#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);
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);
agregarí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.
Terminé usando el siguiente código como ejemplo de trabajo tanto para la codificación como para la decodificación JPEG. El mejor ejemplo que puedo encontrar es su autocontenido 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 <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;
}