libjpeg-turbo的TurboJPEG使用示例或教程

37
libjpeg-turbo的说明文档here这样描述TurboJPEG API: “这个API封装了libjpeg-turbo库,并提供了一个易于使用的接口,用于在内存中压缩和解压JPEG图像”。很好,但是现在有没有一些使用这个API的可靠示例呢?只想要在内存中解压一个相当普通的JPEG文件。

我发现了一些东西,比如https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c ,似乎正在使用TurboJPEG API,但是否有更多可靠/多样化的示例?

libjpeg-turbo的源代码文档得到了很好的记录,所以这确实有所帮助。

4个回答

69

好的,我知道您已经解决了问题,但是像我这样的一些人可能正在搜索一些简单的示例,所以我将分享我创建的内容。这是一个压缩和解压缩RGB图像的示例。否则,我认为TurboJPEG API文档相当容易理解!

压缩:

#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。

1
给那些点踩的人:我仍在维护这个答案,很乐意知道你认为哪里不好。所以请留下评论! - Theolodis
1
新的TJ版本(1.4)已经解压到YUV平面: tjDecompressToYUVPlanes(c_jpegDecompressor, c_jpeg_buffer, _jpegSize, m_planesYUV, 0, NULL, 0, m_accuracy); - mousomer
2
Turbojpeg的文档链接已经失效,请尝试访问https://libjpeg-turbo.org/Documentation/Documentation。 - Alan Corey
2
@jean-loup 你可能在看jpeg库的更快实现吗?因为libjpeg-turbo != Turbojpeg。请参考https://cdn.rawgit.com/libjpeg-turbo/libjpeg-turbo/master/doc/html/group___turbo_j_p_e_g.html。 - Theolodis
1
@Theolodis 有些混淆,因为libjpeg-turbo的头文件是turbojpeg.h,而在Ubuntu中的libjpeg-turbo并不是这个库...但是从git仓库安装后,它完美地工作了。 - jean-loup
显示剩余10条评论

8
我最终使用以下代码作为JPEG编码和解码的工作示例。这是我能找到的最好的例子,它是自包含的,可以初始化一个虚拟图像并将编码后的图像输出到本地文件。
以下代码不是我自己的,感谢https://sourceforge.net/p/libjpeg-turbo/discussion/1086868/thread/e402d36f/#8722提供的帮助。再次在此发布以帮助任何发现难以使用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;
}

4

5
如果代码很小,您可以在这里分享它以改进您的答案 ;) - Rui Marques

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;
}

1
很抱歉取消接受你的答案,但是现在我仔细实现时发现你在发布的代码中甚至没有调用tj函数(例如tjDecompress)。 - occulus
1
不一定需要使用TurboJpeg API才能从libjpeg-turbo库中获得速度优势。在我的例子中,我只是使用标准的libjpeg API。为什么你不想使用libjpeg API呢? - Mārtiņš Možeiko
4
我的问题很明显是关于TurboJPEG API的。我使用它是因为相对于标准的libjpeg接口,它更简单易用。 - occulus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接