使用libjpeg-turbo庫實現(xiàn)無損圖像到j(luò)pg壓縮圖像的內(nèi)存效果轉(zhuǎn)換

0.前言

深度學(xué)習(xí)訓(xùn)練的圖片大多是經(jīng)過壓縮的jpeg圖像,而深度學(xué)習(xí)推理過程面臨的圖像往往是內(nèi)存中的無損圖像,從而造成識別效果上的差異。解決差異的方法之一就是將無損的圖像進行壓縮處理后再進行深度學(xué)習(xí)推理。此處并不是使用JPEG算法實現(xiàn)壓縮過程,而是使用的libjpeg-turbo第三方庫實現(xiàn)的,libjpeg-turbo是一個專門為 x86 和 x86-64 處理器優(yōu)化的高速 libjpeg 的改進版本,實現(xiàn)了libjpeg所有的API并且速度提高了2-6倍。libjpeg是IJG(Independent JPEG Group,一個非正式團體)發(fā)布用于JPEG圖像壓縮的廣泛使用的免費庫,第一版于1991年10月7日發(fā)布。

1.libjpeg-turbo編譯安裝

step1:下載安裝NASM
NASM全稱The Netwide Assembler,是一款基于80x86和x86-64平臺的匯編語言編譯程序,其設(shè)計初衷是為了實現(xiàn)編譯器程序跨平臺和模塊化的特性。編譯libjpeg-turbo時需要這個東西。
下載地址:https://www.nasm.us/index.php
下載對應(yīng)的安裝版本,雙擊安裝即可。
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進行編譯,選好兩個路徑就行,其他都默認(rèn)的。


vs編譯后就會出現(xiàn)lib和bin了,并且也有靜態(tài)庫。后面我用的都是turbojpeg-static.lib這個靜態(tài)庫。

2.參考example.txt實現(xiàn)功能。

其實就是參考了example中的編碼過程和解碼過程,代碼直接復(fù)制過來就能用,我在里面加了一些opencv的操作。另外,自定義的錯誤處理代碼可參考example自行處理。
還有一點比較重要的是使用庫中提供的動態(tài)分配內(nèi)存的函數(shù)jpeg_mem_dest代替example中的jpeg_stdio_dest函數(shù)。
函數(shù)原型:
jpeg_mem_dest(j_compress_ptr cinfo,unsigned char ** outbuffer,size_t * outsize);
參數(shù)說明:
oubuffer:壓縮后的Jpg圖像,由函數(shù)返回,其內(nèi)存是在jpeg_mem_dest()函數(shù)中申請的,所以壓縮完之后需要釋放空間,否則造成內(nèi)存泄露。
outSize:壓縮后圖像的字節(jié)數(shù),由函數(shù)返回。
outbuffer里面存放著壓縮后的數(shù)據(jù),只有jpeg_finish_compress(&cinfo)調(diào)用后outbuffer和outsize才是準(zhǔn)確的數(shù)據(jù)地址及數(shù)據(jù)大小。

#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();//計時結(jié)束
    double cTime = (double)(endTime - startTime) / CLOCKS_PER_SEC;
    cout << "total time is: " << cTime << "s" << endl;
    imshow("out", jpgImg);
    waitKey();
}

3.結(jié)論

一開始使用的是libjpeg,解碼一張大點的圖需要45ms,換成libjpeg-turbo后同樣的圖解碼需要15ms。這和halcon的用時就一樣了。

4.NASM、安裝版和源碼也可以在我的網(wǎng)盤中下載

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容