Примеры или руководства по использованию TurboJPEG в libjpeg-turbo
Инструкции для libjpeg-turbo здесь описывают API TurboJPEG следующим образом: "Этот API оборачивает libjpeg-turbo и предоставляет простой в использовании интерфейс для сжатия и распаковки изображений JPEG в памяти". Отлично, но есть ли надежные примеры использования этого API? Просто хочу распаковать довольно ванильный JPEG в памяти.
Я нашел несколько битов, таких как https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c, которые, похоже, используют API TurboJPEG, но есть ли более твердые / разнообразные примеры?
Источник для libjpeg-turbo хорошо документирован, так что это помогает.
4 ответа
В конце я использовал комбинацию случайного кода, найденного в Интернете (например, https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c) и файлы.c и заголовочные файлы для libjeg-turbo, которые хорошо документированы. Этот официальный API также является хорошим источником информации.
Хорошо, я знаю, что вы уже решили свою проблему, но некоторые люди, как и я, могли бы найти простой пример, которым я поделюсь с вами тем, что я создал. Это пример сжатия и распаковки изображения RGB. В противном случае, я думаю, что документация по API TurboJPEG довольно проста для понимания!
Сжатие:
#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);
После этого у вас есть сжатое изображение в _compressedImage. Для распаковки вам необходимо сделать следующее:
Декомпрессия:
#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);
Несколько случайных мыслей:
Я только что вернулся к этому, когда писал свою дипломную работу бакалавра, и заметил, что если вы запускаете сжатие в цикле, предпочтительно хранить самый большой размер буфера JPEG, чтобы не приходилось выделять новый каждый ход. В основном, вместо того, чтобы делать:
long unsigned int _jpegSize = 0;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
мы бы добавили объектную переменную, содержащую размер выделенной памяти long unsigned int _jpegBufferSize = 0;
и перед каждым циклом сжатия мы устанавливаем jpegSize обратно в это значение:
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;
после сжатия можно сравнить размер памяти с фактическим jpegSize и установить его в jpegSize, если он больше, чем предыдущий объем памяти.
В итоге я использовал приведенный ниже код в качестве рабочего примера для кодирования и декодирования JPEG. Лучший пример, который я могу найти, это автономный, который инициализирует фиктивное изображение и выводит закодированное изображение в локальный файл.
Ниже код не мой, кредит идет на https://sourceforge.net/p/libjpeg-turbo/discussion/1086868/thread/e402d36f/. Публикуя его здесь снова, чтобы помочь кому-либо, с трудом заставить работать 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;
}
Вот фрагмент кода, который я использую для загрузки jpeg из памяти. Возможно, это потребует некоторого исправления, потому что я извлек его из разных файлов в моем проекте. Он будет загружать изображения как в градациях серого, так и в формате rgb (для bpp будет установлено значение 1 или 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;
}