使用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<stdio.h>
#include "jpeglib.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352