摘自:http://blog.csdn.net/gamer_gerald/archive/2007/06/08/1643623.aspx
NET framework框架类库中的Image类和Bitmap类提供了常用图像格式的解析读取和存储,这些常用格式包括BMP,JEPG,GIF,PNG,EXIF,TIFF.但是可惜的是不支持PGM格式,我们要自己来处理PGM格式的文件.
1.PGM图像详解
PGM 是便携式灰度图像格式(portable graymap file format),在黑白超声图像系统中经常使用PGM格式的图像.文件的后缀名为".pgm",PGM格式图像格式分为两类:P2和P5类型.不管是P2还是P5类型的PGM文件,都由两部分组成,文件头部分和数据部分.
文件头部分
文件头包括的信息依次是:
1.PGM文件的格式类型(是P2还是P5);
2.图像的宽度;
3.图像的高度;
4.图像灰度值可能的最大值;
文件头的这四部分信息都是以ASCII码形式存储的,所以可以直接在将P2或P5格式的PGM文件在记事本中打开看到文件头的信息.
P5格式的PGM文件在记事本中打开(如下图)
P2格式的PGM文件在记事本中打开(如下图)
在P2或P5类型的PGM文件头的4个信息之间用分割符分开,PGM的合法分隔符包括:空格,TAB,回车符,换行符.PGM文件头的信息应该由合法分割符号分开,如上面两幅图所展.文件头的第4个信息,图像灰度值可能的最大值标明了文件数据部分可能出现的像素灰度值的最大值.上面两幅图都是指定的255,所以在数据区的像素数据取值范围在0到255.
数据部分
数据部分记录图像每个像素的灰度值,按照图像从上到下,从左到右的顺序依次存储每个像素的灰度值.对于像素灰度值的表示P2格式和P5格式有所不同.
P5格式
P5格式的文件,每个像素用可以用二进制表示.比如有一幅P5格式图像,灰度值可能的最大值为255,它的第一行第一列像素值为100,那么该图像每个像素使用一个字节表示,第一行第一列为数值为100的二进制一个字节表示.如果这副图灰度值可能的最大值是65535,那么它的第一行第一列为数值为100的二进制两个字节表示(因为表示到65535需要两个字节).每个像素数据之间没有间隔的连续存储,图像一行信息结束后从下一行第一列继续,两行图像数据之间也没有间隔的连续存储,直到将图像的所有信息表示完.因为是以二进制表示,所以数据部分在记事本中打开后看到的将会是乱码.
P2格式
P2格式的文件,每个像素使用字符串来表示,比如一副P2格式图像,灰度值可能的最大值为255,它的第一行第一列像素值为100,那么该图像图像每个像素使用3个ASCII字符表示,第一行第一列数据为ASII表示的"100".不同于P5格式,每个像素数据之间需要用一个空格符分开存储,在图像的每一行数据结束时需要换行.还有一点需要注意,P2格式文件数据部分当数据超过70个字节的时候,会自动换行.也就是说图像的每一行数据末尾,或者存储时超过70个字节时,需要进行换行.
如下图所示,这是一个P2格式的PGM文件在Visual Studio中查看的效果.我们可以看到,每超过70个自己时候需要换行,在图像数据一行结束时候也需要换行(在0 1 1 1结束后,图像第一行数据结束换行).
2.PGM图像处理方法
在清楚了PGM内部具体结构后,下面就可以对它进行处理了.因为我们实验室经常布置图像处理程序,由于都是超声设备上方面的图像处理,所以都是PGM格式的图像.于是我便将对PGM文件的操作自己写了类来进行实现,我是使用的C#语言来做的.如果有朋友也有这样的需要,欢迎使用我所贴出的代码.
代码如下,代码后面有说明,
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace ImageHomeWork6
... {
public enum PGMType ...{ P2 , P5 , UNKOWN };
public class UnKonwPGMTypeException : Exception
...{ }
public class PGMInformation
...{
private PGMType type = PGMType.UNKOWN;
public PGMType Type
...{
get ...{ return type; }
set ...{ type = value; }
}
private UInt32 width;
public UInt32 Width
...{
get ...{ return width; }
set
...{
width = value;
pixelCount = Convert.ToInt32(width * height);
}
}
private UInt32 height;
public UInt32 Height
...{
get ...{ return height; }
set
...{
height = value;
pixelCount = Convert.ToInt32(width * height);
}
}
private int pixelCount;
public int PixelCount
...{
get ...{ return pixelCount; }
}
private byte grayMax;
public byte GrayMax
...{
get ...{ return grayMax; }
set ...{ grayMax = value; }
}
private byte[] pgmByteData = null;
public byte[] PgmByteData
...{
get ...{ return pgmByteData; }
set ...{ pgmByteData = value; }
}
private string[,] pgmStringData = null;
public string[,] PgmStringData
...{
get ...{ return pgmStringData; }
set ...{ pgmStringData = value; }
}
static private PGMInformation pgmInformation;
static public PGMInformation PGMinformationInstance
...{
get
...{
if (pgmInformation == null)
pgmInformation = new PGMInformation();
return pgmInformation;
}
}
}
public class PgmFileUtil
...{
public PgmFileUtil()
...{
data = PGMInformation.PGMinformationInstance;
}
private FileStream fileStream = null;
private BinaryReader binaryReader = null;
private StreamReader stringReader = null;
private BinaryWriter binaryWriter = null;
private StreamWriter stringWriter = null;
private PGMInformation data;
public PGMInformation Data
...{
get ...{ return data; }
set ...{ data = value; }
}
static private PgmFileUtil instance = null;
static public PgmFileUtil Instance
...{
get
...{
if (instance == null)
instance = new PgmFileUtil();
return instance;
}
}
public void LoadPgmFile(string path)
...{
fileStream = new FileStream(path, FileMode.Open);
stringReader = new StreamReader(fileStream);
string typeHead = stringReader.ReadLine();
string sizeHead = stringReader.ReadLine();
if (typeHead[1] == '5')
...{
data.Type = PGMType.P5;
}
else
...{
if (typeHead[1] == '2')
...{
data.Type = PGMType.P2;
}
else
...{
data.Type = PGMType.UNKOWN;
throw new UnKonwPGMTypeException ();
return;
}
}
int midPosit = 0;
for (int posit = 0; posit < sizeHead.Length; posit++)
...{
if (sizeHead[posit] == ' ')
...{
midPosit = posit;
break;
}
}
if (midPosit == 0)
...{
data.Width = Convert.ToUInt32(sizeHead);
sizeHead = null;
sizeHead = stringReader.ReadLine();
data.Height = Convert.ToUInt32(sizeHead);
}
else
...{
StringBuilder sizeHead2 = new StringBuilder();
for(int posit = 0 ; posit < midPosit ; posit++)
...{
sizeHead2.Append(sizeHead[posit]);
}
data.Width = Convert.ToUInt32(sizeHead2.ToString());
sizeHead2 = new StringBuilder();
for (int posit = midPosit; posit < sizeHead.Length; posit++)
...{
sizeHead2.Append(sizeHead[posit]);
}
data.Height = Convert.ToUInt32(sizeHead2.ToString());
}
string grayMax = stringReader.ReadLine();
data.GrayMax = Convert.ToByte(grayMax);
if (data.Type == PGMType.P5)
...{
binaryReader = new BinaryReader(fileStream);
data.PgmByteData = new byte[data.PixelCount];
binaryReader.Read(data.PgmByteData, 0, data.PixelCount);
stringReader.Close();
binaryReader.Close();
}
if (data.Type == PGMType.P2)
...{
data.PgmStringData = new string[data.Height,data.Width];
data.PgmByteData = new byte[data.PixelCount];
char[] temp0, temp;
int lineLength = 0;
for (int row = 0; row < data.Height; row++)
...{
for (int col = 0; col < data.Width; col++)
...{
if (row == 3&& col == 0x12)
col = col;
temp0 = new char[4];
stringReader.Read(temp0, 0, 4);
lineLength += 4;
int posit = 0;
for (int i = 0; i < 4; i++)
...{
if (temp0[i] == ' ')
...{
posit = i;
break;
}
}
temp = new char[posit];
for (int i = 0; i < posit; i++)
...{
temp[i] = temp0[i];
}
data.PgmStringData[row, col] = new String(temp);
data.PgmByteData[row * data.Width + col] =
Convert.ToByte(data.PgmStringData[row, col]);
if (lineLength >= 70)
...{
stringReader.Read();
lineLength = 0;
if (col == (data.Width - 1))
continue;
}
if (col == (data.Width - 1))
...{
stringReader.Read();
lineLength = 0;
}
}
}
}
fileStream.Close();
}
}
}
类PgmFileUtil是需要来进行PGM图像操作的类,这是一个Singleton的类,直接使用该类的LoadPgmFile方法可以读入P5和P2格式的PGM文件.在PgmFileUtil类里面有一个类型为PGMInformation的Data属性字段.该字段里面含有读入的PGM文件格式(P2,P5或Unkown),图像宽度,图像高度,灰度可能最大值.还有用字节流的图像数据和字符串流的图像数据.
唉,终于写完了,其实真是满简单的东西,希望对大家有帮助.我自己搞忘了的时候,也可以看看,发现自己记忆力也不如以前好了^_^
下周星期六(6月23日)就要考六级了,努力吧,希望这次自己考个好成绩吧,因为我是个好人啊,这也是最后一次考六级了,以后要把时间话到其它的英语考试上了.希望所有的六级考友都可以考到自己期望的分数.
I just believe there is nothing impossible to a willing heart. No pain, no gain......