centos7 C++ 使用libjpeg-turbo (讓jpg 轉bmp以及bmp轉jpg)
阿新 • • 發佈:2018-12-13
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