在网上找了好久都没能找到PPM格式的详细的编码规则, http://wangbingkun.spaces.live.com/blog/cns!E188E2874053C244!215.entry 链接给出了简单的介绍。于是我就利用Matlab读取出一幅PPM格式的图片,然后再用WinEdit读取该图片的内存分布,通过对比发现其主要由以下几个部分组成:
1、文件类型,我所选取的PPM格式是P6,占据前两个字节(第0,1字节),16进制表示为 50 36;
2、图片的像素宽度,与文件类型隔一个字节,如宽度512存储时占据三个字节(第3,4,5字节),16进制表示为35 31 32;
3、图片的像素高度,与宽度也是隔一个字节,表示方式和宽度相同,如果宽度为一个三位数,则高度值从第7字节开始存放,如果宽度为一个二位数,则高度从第6字节开始存放;
4、图片的最大像素值,与高度也是隔一个字节,当然其具体的存放位置和宽度和高度的位数有关系的。
5、图像数据区,与最大像素值隔一个字节,P6类型的是每个像素三个字节,整个图像的扫描顺序是从左到右,由上而下,每个像素内部的三个字节按照BGR顺序排列的。
根据以上的分析,下面给出一段将P6类型的PPM格式转化为BMP格式的代码:
HDIB CDibImage::ReadPPMFile(CFile& file)
{
unsigned char * pHeader=new unsigned char [15];
HDIB hDIB_Des;
LPSTR pDIB_Des;
HDIB hPPM_Src;
LPSTR pPPM_Src;
DWORD dwSrc=file.GetLength(); // 获取源文件的长度
file.Read(pHeader, 15);
if(pHeader[0]!=80||pHeader[1]!=54) //判断是否是P6格式
{
AfxMessageBox("不是P6格式!");
return NULL;
}
int j=0, flag[4]; // 找出主要参数的分隔符的位置
for (int i=2; i<15; i++)
{
if(pHeader[i]>57||pHeader[i]<48)
{
flag[j]=i;
j++;
if(j>3)
break;
}
}
if(flag[0]!=2)
return NULL;
int lenWidth=0, lenHeight=0, maxColorNum=0; //分别得出原始图像的宽度和高度
for (i=1; i<flag[1]-flag[0]; i++)
{
lenWidth=lenWidth*10+pHeader[flag[0]+i]%16;
}
for (i=1; i<flag[2]-flag[1]; i++)
{
lenHeight=lenHeight*10+pHeader[flag[1]+i]%16;
}
for (i=1; i<flag[3]-flag[2]; i++)
{
maxColorNum=maxColorNum*10+pHeader[flag[2]+i]%16;
}
int rowLenDes=WIDTHBYTES(24*lenWidth); //分别给出位图的宽度和高度
int heightLenDes=lenHeight;
int dwData=rowLenDes*heightLenDes; //位图数据区大小
int dwInfo=40;
int dwTotal=dwInfo+dwData; //这里构造的是24位位图
hPPM_Src = (HDIB) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwSrc);
if (hPPM_Src == 0)
{
return NULL;
}
pPPM_Src = (LPSTR) ::GlobalLock((HGLOBAL) hPPM_Src);
DWORD aaa=file.ReadHuge(pPPM_Src, dwSrc-flag[3]-1);
if (aaa!=dwSrc-flag[3]-1) // 读象素
{
::GlobalUnlock((HGLOBAL) hPPM_Src);
::GlobalFree((HGLOBAL) hPPM_Src);
return NULL;
}
hDIB_Des = (HDIB) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwTotal);
if (hDIB_Des == 0)
{
return NULL;
}
pDIB_Des=(LPSTR)GlobalLock((HGLOBAL)hDIB_Des);
BYTE *pDIBBits=(BYTE*)(40+pDIB_Des);
int lenRow=3*lenWidth;
for(i=0; i<lenHeight; i++)
{
for(j=0; j<lenRow; j=j+3)
{
pDIBBits[i*rowLenDes+j]=pPPM_Src[(lenHeight-i)*lenRow+j+2]; //此处考虑到了PPM的BGR的排列顺序
pDIBBits[i*rowLenDes+j+1]=pPPM_Src[(lenHeight-i)*lenRow+j+1];
pDIBBits[i*rowLenDes+j+2]=pPPM_Src[(lenHeight-i)*lenRow+j];
}
if(rowLenDes>lenRow)
{
for(j=lenRow; j<rowLenDes; j++)
{
pDIBBits[i*rowLenDes+j]=0;
}
}
}
((LPBITMAPINFOHEADER)pDIB_Des)->biHeight=heightLenDes;
((LPBITMAPINFOHEADER)pDIB_Des)->biWidth=lenWidth;
((LPBITMAPINFOHEADER)pDIB_Des)->biClrUsed=0;
((LPBITMAPINFOHEADER)pDIB_Des)->biSize=40;
((LPBITMAPINFOHEADER)pDIB_Des)->biBitCount=24;
((LPBITMAPINFOHEADER)pDIB_Des)->biPlanes=1;
((LPBITMAPINFOHEADER)pDIB_Des)->biSizeImage=heightLenDes*lenWidth;
::GlobalUnlock((HGLOBAL) hDIB_Des);
::GlobalUnlock((HGLOBAL) hPPM_Src);
::GlobalFree((HGLOBAL) hPPM_Src);
return hDIB_Des;
}
由于我在实现这一转换的时候不知道PPM的具体的数据格式,仅是凭自己机器中存放的PPM格式的图像得出的规律而实现的,望知道PPM更详细信息的朋友多多指点。