使用libjpeg-turbo库实现无损图像到jpg压缩图像的内存效果转换

0.前言

深度学习训练的图片大多是经过压缩的jpeg图像,而深度学习推理过程面临的图像往往是内存中的无损图像,从而造成识别效果上的差异。解决差异的方法之一就是将无损的图像进行压缩处理后再进行深度学习推理。此处并不是使用JPEG算法实现压缩过程,而是使用的libjpeg-turbo第三方库实现的,libjpeg-turbo是一个专门为 x86 和 x86-64 处理器优化的高速 libjpeg 的改进版本,实现了libjpeg所有的API并且速度提高了2-6倍。libjpeg是IJG(Independent JPEG Group,一个非正式团体)发布用于JPEG图像压缩的广泛使用的免费库,第一版于1991年10月7日发布。

1.libjpeg-turbo编译安装

step1:下载安装NASM
NASM全称The Netwide Assembler,是一款基于80x86和x86-64平台的汇编语言编译程序,其设计初衷是为了实现编译器程序跨平台和模块化的特性。编译libjpeg-turbo时需要这个东西。
下载地址:https://www.nasm.us/index.php
下载对应的安装版本,双击安装即可。
step2:安装libjpeg-turbo-vc.exe
下载地址:https://sourceforge.net/projects/libjpeg-turbo
下载最新版本的exe,双击安装,在安装路径上就有了include、lib、bin等文件夹。但是这里面的东西除了include里面的头文件是好用的其他的都不好用。还是自己下载源码编译出来的能用。
step3:下载libjpeg-turbo源码编译
下载地址:https://github.com/libjpeg-turbo/libjpeg-turbo
使用cmake,vs进行编译,选好两个路径就行,其他都默认的。


vs编译后就会出现lib和bin了,并且也有静态库。后面我用的都是turbojpeg-static.lib这个静态库。

2.参考example.txt实现功能。

其实就是参考了example中的编码过程和解码过程,代码直接复制过来就能用,我在里面加了一些opencv的操作。另外,自定义的错误处理代码可参考example自行处理。
还有一点比较重要的是使用库中提供的动态分配内存的函数jpeg_mem_dest代替example中的jpeg_stdio_dest函数。
函数原型:
jpeg_mem_dest(j_compress_ptr cinfo,unsigned char ** outbuffer,size_t * outsize);
参数说明:
oubuffer:压缩后的Jpg图像,由函数返回,其内存是在jpeg_mem_dest()函数中申请的,所以压缩完之后需要释放空间,否则造成内存泄露。
outSize:压缩后图像的字节数,由函数返回。
outbuffer里面存放着压缩后的数据,只有jpeg_finish_compress(&cinfo)调用后outbuffer和outsize才是准确的数据地址及数据大小。

#include
#include "jpeglib.h"
#include 
#include 
typedef unsigned char uchar;
using namespace cv;
using namespace std;
void tojpg(cv::Mat &srcImg, cv::Mat &jpgImg, int quality)
{
    //**************压缩编码**************
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    JSAMPROW row_pointer[1];
    int row_stride;
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);
    uchar* outbuffer;
    outbuffer = NULL;
    unsigned long outSize = 0;
    jpeg_mem_dest(&cinfo, &outbuffer, &outSize);
    cinfo.image_width = srcImg.cols;
    cinfo.image_height = srcImg.rows;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, quality, TRUE);
    jpeg_start_compress(&cinfo, TRUE);
    row_stride = srcImg.cols * 3;
    cv::Mat RGBIMG(srcImg.rows, srcImg.cols, CV_8UC3);
    cvtColor(srcImg, RGBIMG, CV_BGR2RGB);
    JSAMPLE *image_buffer = RGBIMG.data;
    while (cinfo.next_scanline < cinfo.image_height) {
        row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride];
        (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }
    jpeg_finish_compress(&cinfo);
        //编码后直接写出来就是能打开的图片
    /*FILE *f = fopen("outbuffer.jpg", "wb");
    fwrite(outbuffer, outSize, 1, f);
    fclose(f);*/
    //************解码成图像******************
    struct jpeg_decompress_struct cinfo2;
    struct my_error_mgr jerr2;
    JSAMPARRAY buffer;      /* Output row buffer */

    cinfo2.err = jpeg_std_error(&jerr2.pub);
    jerr2.pub.error_exit = my_error_exit;
    if (setjmp(jerr2.setjmp_buffer)) {
        jpeg_destroy_decompress(&cinfo2);
        return;
    }
    jpeg_create_decompress(&cinfo2);
    jpeg_mem_src(&cinfo2, outbuffer, outSize);
    (void)jpeg_read_header(&cinfo2, TRUE);
    (void)jpeg_start_decompress(&cinfo2);
    cinfo2.output_width = cinfo.image_width;
    cinfo2.output_height = cinfo.image_height;
    cinfo2.output_components = cinfo.input_components;
    row_stride = cinfo2.output_width * cinfo2.output_components;
    buffer = (*cinfo.mem->alloc_sarray)
        ((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
    
    while (cinfo2.output_scanline < cinfo2.output_height) {
        (void)jpeg_read_scanlines(&cinfo2, buffer, 1);
        memcpy(jpgImg.data + (cinfo2.output_scanline - 1)*row_stride, buffer[0], row_stride);   
    }
    cvtColor(jpgImg, jpgImg, CV_RGB2BGR);
    /*****销毁解码******/
    (void)jpeg_finish_decompress(&cinfo2);
    /* Step 8: Release JPEG decompression object */
    jpeg_destroy_decompress(&cinfo2);

    /******销毁编码*******/
    if (NULL != outbuffer)
    {
        free(outbuffer);
        //delete outbuffer;
        outbuffer = NULL;
    }
    jpeg_destroy_compress(&cinfo);
    return;
}
void main()
{
    cv::Mat bmpimg = cv::imread("1.bmp");
    imshow("src", bmpimg);
    clock_t startTime, endTime;
    startTime = clock();
    Mat jpgImg(bmpimg.rows, bmpimg.cols, CV_8UC3, Scalar(0,0,0));
    tojpg(bmpimg, jpgImg, 10);
    endTime = clock();//计时结束
    double cTime = (double)(endTime - startTime) / CLOCKS_PER_SEC;
    cout << "total time is: " << cTime << "s" << endl;
    imshow("out", jpgImg);
    waitKey();
}

3.结论

一开始使用的是libjpeg,解码一张大点的图需要45ms,换成libjpeg-turbo后同样的图解码需要15ms。这和halcon的用时就一样了。

4.NASM、安装版和源码也可以在我的网盘中下载

https://pan.baidu.com/s/1k-_VW0spuRaQkCajR8ET2Q
提取码:5gco

你可能感兴趣的:(使用libjpeg-turbo库实现无损图像到jpg压缩图像的内存效果转换)