1. 程式人生 > >centos7 C++ 使用libjpeg-turbo (讓jpg 轉bmp以及bmp轉jpg)

centos7 C++ 使用libjpeg-turbo (讓jpg 轉bmp以及bmp轉jpg)

    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