[libjpeg-turbo]读取一张raw图(NV12格式),利用libjpeg-turbo压缩为jpg图像。并统计耗时

笔者在网上找到了很多关于图像压缩库的使用,其中包括libjpeg和libjpeg-turbo,后者据说是在前者的基础上改进了算法,速度提升很大。然而,真正去搜索libjpeg-turbo时,还是会有很多内容是libjpeg的,导致最初误以为是turbo版,结果耗时较大。

最终,折腾了几天,重新改用了turbo的接口,安装了新版本的库,跑出来新的速度确实比libjpeg快不少。

也希望后续用到的朋友能够少走点弯路!

1.encode_by_turbo.h

#include <iostream>
#include <fstream>
#include <cstring>

#include <stdio.h>
#include <stdlib.h>
#include <turbojpeg.h>
int tyuv2jpeg(unsigned char* yuv_buffer, int yuv_size, int width, int height,unsigned char** jpeg_buf, unsigned long* jpeg_size,int quality) ;
void convertNV12toYUV420P(const unsigned char* srcData, unsigned char* dstData, int width, int height);
int read_Image_from_raw_by_iostream(const std::string  filename, unsigned char **buffer,int *yuvsize);

2.encode_by_turbo.cpp

#include "encode_by_turbo.h"


int tyuv2jpeg(unsigned char* yuv_buffer, int yuv_size, int width, int height,unsigned char** jpeg_buf, unsigned long* jpeg_size,int quality)  
{  
    

    tjhandle handle = NULL;  
    int flags = 0;  
    int padding = 1; //must be 1 or 4  
    int need_size = 0;  
    int ret = 0;  
    int subsample=TJSAMP_420;
    
    handle = tjInitCompress();  
    
    flags |= 0;  
  
    need_size = tjBufSizeYUV2(width, padding, height, subsample);  
    if (need_size != yuv_size)  
    {  
        printf("we detect yuv size: %d, but you give: %d, check again.\n", need_size, yuv_size);  
        return 0;  
    }  
  
    ret = tjCompressFromYUV(handle, yuv_buffer, width, padding, height, subsample, jpeg_buf, jpeg_size, quality, flags);  
    if (ret < 0)  
    {  
        printf("compress to jpeg failed: %s\n", tjGetErrorStr());  
    }  
    

    tjDestroy(handle);  
  
    return ret;  
}


void convertNV12toYUV420P(const unsigned char* srcData, unsigned char* dstData, int width, int height)
{
    int frameSize = width * height;
    int qFrameSize = frameSize / 4;

    const unsigned char* srcY = srcData;
    const unsigned char* srcUV = srcData + frameSize;

    unsigned char* dstY = dstData;
    unsigned char* dstU = dstData + frameSize;
    unsigned char* dstV = dstData + frameSize + qFrameSize;

    // Convert Y
    for (int i = 0; i < frameSize; i++)
    {
        *dstY++ = *srcY++;
    }

    // Convert U and V
    for (int i = 0; i < qFrameSize; i++)
    {
        *dstU++ = *srcUV++;
        *dstV++ = *srcUV++;
    }
}


int read_Image_from_raw_by_iostream(const std::string  filename, unsigned char **buffer,int *yuvsize)
{
	   // 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);
    *yuvsize=length;
    return 0;

}

3.main.cpp

#include "encode_by_turbo.h"

#include <algorithm>
#include <chrono>

struct Rawimage_t {
	size_t input_w, input_h;
	unsigned char *yuv420sp;
    unsigned char *yuv420p;
};

int main() 
{
    unsigned char *jpeg_buf = NULL;
    unsigned long jpeg_size = 0;
    int yuv_size=0;
   
    Rawimage_t image;
	  
    image.input_w=3840;
    image.input_h=2176;

    read_Image_from_raw_by_iostream("test.raw",&image.yuv420sp,&yuv_size);

    image.yuv420p = (unsigned char *)malloc(yuv_size+1);

    if(image.yuv420p == NULL)
    {
       printf("lpr_jpeg_save malloc failed\n");
       return -1;
    }

        auto start = std::chrono::high_resolution_clock::now();
        
        convertNV12toYUV420P(image.yuv420sp,image.yuv420p,image.input_w,image.input_h);
        
        auto stop = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
        
        std::cout << "Time taken by : " << duration.count() /1000.0<< " ms" << std::endl;	
        
        start = std::chrono::high_resolution_clock::now();

        tyuv2jpeg(image.yuv420p,yuv_size,image.input_w,
                    image.input_h,&jpeg_buf,&jpeg_size,60); //60:quality
        
        stop = std::chrono::high_resolution_clock::now();
   	    duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
	    
        std::cout << "Time taken by function: " << duration.count() /1000.0<< " ms" << std::endl;	
         
        // 保存压缩后的 JPEG 数据
        FILE* outFile = fopen("output.jpg", "wb");
        fwrite(jpeg_buf, jpeg_size, 1, outFile);
        fclose(outFile);

        free(image.yuv420sp);
        free(image.yuv420p);
   
    }

    free(jpeg_buf);
     
    return 0;
}

4.Makefile

a : main.cpp encode_by_turbo.cpp
	g++ main.cpp encode_by_turbo.cpp -I/opt/libjpeg-turbo/include -L/opt/libjpeg-turbo/lib64 -lturbojpeg

  • 13
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要获取一张yuv420片的数据,可以使用libjpeg-turbo提供的API函数进行操作。以下是获取yuv420片数据的基本步骤: 1. 打开yuv420片文件并读取数据,可以使用标准的文件操作函数(例如fopen、fread等)。 2. 创建libjpeg-turbo解码器对象,可以使用tjInitDecompress函数。此函数返回一个指向解码器对象的指针。 3. 将yuv420片数据解码为RGB格式,可以使用tjDecodeYUV函数。此函数将yuv420数据转换为RGB数据,并将转换后的数据存储在一个指定的缓冲区中。 4. 关闭解码器对象,可以使用tjDestroy函数。此函数释放解码器对象占用的内存。 以下是一个获取yuv420片数据的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <turbojpeg.h> int main(int argc, char **argv) { int width, height, subsample, colorspace; unsigned char *rgb_buffer = NULL; FILE *f = NULL; tjhandle handle = NULL; // 打开yuv420片文件并读取数据 f = fopen("test.yuv", "rb"); if (f == NULL) { printf("Error: failed to open file\n"); return -1; } fseek(f, 0, SEEK_END); long file_size = ftell(f); fseek(f, 0, SEEK_SET); unsigned char *yuv_buffer = (unsigned char *)malloc(file_size); fread(yuv_buffer, file_size, 1, f); fclose(f); // 创建解码器对象 handle = tjInitDecompress(); if (handle == NULL) { printf("Error: failed to create decompressor\n"); goto fail; } // 获取片信息 if (tjDecompressHeader3(handle, yuv_buffer, file_size, &width, &height, &subsample, &colorspace) != 0) { printf("Error: failed to get image info\n"); goto fail; } // 分配RGB缓冲区 rgb_buffer = (unsigned char *)malloc(width * height * TJPF_RGB); if (rgb_buffer == NULL) { printf("Error: failed to allocate RGB buffer\n"); goto fail; } // 将yuv420数据解码为RGB数据 if (tjDecodeYUV(handle, yuv_buffer, file_size, subsample, rgb_buffer, width, 0, height, TJPF_RGB, TJFLAG_FASTDCT) != 0) { printf("Error: failed to decode image\n"); goto fail; } // 关闭解码器对象 tjDestroy(handle); // 在这里处理RGB数据 // 释放内存 free(yuv_buffer); free(rgb_buffer); return 0; fail: if (handle != NULL) { tjDestroy(handle); } if (yuv_buffer != NULL) { free(yuv_buffer); } if (rgb_buffer != NULL) { free(rgb_buffer); } return -1; } ``` 在上面的示例代码中,我们首先打开yuv420片文件并读取数据,然后创建libjpeg-turbo解码器对象。接下来,我们使用tjDecompressHeader3函数获取片信息,并分配RGB缓冲区。最后,我们使用tjDecodeYUV函数将yuv420数据解码为RGB数据,并将其存储在RGB缓冲区中。在解码完成后,我们关闭解码器对象,并释放占用的内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值