OpenCV中將RGB數組在內存中壓縮成JPEG文件

環境

  1. jpeg庫: jpegsr9a  下載地址:http://www.ijg.org/
  2. 編譯環境: vs2015
  3. Opencv 3.4.2

一、RGB數組來自BMP文件,直接輸出在文件系統上


1.1 代碼

void bmptojpg24(const char *strSourceFileName, constchar *strDestFileName)
{
   BITMAPFILEHEADER bfh;    // bmp文件頭
   BITMAPINFOHEADER bih;    // bmp頭信息
   RGBQUAD rq[256];         // 調色板
   int i=0,j=0;
   int nAdjust;// 用於字節對齊
   int nAdjust24;// 用於字節對齊

   BYTE *data=NULL;//new BYTE[bih.biWidth*bih.biHeight];
   BYTE *pData24= NULL;//newBYTE[bih.biWidth*bih.biHeight];
   int nComponent= 0;

   // 打開圖像文件
  FILE *f= fopen(strSourceFileName,"rb");
   if (f==NULL)
   {
      printf("Open file error!\n");
      return;
   }
   // 讀取文件頭
   fread(&bfh,sizeof(bfh),1,f);
   // 讀取圖像信息
   fread(&bih,sizeof(bih),1,f);
   // 8位字節對齊
   nAdjust = bih.biWidth%4;
   if (nAdjust)nAdjust = 4-nAdjust;
   // 24位字節對齊
   nAdjust24 = bih.biWidth*3%4;
   if (nAdjust24)nAdjust24 = 4-nAdjust24;

   switch (bih.biBitCount)
   {
   case 8:
      if (bfh.bfOffBits-1024<54)
      {
         fclose(f);
         return;
      }

      data= new BYTE[(bih.biWidth+nAdjust)*bih.biHeight];
      pData24 = new BYTE[(bih.biWidth*3+nAdjust24)*bih.biHeight];

      // 定位調色板,並讀取調色板
      fseek(f,bfh.bfOffBits-1024,SEEK_SET);
      fread(rq,sizeof(RGBQUAD),256,f);
      // 讀取位圖
      fread(data,bih.biWidth*bih.biHeight,1,f);
      fclose(f);
      nComponent = 3;
      for (j=0;j<bih.biHeight;j++) {
         for (i=0;i<bih.biWidth;i++)
         {
            pData24[j*(bih.biWidth*3+nAdjust24)+i*3] = rq[data[j*(bih.biWidth+nAdjust)+i]].rgbRed;
            pData24[j*(bih.biWidth*3+nAdjust24)+i*3+1] = rq[data[j*(bih.biWidth+nAdjust)+i]].rgbGreen;
            pData24[j*(bih.biWidth*3+nAdjust24)+i*3+2] = rq[data[j*(bih.biWidth+nAdjust)+i]].rgbBlue;
         }
      }
      break;
   case 24:
      {
         data= new BYTE[(bih.biWidth*3+nAdjust24)*bih.biHeight];
         pData24 = new BYTE[(bih.biWidth*3+nAdjust24)*bih.biHeight];
         fseek(f,bfh.bfOffBits,SEEK_SET); 
         fread(data,(bih.biWidth*3+nAdjust24)*bih.biHeight,1,f);
         fclose(f);
         for (j=0;j<bih.biHeight;j++){
            for (i = 0;i<bih.biWidth;i++)
            {
                pData24[j*(bih.biWidth*3+nAdjust24)+i*3]= data[j*(bih.biWidth*3+nAdjust24)+i*3+2];
                pData24[j*(bih.biWidth*3+nAdjust24)+i*3+1]= data[j*(bih.biWidth*3+nAdjust24)+i*3+1];
                pData24[j*(bih.biWidth*3+nAdjust24)+i*3+2]= data[j*(bih.biWidth*3+nAdjust24)+i*3];
            }
         }
         nComponent = 3;
         break;
      }
   default:
      fclose(f);
      return;
   }

   // 以上圖像讀取完畢
   cout<<"nAdjust24 = "<<nAdjust24<<endl;
   if(IsMemory)
   {
      IMGSTRUCT BMP;
      BMP.Img= pData24;
      BMP.height= bih.biHeight;
      BMP.width= bih.biWidth;
      BMP.nChannel= nComponent;

      //cout<<"pData24  is "<<strlen(pData24)<<endl;
      unsigned char *lpDstBuf = new BYTE[(bih.biWidth*3+nAdjust24)*bih.biHeight];
      memset(lpDstBuf,0,(bih.biWidth*3+nAdjust24)*bih.biHeight);
      unsigned long  dwDstBufSize ;
      int quality= 60;

      //cout<<"lpDstBuf is"<<strlen(lpDstBuf)<<endl;
      WriteJPEGtoMemory(&BMP,lpDstBuf,dwDstBufSize,quality);

      f=fopen(strDestFileName,"wb");
      if (f==NULL)
      {
         delete [] data;
         //delete [] pDataConv;
         return;
      }
      fwrite(lpDstBuf,dwDstBufSize,1,f);
      fclose(f);
      delete [] data;
      delete [] pData24;
      delete [] lpDstBuf;
   }
   else
   {

      struct jpeg_compress_structjcs;
      struct jpeg_error_mgrjem;
      jcs.err= jpeg_std_error(&jem);
      jpeg_create_compress(&jcs);

      f=fopen(strDestFileName,"wb");
      if (f==NULL)
      {
         delete [] data;
         //delete [] pDataConv;
         return;
      }
      jpeg_stdio_dest(&jcs, f);
      jcs.image_width= bih.biWidth;      // 爲圖的寬和高,單位爲像素
      jcs.image_height= bih.biHeight;
      jcs.input_components= nComponent;      // 1,表示灰度圖,如果是彩色位圖,則爲
      if (nComponent==1)
       jcs.in_color_space = JCS_GRAYSCALE;//JCS_GRAYSCALE表示灰度圖,JCS_RGB表示彩色圖像
      else
        jcs.in_color_space = JCS_RGB;

      jpeg_set_defaults(&jcs);
      jpeg_set_quality(&jcs, 60, true);

      jpeg_start_compress(&jcs, TRUE);

      JSAMPROW row_pointer[1];       // 一行位圖
      int row_stride;                // 每一行的字節數

      row_stride = jcs.image_width*nComponent;    // 如果不是索引圖,此處需要乘以
      // 對每一行進行壓縮
      while (jcs.next_scanline < jcs.image_height) {
         row_pointer[0] = & pData24[(jcs.image_height-jcs.next_scanline-1)* (row_stride+nAdjust24)];
         jpeg_write_scanlines(&jcs, row_pointer,1);
      }
      jpeg_finish_compress(&jcs);

      jpeg_destroy_compress(&jcs);
      fclose(f);
      delete [] data;
      delete [] pData24;
   }
}

1.2    注意
代碼中紅色標記的代碼:

由於BMP圖片格式的原因,讀入的RGB顏色順序是BGR形式的,需要調整爲RGB順序:

pData24[j*(bih.biWidth*3+nAdjust24)+i*3]= data[j*(bih.biWidth*3+nAdjust24)+i*3+2];
pData24[j*(bih.biWidth*3+nAdjust24)+i*3+1]= data[j*(bih.biWidth*3+nAdjust24)+i*3+1];
pData24[j*(bih.biWidth*3+nAdjust24)+i*3+2]= data[j*(bih.biWidth*3+nAdjust24)+i*3];

 

由於BMP格式中,圖片原點是存放在右下角的,所以需要將它顛倒爲正序。

row_pointer[0] = & pData24[(jcs.image_height-jcs.next_scanline-1) *(row_stride+nAdjust24)];

二、RGB數組來自opencv,直接輸出在內存


2.1 代碼

//直接將bayer格式的圖片轉化爲RGB順序
cvCvtColor(param.img, dst,CV_BayerBG2RGB);
//申請輸出空間並且初始化
unsigned char  * out = new unsignedchar[size.width*size.height*3];
memset(out,0,size.width*size.height*3);
//JPEG長度
unsigned long  dwDstBufSize ;
//圖像質量
int quality = 95;
// quality = 100,1024*768的RGB圖片壓縮後是500KB左右
// quality = 95,1024*768的RGB圖片壓縮後是200KB左右
IMGSTRUCT BMP;
BMP.Img = (unsigned char*)dst->imageData;
BMP.height =size.height;
BMP.width = size.width;
BMP.nChannel = 3;
//out中存放是壓縮以後jpeg文件的起始地址,dwDstBufSize是文件的長度
WriteJPEGtoMemory(&BMP,out,dwDstBufSize,quality);

// 將pbuf中圖像數據JPEG編碼到內存中, 與WriteJPEG的主要區別:用jpeg_mem_dest()替換jpeg_stdio_dest()
bool WriteJPEGtoMemory(IMGSTRUCT *pbuf,unsigned char *lpDstBuf, unsignedlong & dwDstBufSize,int quality)
{
   if (pbuf== NULL) {  
      // MessageBox(NULL,"WriteJPEGtoMemory() -- 傳入的圖像結構體指針爲NULL!", "錯誤", MB_ICONHAND);
      return FALSE;
   }
   if (pbuf->height <= 0 || pbuf->width <= 0 || pbuf->Img <= NULL){
      //MessageBox(NULL, "WriteJPEGtoMemory()-- 圖像參數有誤!", "錯誤", MB_ICONHAND);
      return FALSE;
   }

   int nbits;
   //定義壓縮信息
   struct jpeg_compress_structcinfo;
   //定義錯誤信息
   struct my_error_mgrjerr;

   //爲JPEG文件壓縮對象分配內存並對其初始化
   cinfo.err= jpeg_std_error(&jerr.pub);
   jerr.pub.error_exit = my_error_exit;
   if (setjmp(jerr.setjmp_buffer)){
      jpeg_destroy_compress(&cinfo);
      return FALSE;
   }
   jpeg_create_compress(&cinfo);

   //確定要用於輸出壓縮的jpeg的數據空間
   jpeg_mem_dest(&cinfo, &lpDstBuf,&dwDstBufSize); 

   //設置壓縮參數
   cinfo.image_width  = pbuf->width;
   cinfo.image_height= pbuf->height;
   if (pbuf->nChannel == 1) {
      cinfo.input_components = nbits= 1; 
      cinfo.in_color_space = JCS_GRAYSCALE;
   }
   else if(pbuf->nChannel== 3) {
      cinfo.input_components = nbits= 3;
      cinfo.in_color_space = JCS_RGB;
   }
   else {
      //MessageBox(NULL,"WriteJPEGFile() -- 傳入圖像結構體顏色通道不正確!", "錯誤", MB_ICONHAND);
      jpeg_destroy_compress(&cinfo);
      return FALSE;
   }


   jpeg_set_defaults(&cinfo);
   jpeg_set_quality(&cinfo, quality,TRUE );
   //開始壓縮
   jpeg_start_compress(&cinfo, TRUE);

   //JSAMPROW outRow[1];
   BYTE * outRow;
   int i= 0;
   //unsigned char *outRow;
   while (cinfo.next_scanline < cinfo.image_height) {
      outRow = pbuf->Img +(cinfo.next_scanline * pbuf->width * nbits);
      (void)jpeg_write_scanlines(&cinfo, &outRow,1);
      i++;
      //cout<<"i = "<<i<<endl;

   }
   cout<<"Memory~"<<endl;
   //完成壓縮
   jpeg_finish_compress(&cinfo);
   //釋放壓縮對象
   jpeg_destroy_compress(&cinfo);

   outRow = NULL;
   return TRUE;
}

2.2    說明
由於這裏是直接調用opencv的函數,將輸入的RGB數組順序調整正確了。所以在壓縮的時候,直接使用

outRow = pbuf->Img + (cinfo.next_scanline* pbuf->width * nbits);

不用在調整順序了。

在內存中取出這張的圖片的時候,可以直接保存成文件。代碼如下:      

f=fopen(strDestFileName,"wb");
      if (f==NULL)
      {
         delete [] data;
         //delete [] pDataConv;
         return;
      }
      fwrite(out,dwDstBufSize,1,f);
      fclose(f);


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章