centos7 C++ 使用libjpeg-turbo (让jpg 转bmp以及bmp转jpg)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sz76211822/article/details/84338753

    libjpeg-turbo这个库比libjpeg强大一些,libjpeg解析完bmp以后,数据格式是RGB,如果再保存为BGR就比较麻烦,而libjpeg-turbo保存为BGR格式的时候有JCS_EXT_BGR这个参数,所以保存bmp图片就方便很多了。

    解析为bmp时,bmp需要4字节对齐,这里我的做法是,假如一个jpg的tup宽度是157,我这边是将图片扩展为160个像素。这里支持从本地文件加载jpg后解析为bmp,也可以从内存解析为bmp,同时支持将bmp压缩为jpg

  

   封装头文件:

   

#ifndef __PARSE_JPEG__
#define __PARSE_JPEG__

#include <stdio.h>
#include <string.h>
#include<sys/types.h>

extern "C" {
    #include "include/jpeglib.h"
}

class ParseJpeg
{//不使用单例是为了支持多线程操作
public:
    ParseJpeg();
    ~ParseJpeg();

public:
    unsigned char* Parse(const char* pstrImageName, bool bHasHeader/*是否只要bmp数据区*/, long& nLen/*返回bmp图像的长度*/, long& nWidth, long& nHeight);
    unsigned char* Parse(const char* pstrImageData, long nImageSize, bool bHasHeader/*是否只要bmp数据区*/, long& nLen/*返回bmp图像的长度*/, long& nWidth, long& nHeight);
    bool CompressBMPToJPG(const char *pstrFilename, unsigned char *bits, int nWidth, int nHeight, int nDepth, int nQuality);

private:
    void analyse_jpeg(const char* pstrImageName, long& nWidth, long& nHeight);//从本地文件夹加载JPG并解析为BMP
    void analyse_jpeg(const char* pstrImageData, long nImageSize, long& nWidth, long& nHeight);//从内存直接解析JPG文件
    void write_bmp_header(long nWidth, long nHeight, long nDepth);
    void write_bmp_data(j_decompress_ptr cinfo, unsigned char *src_buff);

private:
    bool m_bHasHeader;//是否输出bmp图片文件头
    long m_nLenHeader;
    unsigned char* m_pJpgBuffer;
    unsigned char* m_pLineBuffer;
    unsigned char* m_pImageBuffer;
};


#endif
#include "ParseJpeg.h"
#define MAX_IMAGE_SIZE  20 * 1024 * 1024 //针对1920*1080的图像

#pragma pack(2)
typedef struct BITMAPFILEHEADER {
    u_int16_t bfType;
    u_int32_t bfSize;
    u_int16_t bfReserved1;
    u_int16_t bfReserved2;
    u_int32_t bfOffBits;
} BITMAPFILEHEADER;

typedef struct BITMAPINFOHEADER {
    u_int32_t biSize;
    u_int32_t biWidth;
    u_int32_t biHeight;
    u_int16_t biPlanes;
    u_int16_t biBitCount;
    u_int32_t biCompression;
    u_int32_t biSizeImage;
    u_int32_t biXPelsPerMeter;
    u_int32_t biYPelsPerMeter;
    u_int32_t biClrUsed;
    u_int32_t biClrImportant;
} BITMAPINFODEADER;


ParseJpeg::ParseJpeg()
{//主要是为了防止频繁分配内存
    m_bHasHeader = false;
    m_nLenHeader = 0;

    m_pJpgBuffer = NULL;
    while(!m_pJpgBuffer){
        try{
            m_pJpgBuffer = new unsigned char[MAX_IMAGE_SIZE];
        }
        catch(...){}
    }

    m_pLineBuffer = NULL;
    while(!m_pLineBuffer){
        try{
            m_pLineBuffer = new unsigned char[MAX_IMAGE_SIZE];
        }
        catch(...){}
    }

    m_pImageBuffer = NULL;
    while(!m_pImageBuffer){
        try{
            m_pImageBuffer = new unsigned char[MAX_IMAGE_SIZE];
        }
        catch(...){}
    }
}
ParseJpeg::~ParseJpeg()
{
    if(m_pJpgBuffer){
        delete[] m_pJpgBuffer;
        m_pJpgBuffer = NULL;
    }
    if(m_pLineBuffer){
        delete[] m_pLineBuffer;
        m_pLineBuffer = NULL;
    }
    if(m_pImageBuffer){
        delete[] m_pImageBuffer;
        m_pImageBuffer = NULL;
    }
}

void ParseJpeg::analyse_jpeg(const char* pstrImageName, long& nWidth, long& nHeight)
{
    FILE* pFile = fopen(pstrImageName, "rb+");
    if(pFile){
        struct jpeg_decompress_struct cinfo;
        struct jpeg_error_mgr jerr;

        cinfo.err = jpeg_std_error(&jerr);    //一下为libjpeg函数,具体参看相关文档
        jpeg_create_decompress(&cinfo);
        jpeg_stdio_src(&cinfo, pFile);
        jpeg_read_header(&cinfo, TRUE);
        jpeg_start_decompress(&cinfo);

        unsigned long width = cinfo.output_width;
        unsigned long height = cinfo.output_height;
        unsigned short depth = cinfo.output_components;
        if(width % 4 != 0){
            nWidth = width + 4 - width % 4;
        }
        else{
            nWidth = width;
        }
        nHeight = height;
        JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, width * depth, 1);

        unsigned char *point = m_pJpgBuffer;
        while (cinfo.output_scanline < height) {
            jpeg_read_scanlines(&cinfo, buffer, 1);//读取一行jpg图像数据到buffer
            memcpy(point, *buffer, width * depth);//将buffer中的数据逐行给src_buff
            point += width*depth;//一次改变一行
        }

        if(m_bHasHeader){
            write_bmp_header(nWidth, nHeight, depth);//写bmp文件头
        }
        write_bmp_data(&cinfo, m_pJpgBuffer);//写bmp像素数据

        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);

        fclose(pFile);
    }
}
void ParseJpeg::analyse_jpeg(const char* pstrImageData, long nImageSize, long& nWidth, long& nHeight)
{//从内存直接解析JPG文件
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);    //一下为libjpeg函数,具体参看相关文档
    jpeg_create_decompress(&cinfo);
    jpeg_mem_src(&cinfo, (unsigned char*)pstrImageData, nImageSize);
    jpeg_read_header(&cinfo, TRUE);
    jpeg_start_decompress(&cinfo);

    unsigned long width = cinfo.output_width;
    unsigned long height = cinfo.output_height;
    unsigned short depth = cinfo.output_components;

    if(width % 4 != 0){
        nWidth = width + 4 - width % 4;
    }
    else{
        nWidth = width;
    }
    
    nHeight = height;
    JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, width * depth, 1);

    unsigned char *point = m_pJpgBuffer;
    while (cinfo.output_scanline < height) {
        jpeg_read_scanlines(&cinfo, buffer, 1);//读取一行jpg图像数据到buffer
        memcpy(point, *buffer, width * depth);//将buffer中的数据逐行给src_buff
        point += width*depth;//一次改变一行
    }

    if(m_bHasHeader){
        write_bmp_header(nWidth, nHeight, depth);//写bmp文件头
    }
    write_bmp_data(&cinfo, m_pJpgBuffer);//写bmp像素数据

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
}
void ParseJpeg::write_bmp_header(long nWidth, long nHeight, long nDepth)
{
    struct BITMAPFILEHEADER bfh;
    struct BITMAPINFOHEADER bih;

    unsigned long headersize = 0;
    unsigned long filesize = 0;

    if (nDepth == 1) {
        headersize = 14 + 40 + 256 * 4;
        filesize = headersize + nWidth * nHeight;
    }

    if (nDepth == 3) {
        headersize = 14 + 40;
        filesize = headersize + nWidth * nHeight * nDepth;
    }

    memset(&bfh, 0, sizeof(struct BITMAPFILEHEADER));
    memset(&bih, 0, sizeof(struct BITMAPINFOHEADER));

    //写入比较关键的几个bmp头参数
    bfh.bfType = 0x4D42;
    bfh.bfSize = filesize;
    bfh.bfOffBits = headersize;

    bih.biSize = 40;
    bih.biWidth = nWidth;
    bih.biHeight = nHeight;
    bih.biPlanes = 1;
    bih.biBitCount = (unsigned short)nDepth * 8;
    bih.biSizeImage = nWidth * nHeight * nDepth;

    memcpy(m_pImageBuffer, &bfh, sizeof(struct BITMAPFILEHEADER));
    memcpy(m_pImageBuffer + sizeof(struct BITMAPFILEHEADER), &bih, sizeof(struct BITMAPINFOHEADER));
    if (nDepth == 1) {//灰度图像要添加调色板
        unsigned char *platte = NULL;
        while(!platte){
            try{
                platte = new unsigned char[256*4];
            }
            catch(...){}
        }

        unsigned char j = 0;
        for (int i = 0; i < 1024; i += 4) {
            platte[i] = j;
            platte[i+1] = j;
            platte[i+2] = j;
            platte[i+3] = 0;
            j++;
        }
        memcpy(m_pImageBuffer + sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER), platte, sizeof(unsigned char) * 1024);
        m_nLenHeader = sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER) + sizeof(unsigned char) * 1024;
        delete[] platte;
    }
    else{
        m_nLenHeader = sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER);
    }
}

void ParseJpeg::write_bmp_data(j_decompress_ptr cinfo, unsigned char *src_buff)
{
    unsigned long width = cinfo->output_width;
    unsigned long height = cinfo->output_height;
    unsigned short depth = cinfo->output_components;

    unsigned char *point = src_buff + width * depth * (height - 1);    //倒着写数据,bmp格式是倒的,jpg是正的
    for (unsigned long i = 0; i < height; i++) {
        for (unsigned long j = 0; j < width * depth; j += depth) {
            if (depth == 1) {//处理灰度图
                m_pLineBuffer[j] = point[j];
            }

            if (depth == 3) {//处理彩色图
                m_pLineBuffer[j + 2] = point[j + 0];
                m_pLineBuffer[j + 1] = point[j + 1];
                m_pLineBuffer[j + 0] = point[j + 2];
            }
        }
        point -= width * depth;


        memcpy(m_pImageBuffer + m_nLenHeader, m_pLineBuffer, sizeof(unsigned char) * width * depth);
        m_nLenHeader += sizeof(unsigned char) * width * depth;
        if(width % 4 != 0){
            long nOffset = 4 - width % 4;//本人添加,当bmp图片宽度不以4字节对齐的时候,会出现图像倾斜、模糊等状况
            for(int i = 0; i < nOffset * depth; i++){
                m_pImageBuffer[m_nLenHeader++] = 0x00;
            }
        }
    }
}


unsigned char* ParseJpeg::Parse(const char* pstrImageName, bool bHasHeader, long& nLen, long& nWidth, long& nHeight)
{
    m_bHasHeader = bHasHeader;
    m_nLenHeader = 0;
    memset(m_pJpgBuffer, 0, MAX_IMAGE_SIZE);
    memset(m_pLineBuffer, 0, MAX_IMAGE_SIZE);
    memset(m_pImageBuffer, 0, MAX_IMAGE_SIZE);
    analyse_jpeg(pstrImageName, nWidth, nHeight);
    nLen = m_nLenHeader;
    return m_pImageBuffer;
}

unsigned char* ParseJpeg::Parse(const char* pstrImageData, long nImageSize, bool bHasHeader, long& nLen/*返回bmp图像的长度*/, long& nWidth, long& nHeight)
{
    m_bHasHeader = bHasHeader;
    m_nLenHeader = 0;
    memset(m_pJpgBuffer, 0, MAX_IMAGE_SIZE);
    memset(m_pLineBuffer, 0, MAX_IMAGE_SIZE);
    memset(m_pImageBuffer, 0, MAX_IMAGE_SIZE);
    analyse_jpeg(pstrImageData, nImageSize, nWidth, nHeight);
    nLen = m_nLenHeader;
    return m_pImageBuffer;
}

bool ParseJpeg::CompressBMPToJPG(const char *pstrFilename, unsigned char *bits, int nWidth, int nHeight, int nDepth, int nQuality)
{
    FILE * pFile = fopen(pstrFilename, "wb+");
    if (pFile){
        struct jpeg_compress_struct cinfo;
        struct jpeg_error_mgr jerr;
        JSAMPROW row_pointer[1];        //pointer to JSAMPLE row[s]

        cinfo.err = jpeg_std_error(&jerr);
        jpeg_create_compress(&cinfo);
        jpeg_stdio_dest(&cinfo, pFile);
        cinfo.image_width = nWidth;      //image width and height, in pixels
        cinfo.image_height = nHeight;
        cinfo.input_components = 3;         //# of color components per pixel
        cinfo.in_color_space = JCS_EXT_BGR;         //colorspace of input image
        jpeg_set_defaults(&cinfo);
        jpeg_set_quality(&cinfo, nQuality, TRUE);//limit to baseline-JPEG values
        jpeg_start_compress(&cinfo, TRUE);

        int row_stride = nWidth * nDepth; // JSAMPLEs per row in image_buffer
        while (cinfo.next_scanline < cinfo.image_height)
        {
            //这里我做过修改,由于jpg文件的图像是倒的,所以改了一下读的顺序
            row_pointer[0] = &bits[(cinfo.image_height - cinfo.next_scanline - 1) * row_stride];
            (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
        }
        jpeg_finish_compress(&cinfo);
        fclose(pFile);
        jpeg_destroy_compress(&cinfo);
        return true;
    }

    return false;
}

makefile:

INCLUDE_Jpeg=./include
LIBRARY_Jpeg=./lib/libjpeg.a

libChlFaceSdk.so:test.o ParseJpeg.o
	g++ -fPIC -lz -lm -lc -pthread test.o ParseJpeg.o -I$(INCLUDE_Jpeg) $(LIBRARY_Jpeg)   -o ./test 

ParseJpeg.o:ParseJpeg.h ParseJpeg.cpp
	g++ -c ParseJpeg.cpp -fPIC -o ParseJpeg.o

test.o:test.cpp
	g++ -c test.cpp -fPIC -o test.o

clean:
	rm test.o ParseJpeg.o

下载地址:https://download.csdn.net/download/sz76211822/10800690

猜你喜欢

转载自blog.csdn.net/sz76211822/article/details/84338753