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;
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值