//VC++6.0调试通过。但是由于本人最近较忙,所以只做了处理24位位图和32位位图的功能,1、4、8、16位位图从原理上来说是一样的,不过情况稍微复杂一点。你自己解决吧。
//注意,运行时必须要在控制台(cmd窗口)运行,如果直接在编译器那里编译时,是看不到希望的结果的。有问题的话这两天可以百度找我,再过几天就回家了,得明年才回来。
#include
#include
#include
#include
using namespace std;
/*
位图文件分四个部分:
1.文件头
2.文件信息头
3.调色板
4.实际位图数据
//位图文件文件头,2*3+4*2=14字节
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;//文件类型,必须为0x424D,由于WORD类型存放数据是先低字节、再高字节,所以实际上在判断时要用0x4d42(见代码,或者可直接用10进制的数19778),即"BM",'B'=0x42,'M'='0x4d'
DWORD bfSize;//文件大小
WORD bfReserved1;//保留字,必须设置为0,可不考虑,下同
WORD bfReserved2;
DWORD bfOffBits;//文件头到实际位图数据的偏移字节数,即前三部分长度之和
}BITMAPFILEHEADER;
//文件信息头,2*2+4*9=40字节
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;//此结构长度,为40
LONG biWidth;//图像宽度,单位为像素
LONG biHeight;//图像高度,单位为像素
WORD biPlanes;//必须为1
WORD biBitCount;//颜色位数,常用有:1(黑白2色图)、4(16色图)、8(256色图)、24(真彩色图)。新bmp格式还支持32位(增强型真彩色图)
DWORD biCompression;//指定位图是否压缩,有效值:BI_RGB、BI_RLE8、BI_RLE4、BI_BITFIELDS,均为Windows定义好的常量
DWORD biSizeImage;//实际位图数据占用的字节数
LONG biXPelsPerMeter;//目标设备水平分辨率,单位:像素/米
LONG biYPelsPerMeter;//目标设备垂直分辨率,单位:像素/米
DWORD biClrUsed;//图像实际用的到颜色数,如果为0,则实际用到的颜色数为:2的biBitCount次方
DWORD biClrImportant;//重要的颜色数,为0时表示所有颜色都重要
}BITMAPINFOHEADER;
//位图调色板(真彩色不需要),是一个数组,共biClrUsed个元素,若该值为0,则共有2的biBitCount次方个元素。每个元素是一个颜色表结构:
//颜色表结构
typedef struct tagRGBQUAD
{
BYTE rgbBlue; //蓝色分量
BYTE rgbGreen; //绿色分量
BYTE rgbRed; //红色分量
BYTE rgbReserved;//保留值
}RGBQUAD;
*/
class CBmp
{
public:
//读图像文件信息
bool ReadInfo(char FilePath[]);
//写图像灰度信息到文本文件
bool WriteToTxt(char FilePath[]);
CBmp();
virtual ~CBmp();
private:
BITMAPFILEHEADER m_sBMFH; //位图文件头
BITMAPINFOHEADER m_sBMIH; //位图信息头
RGBQUAD * m_pPalette; //调色板
BYTE * m_pBData; //位图数据
};
CBmp::CBmp()
{
m_pPalette = NULL;
m_pBData = NULL;
}
CBmp::~CBmp()
{
if(m_pPalette!=NULL)
{
delete [] m_pPalette;
m_pPalette = NULL;
}
if(m_pBData!=NULL)
{
delete [] m_pBData;
m_pBData = NULL;
}
}
//读图像文件
bool CBmp::ReadInfo(char FilePath[])
{
int nSize; //用来记录调色板的大小
ifstream fin;
fin.open(FilePath,ios::binary);
if(!fin)
{
cerr<<"Open file error!"<
}
//读取文件头
fin.read((char *)(&m_sBMFH),sizeof(BITMAPFILEHEADER));
// 判断是否BMP格式
if(m_sBMFH.bfType!=0x4d42)
{
cout<<"该文件不是BMP格式的文件!"<
}
//读取文件信息头
fin.read((char *)(&m_sBMIH),sizeof(BITMAPINFOHEADER));
//计算调色板的大小
nSize = m_sBMFH.bfOffBits - sizeof(BITMAPFILEHEADER) - sizeof(BITMAPINFOHEADER);
if(nSize!=0)
{
//读取调色板
m_pPalette = new RGBQUAD [nSize/4];
fin.read((char *)(m_pPalette),nSize);
}
//读取图像数据
m_pBData = new BYTE [m_sBMIH.biSizeImage];
fin.read((char *)(m_pBData),m_sBMIH.biSizeImage);
return true;
}
//写图像灰度信息到文本文件
bool CBmp::WriteToTxt(char FilePath[])
{
// 选用的灰度转换公式不同,所得的结果基本上是一致的,但是会稍有差别。
// Gray = (R*38 + G*75 + B*15) >> 7; //高精度转灰度公式
// Gray = R*0.299 + G*0.587 + B*0.114 //使用浮点计算,速度较慢
int k = 0,i,j;
int r,g,b;
int Gray;
BYTE *pt;
ofstream fout;
int t=0;
j = strlen(FilePath);
char *TxtFilePath = new char [j+1]; //记录灰度值的文本文件路径
TxtFilePath[j-3] = 't';
TxtFilePath[j-2] = 'x';
TxtFilePath[j-1] = 't';
TxtFilePath[j] = '\0';
for(i=j-4;i>=0;i--)
TxtFilePath[i] = FilePath[i] ;
switch(m_sBMIH.biBitCount)
{
case 1: //1、4、8位的位图有调色板,要先读取调色板,根据索引找到真实的图像RGB值,再转换。
cout<<"黑白2色(1位)位图的处理暂未实现!"<
case 4:
cout<<"16色(4位)位图的处理暂未实现!"<
case 8:
cout<<"256色(8位)位图的处理暂未实现"<
case 16: //16位位图无调色板,一般为前5位记录R值,中6位记录G值,后5位记录B值
cout<<"16位位图的处理暂未实现"<
case 24:
if(m_sBMIH.biWidth*3%4!=0)t=4-m_sBMIH.biWidth*3%4; //位图数据要求每行字节数必须是4的倍数,不足补齐t
fout.open(TxtFilePath,ios::out);
for(i=m_sBMIH.biHeight-1;i>=0;i--)
{
fout<<"第"<
pt = m_pBData+i*(m_sBMIH.biWidth*3+t)+3*j ;
r = (int)(*pt);
g = (int)(*(pt+1));
b = (int)(*(pt+2));
// Gray = (r*0.299+g*0.587+b*0.114);
Gray = (r*38 + g*75 + b*15) >> 7;
fout<
fout<
fout.close();
break;
case 32:
//每行字节数必是4的倍数,所以不用补齐
fout.open(TxtFilePath,ios::out);
for(i=m_sBMIH.biHeight-1;i>=0;i--)
{
fout<<"第"<
pt = m_pBData+i*(m_sBMIH.biWidth*4+t)+4*j ;
r = (int)(*pt);
g = (int)(*(pt+1));
b = (int)(*(pt+2));
// Gray = (r*0.299+g*0.587+b*0.114);
Gray = (r*38 + g*75 + b*15) >> 7;
fout<
fout<
fout.close();
break;
default:
return false;
}
cout<<"灰度值记录在文件"<
return true;
}
int main(int argc,char *argv[])
{
if(argc !=2 )
{
cout<<"输入参数错误,正确形式:\nyourprogram filePath"<
}
CBmp Bitmap;
if( Bitmap.ReadInfo(argv[1]) )
Bitmap.WriteToTxt(argv[1]);
else
{
cout<<"读取文件出错"<
}
return 1;
}
// 调试通过,1~32位色都可以。
// OutImageData.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
// 从位图文件装入,将位图数据写入文本文件
// 支持调色板位图,和RGB图像数据位图。
// 注:crt 的_tscanf有bug,无法输入正确的中文字符,故工程采用多字节字符集。
void WriteIndexColorBmpData( HBITMAP hbmp, HANDLE hTextFile);
void WriteColorValueBmpData( HBITMAP hbmp, HANDLE hTextFile);
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR the2Para[256];
int nPathlen= 0;
if ( argc> 1)
{
_tcscpy( the2Para, argv[1]);
nPathlen= (int) _tcslen( the2Para);
}
if ( 0== nPathlen)
{
// 在命令行输入bmp路径
_tprintf(_T("<请输入位图文件路径>:>") );
nPathlen= _tscanf( _T("%250s"), the2Para);
}
// 再次判断字符串长度
nPathlen= (int) _tcslen( the2Para);
if ( nPathlen<= 0 )
return 0;
// 开始装入图像文件。
HBITMAP hBmp= ( HBITMAP) LoadImage( NULL, the2Para, IMAGE_BITMAP, 0, 0
, LR_CREATEDIBSECTION| LR_LOADFROMFILE);
if ( NULL== hBmp)
{
MessageBox( NULL, _T("装入文件错误!"), _T("提升"), MB_OK);
return 0;
}
BITMAP bmpinfo;
if ( GetObject( hBmp, sizeof(bmpinfo), &bmpinfo)<= 0 || NULL== bmpinfo.bmBits)
{
DeleteObject( hBmp);
MessageBox( NULL, _T("读取位图文件数据错误!"), _T("提升"), MB_OK);
return 0;
}
// 新txt文件名称(直接追加.txt。还可以使用strchr 搜索.bmp。这儿为了省事)
the2Para[nPathlen++]= '.';
the2Para[nPathlen++]= 't';
the2Para[nPathlen++]= 'x';
the2Para[nPathlen++]= 't';
the2Para[nPathlen]= 0;
HANDLE hTextFile= CreateFile( the2Para, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL, NULL);
if ( NULL== hTextFile)
{
DeleteObject( hBmp);
MessageBox( NULL, _T("新建文本文件出错!"), _T("提示"), MB_OK);
return 0;
}
// 开始向文件写入数据
if ( bmpinfo.bmBitsPixel> 8)
WriteColorValueBmpData( hBmp, hTextFile);
else
WriteIndexColorBmpData( hBmp, hTextFile);
// 关闭对象句柄,返回。
CloseHandle( hTextFile);
DeleteObject( hBmp);
return 0;
}
// 写入带调色板的位图数据。<= 8 BPP
// 一般灰度图像就使用的调色板位图。可以直接写索引ID,也可以写索引的灰度RGB颜色
// 直接使用 GetPixel ,GetNearestPaletteIndex 。虽然速度有点慢,但是代码简单点。
// 也可以直接读取DIB的缓冲区来获取每个像素的索引,在查调色板获取颜色。也一样
void WriteIndexColorBmpData( HBITMAP hbmp, HANDLE hTextFile)
{
BITMAP bmpinfo;
GetObject( hbmp, sizeof(bmpinfo), &bmpinfo);
//
TCHAR writeBuffer[16];
int nwriteLen= 0;
DWORD dwHaveWrite;
TCHAR*newrowStr= _T("\r\n");
// 创建临时dc
HDC hcomdc= GetDC( NULL);
SelectObject( hcomdc, hbmp);
HPALETTE hPal= ( HPALETTE) GetCurrentObject( hcomdc, OBJ_PAL);
for ( int i = 0 ; i < bmpinfo.bmHeight; i ++ )
{
for ( int j = 0 ; j < bmpinfo.bmWidth; j ++ )
{
COLORREF clrValue= GetPixel( hcomdc, j, i);
UINT nColorIndex= GetNearestPaletteIndex( hPal, clrValue);
// 转换为字符串。如果要写灰度数据的索引(1~255),可以转换nColorIndex。
// 不过 GetNearestPaletteIndex 获得的数据不太准确。
// 可以使用类似 WriteColorValueBmpData 函数的方法,直接从图像缓冲区读取
// 每个像素的颜色索引值就可以了。
nwriteLen= _stprintf( writeBuffer, _T("%d "), clrValue);
WriteFile( hTextFile, writeBuffer, nwriteLen, &dwHaveWrite, NULL);
}
// 写换行符号
WriteFile( hTextFile, newrowStr, 2, &dwHaveWrite, NULL);
}
ReleaseDC( NULL, hcomdc);
}
// 写入不带调色板的位图数据。> 8 BPP
// 写入格式为ABGR 的顺序(A最高位)。可以根据需要转换格式。
void WriteColorValueBmpData( HBITMAP hbmp, HANDLE hTextFile)
{
BITMAP bmpinfo;
GetObject( hbmp, sizeof(bmpinfo), &bmpinfo);
int nPixelStep= bmpinfo.bmBitsPixel/ 8;
if ( nPixelStep< 0)
return;
TCHAR writeBuffer[16];
int nwriteLen= 0;
DWORD dwHaveWrite;
TCHAR*newrowStr= _T("\r\n");
BYTE* BmpBufferStart= (BYTE*)bmpinfo.bmBits+ ( bmpinfo.bmHeight-1) * bmpinfo.bmWidthBytes;
for ( int i = 0; i < bmpinfo.bmHeight; i ++ )
{
BYTE* RowBuffer= BmpBufferStart;
for ( int j = 0 ; j < bmpinfo.bmWidth; j ++ )
{
DWORD dwValue= *((DWORD*)RowBuffer);
if ( 2== nPixelStep) // 16 位一般565格式
{
dwValue= dwValue&0x0FFFF;
}
else if ( 3== nPixelStep) // 24 位
{
dwValue= dwValue&0x0FFFFFF;
}
// 转换为字符串
nwriteLen= _stprintf( writeBuffer, _T("%d "), dwValue);
WriteFile( hTextFile, writeBuffer, nwriteLen, &dwHaveWrite, NULL);
// 像素步进
RowBuffer+= nPixelStep;
}
// 行步进
BmpBufferStart-= bmpinfo.bmWidthBytes;
// 写换行符号
WriteFile( hTextFile, newrowStr, 2, &dwHaveWrite, NULL);
}
}
代码:
int Read24Bmp(unsigned char ** pImage, int * iXSize, int * iYSize,int *iLineWidth, char *filename)
{
FILE *fp;
if ( !(fp = fopen(filename, "rb")) )
{
return -1;
}
//读取宽度和高度,颜色数
unsigned char FileHeader[54];
fread(FileHeader, sizeof(unsigned char), 54, fp);
if (FileHeader[0] != 0x42 && FileHeader[1] != 0x4d)
{
printf("not bmp file!\n");
fclose(fp);
return 0;
}
unsigned long lValue = 0;
*iXSize = *iYSize = 0;
//文件大小
unsigned long lFileSize = 0;
lFileSize = FileHeader[2];
(lValue = FileHeader[3]);lValue=lValue<<8;
lFileSize |= (lValue & 0x0000ff00);
(lValue = FileHeader[4]);lValue=lValue<<16;
lFileSize |= (lValue & 0x00ff0000);
(lValue = FileHeader[5]);lValue=lValue<<24;
lFileSize |= (lValue & 0xff000000);
//文件宽度
*iXSize = FileHeader[18];
(lValue = FileHeader[19]);lValue=lValue<<8;
*iXSize |= (lValue & 0x0000ff00);
(lValue = FileHeader[20]);lValue=lValue<<16;
*iXSize |= (lValue & 0x00ff0000);
(lValue = FileHeader[21]);lValue=lValue<<24;
*iXSize |= (lValue & 0xff000000);
//文件高度
*iYSize = FileHeader[22];
(lValue = FileHeader[23]);lValue=lValue<<8;
*iYSize |= (lValue & 0x0000ff00);
(lValue = FileHeader[24]);lValue=lValue<<16;
*iYSize |= (lValue & 0x00ff0000);
(lValue = FileHeader[25]);lValue=lValue<<24;
*iYSize |= (lValue & 0xff000000);
//是否是24位位图
int iBitCounts = 0;
iBitCounts = FileHeader[28];
(lValue = FileHeader[29]);lValue=lValue<<8;
iBitCounts |= (lValue & 0x0000ff00);
if (iBitCounts != 24)
{
printf("only for 24-bit bitmap!\n");
getchar();
return 0;
}
*iLineWidth = lFileSize / (*iYSize);
*pImage = (unsigned char *)malloc(lFileSize);
fread(*pImage, sizeof(unsigned char), lFileSize, fp);
fclose(fp);
return 0;
}
int WriteClr2Txt(unsigned char * pImage, int iXSize, int iYSize, int iLineWidth, char * filename)
{
FILE *fp;
if (!(fp = fopen(filename, "wb")))
return -1;
int i,j,k;
for (i= 0; i < iYSize; i++)
{
for (j = 0; j < iXSize; j++)
{
fprintf(fp, "%d ", pImage[(iYSize-1-i)*iLineWidth + j*3+2]);//r
fprintf(fp, "%d ", pImage[(iYSize-1-i)*iLineWidth + j*3+1]);//g
fprintf(fp, "%d", pImage[(iYSize-1-i)*iLineWidth + j*3+0]); //b
fprintf(fp, "\r\n");
}
}
fclose(fp);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc != 2)
{
printf("only need one parameter!\n");
getchar();
return 0;
}
char * FileName = "C:\\123.bmp";
char * FileName1 = "C:\\123.txt";
unsigned char ** pImage = (unsigned char **)malloc(sizeof(unsigned char *));
int iX = 0;
int iY = 0;
int iLineWidth = 0;
Read24Bmp(pImage, &iX, &iY, &iLineWidth, W2A(argv[1]));
WriteClr2Txt(*pImage, iX,iY, iLineWidth, FileName1);
free(pImage);
return 0;
}