面对图像较大而带宽不足的情况,往往需要对图像进行压缩,使其变小后得以方便传输。
本文使用libjpeg(非libjpeg-turbo)实现了对图像的压缩。
1.encode_by_libjpeg.h
#include <iostream>
#include <fstream>
#include <cstring>
#include <jpeglib.h>
#include <setjmp.h>
int yuv420sp_to_jpeg(const char * filename, unsigned char* pdata,int image_width,int image_height, int quality);
int read_Image_from_raw_by_iostream(const std::string filename, unsigned char **buffer);
2.encode_by_libjpeg.cpp
#include "encode_by_libjpeg.h"
int yuv420sp_to_jpeg(const char * filename, unsigned char* pdata,int image_width,int image_height, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo); /*初始化*/
FILE * outfile; // target file
if ((outfile = fopen(filename, "wb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_dest(&cinfo, outfile); /*指定输出文件*/
/*设置压缩各项图片参数*/
cinfo.image_width = image_width; // image width and height, in pixels
cinfo.image_height = image_height;
cinfo.input_components = 3; // # of color components per pixel
cinfo.in_color_space = JCS_YCbCr; //colorspace of input image
jpeg_set_defaults(&cinfo);
/*设置压缩质量*/
jpeg_set_quality(&cinfo, quality, TRUE );
//
// cinfo.raw_data_in = TRUE;
cinfo.jpeg_color_space = JCS_YCbCr;
cinfo.comp_info[0].h_samp_factor = 2;
cinfo.comp_info[0].v_samp_factor = 2;
jpeg_start_compress(&cinfo, TRUE);
JSAMPROW row_pointer[1];
unsigned char *yuvbuf;
if((yuvbuf=(unsigned char *)malloc(image_width*3))!=NULL)
memset(yuvbuf,0,image_width*3);
unsigned char *ybase,*ubase;
ybase=pdata;
ubase=pdata+image_width*image_height;
int j=0;
while (cinfo.next_scanline < cinfo.image_height)
{
int idx=0;
for(int i=0;i<image_width;i++)
{
yuvbuf[idx++]=ybase[i + j * image_width];
yuvbuf[idx++]=ubase[j/2 * image_width+(i/2)*2];
yuvbuf[idx++]=ubase[j/2 * image_width+(i/2)*2+1];
}
row_pointer[0] = yuvbuf;
/*逐行扫描压缩写入文件*/
jpeg_write_scanlines(&cinfo, row_pointer, 1);
j++;
}
/*完成压缩*/
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(outfile);
return 0;
}
int read_Image_from_raw_by_iostream(const std::string filename, unsigned char **buffer)
{
// open raw data
std::ifstream fin;
// 注意,这里要指定binary读取模式
fin.open(filename, std::ios::binary);
if (!fin) {
std::cerr << "open failed: " << filename << std::endl;
}
// seek函数会把标记移动到输入流的结尾
fin.seekg(0, fin.end);
// tell会告知整个输入流(从开头到标记)的字节数量
int length = fin.tellg();
// 再把标记移动到流的开始位置
fin.seekg(0, fin.beg);
//std::cout << "file length: " << length << std::endl;
// load buffer
char* temp_buf = new char [length];
// read函数读取(拷贝)流中的length各字节到buffer
fin.read(temp_buf, length);
*buffer=reinterpret_cast<u_char *>(temp_buf);
return 0;
}
3.main.cpp
#include "encode_by_libjpeg.h"
#include <iostream>
#include <algorithm>
#include <chrono>
struct Rawimage_t {
size_t input_w, input_h;
unsigned char *yuv420sp;
};
int main()
{
Rawimage_t image;
int yuv_size=0;
int input_width=3840 ;
int input_height=2176;
read_Image_from_raw_by_iostream(raw[i-4], &image.yuv420sp);
auto start = std::chrono::high_resolution_clock::now();
yuv420sp_to_jpeg("output.jpg",image.yuv420sp,input_width,input_height, 60);
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Time taken: " << duration.count() /1000.0<< " ms" << std::endl;
/* Free image memory */
free(image.yuv420sp);
}
return 0;
}
4.Makefile
a : main.cpp encode_by_libjpeg.cpp
g++ main.cpp encode_by_libjpeg.cpp -I/opt/libjpeg-turbo/include -L/opt/libjpeg-turbo/lib64 -ljpeg