0.前言
深度學(xué)習(xí)訓(xùn)練的圖片大多是經(jīng)過壓縮的jpeg圖像,而深度學(xué)習(xí)推理過程面臨的圖像往往是內(nèi)存中的無損圖像妒牙,從而造成識別效果上的差異胖笛。解決差異的方法之一就是將無損的圖像進(jìn)行壓縮處理后再進(jìn)行深度學(xué)習(xí)推理巍实。此處并不是使用JPEG算法實(shí)現(xiàn)壓縮過程贺奠,而是使用的libjpeg-turbo第三方庫實(shí)現(xiàn)的,libjpeg-turbo是一個(gè)專門為 x86 和 x86-64 處理器優(yōu)化的高速 libjpeg 的改進(jìn)版本隆嗅,實(shí)現(xiàn)了libjpeg所有的API并且速度提高了2-6倍界阁。libjpeg是IJG(Independent JPEG Group,一個(gè)非正式團(tuán)體)發(fā)布用于JPEG圖像壓縮的廣泛使用的免費(fèi)庫胖喳,第一版于1991年10月7日發(fā)布泡躯。
1.libjpeg-turbo編譯安裝
step1:下載安裝NASM
NASM全稱The Netwide Assembler,是一款基于80x86和x86-64平臺(tái)的匯編語言編譯程序丽焊,其設(shè)計(jì)初衷是為了實(shí)現(xiàn)編譯器程序跨平臺(tái)和模塊化的特性较剃。編譯libjpeg-turbo時(shí)需要這個(gè)東西。
下載地址: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進(jìn)行編譯披泪,選好兩個(gè)路徑就行,其他都默認(rèn)的搬瑰。
vs編譯后就會(huì)出現(xiàn)lib和bin了款票,并且也有靜態(tài)庫控硼。后面我用的都是turbojpeg-static.lib這個(gè)靜態(tài)庫。
2.參考example.txt實(shí)現(xiàn)功能艾少。
其實(shí)就是參考了example中的編碼過程和解碼過程卡乾,代碼直接復(fù)制過來就能用,我在里面加了一些opencv的操作缚够。另外幔妨,自定義的錯(cuò)誤處理代碼可參考example自行處理。
還有一點(diǎn)比較重要的是使用庫中提供的動(dòng)態(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();//計(jì)時(shí)結(jié)束
double cTime = (double)(endTime - startTime) / CLOCKS_PER_SEC;
cout << "total time is: " << cTime << "s" << endl;
imshow("out", jpgImg);
waitKey();
}
3.結(jié)論
一開始使用的是libjpeg,解碼一張大點(diǎn)的圖需要45ms婚温,換成libjpeg-turbo后同樣的圖解碼需要15ms描焰。這和halcon的用時(shí)就一樣了。