8位位圖轉換爲24位位圖

8位位圖轉換爲24位位圖

其實,只要從8位位圖中取得一個像素的灰度值,然後去填充24位位圖的三個字節就可以了,很明顯,這樣得到的24位位圖是灰度的,就是RGB各分量是一樣的值。

主要的轉換程序

/******************************************************************************
*函數功能:將8位位圖轉換爲24位位圖
*函數聲明:
   BOOL Bitmap8To24(
    BYTE* srcImage,  -指向源圖像的像素數據的指針
    BYTE* dstImage,  -指向目的圖像的像素數據的指針
    LONG imageWidth, -源圖像的寬度(像素數)
    LONG imageHeight -源圖像的高度(像素數)
    )
******************************************************************************/
BOOL Bitmap8To24(BYTE* srcImage,BYTE* dstImage,LONG imageWidth,LONG imageHeight)
{
 LONG lLineBytes24=((imageWidth*24+31)/32*4);
 LONG lLineBytes8=((imageWidth*8+31)/32*4);
 int n,j;
 for(int i=0;i<imageHeight;i++)
 {
  for(j=0,n=0;j<lLineBytes8;j++,n++)
  {
   BYTE gray=*(srcImage+lLineBytes8*i+j);
   *(dstImage+lLineBytes24*i+n)=gray;
   n++;
   *(dstImage+lLineBytes24*i+n)=gray;
   n++;
   *(dstImage+lLineBytes24*i+n)=gray;
  }
 }
 
 return true;
}

完整的轉換程序

void CICETIMDlg::OnBtnSavegray24bit() 
{
 // TODO: Add your control notification handler code here
 if(m_dib.GetBitCount()==0)
 {
  AfxMessageBox("請先打開位圖");
  return;
 }
 if(m_dib.GetBitCount()==24)
 {
  AfxMessageBox("已經是24位位圖");
  return;
 }
 if(m_dib.GetBitCount()==8)
 {
  CString filename;
  char szFilter[]="位圖文件(*.bmp;*.dib)|*.bmp;*.dib|All Files(*.*)|*.*||";
  CFileDialog dlg(false,"*.bmp",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,szFilter);
  if(dlg.DoModal()==IDOK)
  {
   UpdateData(true);
   filename=dlg.GetPathName();
  }
  else
  {
   return;
  }
  filename.Replace(_T("//"),_T("////"));
  char* file=(char*)filename.GetBuffer(filename.GetLength());
  filename.ReleaseBuffer();
  FILE* fp=fopen(file,"wb");
  if(fp==0)
  {
   AfxMessageBox("保存出錯");
   return;
  }
  BITMAPFILEHEADER bf;
  bf.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
  bf.bfReserved1=0;
  bf.bfReserved2=0;
  bf.bfSize=m_dib.GetHeight()*m_dib.GetWidthBytes(m_dib.GetWidth(),24)+bf.bfOffBits;
  bf.bfType=((WORD)'M'<<8|'B');
  fwrite(&bf,sizeof(BITMAPFILEHEADER),1,fp);
  BITMAPINFOHEADER bi;
  bi.biBitCount=24;
  bi.biClrImportant=0;
  bi.biClrUsed=0;
  bi.biCompression=0L;
  bi.biHeight=m_dib.GetHeight();
  bi.biPlanes=1;
  bi.biSize=sizeof(BITMAPINFOHEADER);
  bi.biSizeImage=m_dib.GetHeight()*m_dib.GetWidthBytes(m_dib.GetWidth(),24);
  bi.biWidth=m_dib.GetWidth();
  bi.biXPelsPerMeter=0;
  bi.biYPelsPerMeter=0;
  fwrite(&bi,sizeof(BITMAPINFOHEADER),1,fp);
  long lLineBytes24=m_dib.GetWidthBytes(m_dib.GetWidth(),24);
  BYTE* image=new BYTE[m_dib.GetHeight()*lLineBytes24];
  if(!Bitmap8To24(m_dib.GetDibData(),image,m_dib.GetWidth(),m_dib.GetHeight()))
  {
   AfxMessageBox("轉換過程錯誤");
   return;
  }
  fwrite(image,m_dib.GetHeight()*lLineBytes24,1,fp);
  fclose(fp);
  //delete[] image;
  AfxMessageBox("保存完畢");
 }
}

24位位圖轉換爲8位位圖

爲什麼要轉換呢?首先,8位的比較容易處理啊,再者我前面的那些輪廓提取、Sobel邊緣檢測、LOG邊緣檢測和閾值分割都是對8位位圖進行的操作,那麼24位位圖就不能處理了嗎?當然能啊,就是可以先把24位位圖轉化位8位位圖就行了。後面會有這方面處理的圖片。

主要的轉換程序

/******************************************************************************
*函數功能:將24位位圖轉換爲8位位圖
*函數聲明:
   BOOL Bitmap24To8(
    BYTE* srcImage,  -指向源圖像的像素數據的指針
    BYTE* dstImage,  -指向目的圖像的像素數據的指針
    LONG imageWidth, -源圖像的寬度
    LONG imageHeight -源圖像的高度
    )
******************************************************************************/
BOOL Bitmap24To8(BYTE* srcImage,BYTE* dstImage,LONG imageWidth,LONG imageHeight)
{
 LONG lLineBytes=((imageWidth*24+31)/32*4);
  LONG lLineBytes8=((imageWidth*8+31)/32*4);
 int n,j;
 for(int i=0;i<imageHeight;i++)
 {
  for(j=0,n=0;j<lLineBytes&&n<lLineBytes8;j++,n++)
  {
   BYTE B=*(srcImage+lLineBytes*i+j);
   j++;
   BYTE G=*(srcImage+lLineBytes*i+j);
   j++;
   BYTE R=*(srcImage+lLineBytes*i+j);
   int y=(B+G+R)/3;
   *(dstImage+lLineBytes8*i+n)=(BYTE)y;
  }
 }
 return true;
}

完整的轉換程序

void CICETIMDlg::OnBtnSavegray8bit() 
{
 // TODO: Add your control notification handler code here
 if(m_dib.GetBitCount()==0)
 {
  AfxMessageBox("請先打開圖片");
  return;
 }
 else if(m_dib.GetBitCount()==8)
 {
  AfxMessageBox("已經是8位位圖");
  return;
 }
 else
 {
  CString filename;
  char szFilter[]="位圖文件(*.bmp;*.dib)|*.bmp;*.dib|All Files(*.*)|*.*||";
  CFileDialog dlg(false,"*.bmp",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,szFilter);
  if(dlg.DoModal()==IDOK)
  {
   UpdateData(true);
   filename=dlg.GetPathName();
  }
  else
  {
   return;
  }
  filename.Replace(_T("//"),_T("////"));
  char* file=(char*)filename.GetBuffer(filename.GetLength());
  filename.ReleaseBuffer();
  FILE* fp=fopen(file,"wb");
  if(fp==0)
  {
   AfxMessageBox("保存出錯");
   return;
  }
  BITMAPFILEHEADER bf;
  bf.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
  bf.bfReserved1=0;
  bf.bfReserved2=0;
  bf.bfSize=m_dib.GetHeight()*m_dib.GetWidthBytes(m_dib.GetWidth(),8)+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
  bf.bfType=((WORD)'M'<<8|'B');
  fwrite(&bf,sizeof(BITMAPFILEHEADER),1,fp);
  BITMAPINFOHEADER bi;
   BITMAPINFO* pInfo=m_dib.GetBitmapInfo();
  bi.biBitCount=8;
  bi.biClrImportant=0;
  bi.biClrUsed=0;
  bi.biCompression=0L;
  bi.biHeight=m_dib.GetHeight();
  bi.biPlanes=1;
  bi.biSize=sizeof(BITMAPINFOHEADER);
  bi.biSizeImage=m_dib.GetHeight()*m_dib.GetWidthBytes(m_dib.GetWidth(),8);
  bi.biWidth=m_dib.GetWidth();
  bi.biXPelsPerMeter=0;
  bi.biYPelsPerMeter=0;
  fwrite(&bi,sizeof(BITMAPINFOHEADER),1,fp);
  fwrite(&(pInfo->bmiColors),sizeof(RGBQUAD),256,fp);
 
  BYTE* image=new BYTE[m_dib.GetHeight()*m_dib.GetWidthBytes(m_dib.GetWidth(),8)];
  if(!Bitmap24To8(m_dib.GetDibData(),image,m_dib.GetWidth(),m_dib.GetHeight()))
  {
   AfxMessageBox("轉換像素過程中出錯");
   return;
  }
  
  fwrite(image,m_dib.GetHeight()*m_dib.GetWidthBytes(m_dib.GetWidth(),8),1,fp);
  fclose(fp);
  delete[] image;
  AfxMessageBox("保存完畢");
 }
}

程序運行時截圖

24位的位圖是這樣的:
下面進行轉換:
這是轉換後電腦上的預覽:
看起來,轉換後好像變換挺大,其實很正常的了,原因你應該會明白的。
這是用程序打開後的圖片:
圖片信息是這樣的:
下面進行輪廓提取:
進行Sobel邊緣檢測:
自適應閾值分割:
固定閾值分割:

打開位圖的程序

//用CFile對象實現打開位圖
BOOL CDib::OpenImage(CString filename)
{
 CFile file;
 file.Open(filename,CFile::modeRead);

 //讀取位圖文件頭
 BITMAPFILEHEADER bmfh;
 int nbmfh=file.Read(&bmfh,sizeof(BITMAPFILEHEADER));
 if(nbmfh!=sizeof(BITMAPFILEHEADER))
 {
  AfxMessageBox("讀取文件頭失敗");
  return false;
 }

 if(bmfh.bfType==0x4d42)
 {
  //計算位圖信息頭的大小
  BITMAPINFOHEADER bmih;
  file.Seek(sizeof(BITMAPFILEHEADER),CFile::begin);
  int nbmih=file.Read(&bmih,sizeof(BITMAPINFOHEADER));
  if(nbmih!=sizeof(BITMAPINFOHEADER))
  {
   AfxMessageBox("讀取位圖信息頭失敗");
   return false;
  }
 
  m_nWidth=bmih.biWidth;
  m_nHeight=bmih.biHeight;
  m_nBitCount=bmih.biBitCount;
  m_lLineBytes=GetWidthBytes(m_nWidth,m_nBitCount);

  file.Seek(bmfh.bfOffBits,CFile::begin);
  //爲像素數據分配空間
  m_lpDibData=new BYTE[m_nHeight*m_lLineBytes];
  DWORD dwdib=file.Read(m_lpDibData,m_nHeight*m_lLineBytes);
  if(dwdib!=static_cast<DWORD>(m_nHeight*m_lLineBytes))
  {
   AfxMessageBox("讀取像素數據失敗");
   return false;
  }

  if(m_nBitCount==8)
  {
   BITMAPINFO* bitmapInfo=(BITMAPINFO*)new char[sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256];
   memcpy(&bitmapInfo->bmiHeader,&bmih,sizeof(BITMAPINFOHEADER));
   if(bmih.biClrUsed!=0)
   {
    for(int i=0;i<(int)bmih.biClrUsed;i++)
    {
     bitmapInfo->bmiColors[i].rgbBlue=i;
     bitmapInfo->bmiColors[i].rgbGreen=i;
     bitmapInfo->bmiColors[i].rgbRed=i;
     bitmapInfo->bmiColors[i].rgbReserved=0;
    }
   }
   else
   {
    for(int i=0;i<256;i++)
    {
     bitmapInfo->bmiColors[i].rgbBlue=i;
     bitmapInfo->bmiColors[i].rgbGreen=i;
     bitmapInfo->bmiColors[i].rgbRed=i;
     bitmapInfo->bmiColors[i].rgbReserved=0;
    }
   }
   m_pBitmapInfo=bitmapInfo;
  } 
  else if(m_nBitCount==24)
  {
   BITMAPINFO* bitmapInfo=(BITMAPINFO*)new char[sizeof(BITMAPINFOHEADER)];
   memcpy(bitmapInfo,&bmih,sizeof(BITMAPINFOHEADER));
   m_pBitmapInfo=bitmapInfo;
  }
 }
 else
 {
  AfxMessageBox("打開的不是位圖文件");
  return false;
 }

 file.Close();
 return true;
}

保存位圖的程序

BOOL CDib::SaveImage(CString filename)
{
 //如果保存時的文件名爲空,則返回
 if(filename=="")
 {
  AfxMessageBox("文件名不能爲空");
  return false;
 }
 
 CFile file;
 file.Open(filename,CFile::modeCreate | CFile::modeWrite | CFile::typeBinary);

 //建立一個位圖文件頭變量,併爲此變量賦值
 BITMAPFILEHEADER bf;
 if(m_nBitCount==8)
 {
  //如果位圖是8位的,則位圖像素數據的偏移地址就爲位圖前三部分的字節數之和
  //即爲:文件頭+信息頭+調色板的大小
  bf.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
 }
 else if(m_nBitCount==24)
 {
  //如果位圖是24位的,則位圖像素數據的偏移地址就爲位圖的前兩部分的字節數之和
  //即爲:文件頭+信息頭
  bf.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
 }
 else
 {
  AfxMessageBox("暫時只能保存8或24位位圖");
  return false;
 }
 //將位圖文件頭中的保留爲都置零
 bf.bfReserved1=0;
 bf.bfReserved2=0;
 if(m_nBitCount==8)
 {
  //如果位圖爲8位的,則位圖文件大小就爲位圖的四部分之和
  //即爲:文件頭+信息頭+調色板+像素數據
  bf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256+m_nHeight*m_lLineBytes;
 }
 else if(m_nBitCount==24)
 {
  //如果位圖爲24位的,則位圖文件大小就爲位圖的三部分之和
  //即爲:文件頭+信息頭+像素數據
  bf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+m_nHeight*m_lLineBytes;
 }
 else
 {
  AfxMessageBox("暫時只能保存8或24位位圖");
  return false;
 }
 //位圖文件的類型爲BM
 bf.bfType=((WORD)'M'<<8|'B');
 //將位圖文件頭寫入文件
 file.Write(&bf,sizeof(BITMAPFILEHEADER));

 //建立位圖信息頭,並準備爲其各項賦值
 BITMAPINFOHEADER bi;
 //將位圖的位數存入信息頭的biBitCount中
 bi.biBitCount=m_nBitCount;
 //將圖像顯示有重要影響的顏色索引的數目,如果是0,表示都重要
 bi.biClrImportant=0;
 //說明位圖實際使用的彩色表中的顏色索引數(設爲0的話,則說明使用所有調色板項)
 bi.biClrUsed=0;
 //目標設備說明位面數,其值總是被設爲1
 bi.biPlanes=1;
 //說明圖像數據壓縮的類型
 bi.biCompression=0L;
 //說明圖像的寬度,以像素爲單位
 bi.biHeight=m_nHeight;
 //說明圖像的寬度,以像素爲單位
 bi.biWidth=m_nWidth;
 //說明BITMAPINFOHEADER結構所需要的字節數
 bi.biSize=sizeof(BITMAPINFOHEADER);
 //說明圖像的大小,以字節爲單位。當用BI_RGB格式時,可設置爲0
 bi.biSizeImage=m_nHeight*m_lLineBytes;
 //說明圖像的水平分辨率,用像素/米來表示
 bi.biXPelsPerMeter=0;
 //說明圖像的垂直分辨率,用像素/米來表示
 bi.biYPelsPerMeter=0;
 //將信息頭寫入文件
 file.Write(&bi,sizeof(BITMAPINFOHEADER));

 if(m_nBitCount==8)
 {
  //如果是8位位圖,還需要寫入調色板
  file.Write(&m_pBitmapInfo->bmiColors,256*sizeof(RGBQUAD));
  //寫入像素數據
  file.Write(m_lpDibData,m_nHeight*m_lLineBytes);
  //關閉文件
  file.Close();
 }
 else if(m_nBitCount==24)
 {
  //寫入像素數據
  file.Write(m_lpDibData,m_nHeight*m_lLineBytes);
  //關閉文件
  file.Close();
 }
 else
 {
  file.Close();
  AfxMessageBox("只能寫入8或24位位圖");
  return false;
 }

 return true;
}

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