您的位置:首页 > 产品设计 > 产品经理

PPM / PGM / PBM 图像文件格式

2013-03-30 20:40 597 查看
下面将详细介绍ppm文件

ppm文件是一种图像文件,有其自己的文件格式。ppm文件由两个部分组成:第一个部分是三行ASCII码,这个部分决定了图像的存储格式以及图像的特征;第二个部分就是图像的数据部分,图像就是由这个部分组成的。

  ppm的第一部分由三行ASCII码组成

第一行是P2/P3/P6

第二行是图像的大小,先是列像素数,后是行像素数,中间有一个空格

第三行是一个介于1和65535之间的整数,而且必须是文本的,用来表示每一个像素的一个分量用几个比特表示。

  三行之后是图像的数据流,从左到右,从上到下。在进行图像数据存储的时候,需要进行数据的格式,假如需要的像素值在0~255之间,那么在进行数据文件保存的时候,所写入文件的值就必须是以%c的形式输入,而且数据之间没有明显的分离字符,图像处理软件会自动地识别这些像素的值,并给予处理。

PPM->Portable PixMap

PGM->Portable GreyMap

PBM->Portable BitMap

PBM支持单色图(1个像素位)

PGM支持灰度图形,能够读PBM图形和PGM图形,输出PGM图形

PPM支持真彩色图形,可以读上面所有格式,输出PPM图形

PPM

  PPM图形文件格式包括两个部分,头部分和图象数据部分。头部分由三部分组成,这三部分由回车或换行分割,但PPM的标准中是要求空格。第一行通常是P3或P6,说明是PPM格式;第二行是图象的宽度和高度,用ASCII来表示;最后一部分是描述像素的最大颜色组成,这里允许描述超过一个字节(0-255)的颜色值。另外可以在上面个部分的后面用#来追加注释,注释行是从#到该行末。

  下面是PPM头的例子:

例子1:

P6 1024 778 255

例子2:

P6

1024 778

255

例子3:

P6#PPM文件格式

1024 778#宽度和高度

# 注释

255

  PPM图象数据的格式依赖于PPM自身的表示,如果是P3格式,数据将以ASCII文本来表示,每个像素的值从0到前面的最大值,每行不应该长于70个字符,如下:

例子4:

P3

# example from the man page

4 4

15

0 0 0 0 0 0 0 0 0 15 0 15

0 0 0 0 15 7 0 0 0 0 0 0

0 0 0 0 0 0 0 15 7 0 0 0

15 0 15 0 0 0 0 0 0 0 0 0

  如果是P6格式,图象数据以字节格式存储,每个色彩成分(R,G,B)一个字节。仅仅在头部的最后一个字段的前面才能有注释,在头部的最后一个字段后面通常是一个回车或换行。P6图象文件比P3文件小,读起来更快。注意,P6文件仅仅用作但字节彩色。

  但并没有按照格式规约的要求来,通常的习惯,图象从上到下,从左到右被存储。每个像素以一个字节来存储,0表示黑色,255表示白色。色彩成分按照通常的红-绿-蓝顺序爱存储。

PGM

  该格式文件存储灰度图形,也就是这里每个像素使用一个值来表示而不是3个(R,G,B)。同PPM唯一不同的是头部用P2和P5,分别表示用ASCII和字节码来表示数据。

例如:

P2

24 7

15

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0

0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0

0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0

0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0

0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

PBM

  使用ASCII的0或1方式来表示数据,0表示白色,1表示黑色。与PPM、PGM不同的头部是少了第三行,因为第三行的最大色彩值在这个模式下已经没有意义了;如下:

P1

# PBM example

24 7

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0

0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0

0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 0

0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0

0 1 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

PPM文件格式分三种:

1. PPM灰度文件

  文件头由3行文本组成,可由fgets读出

  1)第一行为“P2",表示文件类型

  2)第二行为图像的宽度和高度

  3)第三行为最大的象素值255

  接下来是图像数据块。按行顺序存储。每个象素占4个字节,灰度通道为4字节ASCII码表示的整数,高字节在前。左上角为坐标原点。

2. 16位PPM文件(至少适用于读取由DCRAW生成的PPM文件)

  文件头由3行文本组成,可由fgets读出

  1)第一行为“P6",表示文件类型

  2)第二行为图像的宽度和高度

  3)第三行为最大的象素值

  接下来是图像数据块。按行顺序存储。每个象素占3个字节,依次为红绿蓝通道,每个通道为1字节整数。左上角为坐标原点。

3. PPM彩色文件

  文件头由3行文本组成,可由fgets读出

  1)第一行为“P3",表示文件类型

  2)第二行为图像的宽度和高度

  3)第三行为最大的象素值255

  接下来是图像数据块。按行顺序存储。每个象素占12个字节,依次为红绿蓝通道,每个通道为4字节ASCII码表示的整数,高字节在前。左上角为坐标原点。

  可移植像素图格式(PPM),可移植灰度图格式(PGM)和可移植位图格式(PBM)是便于跨平台的图像格式。有时候也被统称为PNM格式。

历史

  PBM格式由Jef Poskanzer在20世纪80年代发明,为了便于通过电子邮件,用ASCII码表示单色位图,能够承受一般的文本格式的变动。

  第一个处理PBM格式的工具库是Pbmplus。它由这个格式的发明人Jef Poskanzer开发,在1988年发布。主要包含Jef编写的将PBM转化为已存在的其他图像格式的工具。在1988年末,Jef开发出PGM、PPM格式以及相关工具,并加入Pbmplus中。Pbmplus的最终发布日期是1991年12月10日。

  在1993年,Netpbm库开始开发,用来替代不再维护的Pbmplus。它是Pbmplus的简单的重新包装,附加全世界开发者提供的额外功能和修订,可能是目前用的最普遍的处理PBM、PGM和PPM格式的工具库。

文件格式描述

  这三种格式在颜色的表示上有差异。PBM是单色,PGM是灰度图,PPM使用RGB颜色。

  每个文件的开头两个字节(ASCII码)作为文件描述子,指出具体格式和编码形式。具体见下表:
文件描述子类型编码
P1
位图ASCII
P2
灰度图ASCII
P3
像素图ASCII
P4
位图二进制
P5
灰度图二进制
P6
像素图二进制
PPM文件的读写源代码:

十里平湖 回复于 2005-09-19 14:12:25

/*pnmfile.h */

#ifndef PNM_FILE_H

#define PNM_FILE_H

#include <cstdlib>

#include <climits>

#include <cstring>

#include <fstream>

#include "image.h"

#include "misc.h"

#include <iostream.h>//for debug,qiansen

#define BUF_SIZE 256

class pnm_error { };

static void read_packed(unsigned char *data, int size, std::ifstream &f) {

unsigned char c = 0;

int bitshift = -1;

for (int pos = 0; pos < size; pos++) {

if (bitshift == -1) {

c = f.get();

bitshift = 7;

}

data[pos] = (c >> bitshift) & 1;

bitshift--;

}

}

static void write_packed(unsigned char *data, int size, std::ofstream &f) {

unsigned char c = 0;

int bitshift = 7;

for (int pos = 0; pos < size; pos++) {

c = c | (data[pos] << bitshift);

bitshift--;

if ((bitshift == -1) || (pos == size-1)) {

f.put(c);

bitshift = 7;

c = 0;

}

}

}

/* read PNM field, skipping comments */

static void pnm_read(std::ifstream &file, char *buf) {

char doc[BUF_SIZE];

char c;

file >> c;

while (c == '#') {

file.getline(doc, BUF_SIZE);

file >> c;

}

file.putback(c);

file.width(BUF_SIZE);

file >> buf;

file.ignore();

}

static image<uchar> *loadPBM(const char *name) {

char buf[BUF_SIZE];

/* read header */

std::ifstream file(name, std::ios::in | std::ios::binary);

pnm_read(file, buf);

if (strncmp(buf, "P4", 2))

throw pnm_error();

pnm_read(file, buf);

int width = atoi(buf);

pnm_read(file, buf);

int height = atoi(buf);

/* read data */

image<uchar> *im = new image<uchar>(width, height);

for (int i = 0; i < height; i++)

read_packed(imPtr(im, 0, i), width, file);

return im;

}

static void savePBM(image<uchar> *im, const char *name) {

int width = im->width();

int height = im->height();

std::ofstream file(name, std::ios::out | std::ios::binary);

file << "P4\n" << width << " " << height << "\n";

for (int i = 0; i < height; i++)

write_packed(imPtr(im, 0, i), width, file);

}

static image<uchar> *loadPGM(const char *name) {

char buf[BUF_SIZE];

/* read header */

std::ifstream file(name, std::ios::in | std::ios::binary);

pnm_read(file, buf);

if (strncmp(buf, "P5", 2))

throw pnm_error();

pnm_read(file, buf);

int width = atoi(buf);

pnm_read(file, buf);

int height = atoi(buf);

pnm_read(file, buf);

if (atoi(buf) > UCHAR_MAX)

throw pnm_error();

/* read data */

image<uchar> *im = new image<uchar>(width, height);

file.read((char *)imPtr(im, 0, 0), width * height * sizeof(uchar));

return im;

}

static void savePGM(image<uchar> *im, const char *name) {

int width = im->width();

int height = im->height();

std::ofstream file(name, std::ios::out | std::ios::binary);

file << "P5\n" << width << " " << height << "\n" << UCHAR_MAX << "\n";

file.write((char *)imPtr(im, 0, 0), width * height * sizeof(uchar));

}

static image<rgb> *loadPPM(const char *name) {

char buf[BUF_SIZE], doc[BUF_SIZE];

/* read header */

std::ifstream file(name, std::ios::in | std::ios::binary);

pnm_read(file, buf);

if (strncmp(buf, "P5", 2)){

//throw pnm_error();

cout<<"pnm version is P6,may be not supported."<<endl;

}

pnm_read(file, buf);

int width = atoi(buf);

pnm_read(file, buf);

int height = atoi(buf);

pnm_read(file, buf);

if (atoi(buf) > UCHAR_MAX)

throw pnm_error();

/* read data */

image<rgb> *im = new image<rgb>(width, height);

file.read((char *)imPtr(im, 0, 0), width * height * sizeof(rgb));

return im;

}

static void savePPM(image<rgb> *im, const char *name) {

int width = im->width();

int height = im->height();

std::ofstream file(name, std::ios::out | std::ios::binary);

file << "P6\n" << width << " " << height << "\n" << UCHAR_MAX << "\n";

file.write((char *)imPtr(im, 0, 0), width * height * sizeof(rgb));

}

template <class T>

void load_image(image<T> **im, const char *name) {

char buf[BUF_SIZE];

/* read header */

std::ifstream file(name, std::ios::in | std::ios::binary);

pnm_read(file, buf);

if (strncmp(buf, "VLIB", 9))

throw pnm_error();

pnm_read(file, buf);

int width = atoi(buf);

pnm_read(file, buf);

int height = atoi(buf);

/* read data */

*im = new image<T>(width, height);

file.read((char *)imPtr((*im), 0, 0), width * height * sizeof(T));

}

template <class T>

void save_image(image<T> *im, const char *name) {

int width = im->width();

int height = im->height();

std::ofstream file(name, std::ios::out | std::ios::binary);

file << "VLIB\n" << width << " " << height << "\n";

file.write((char *)imPtr(im, 0, 0), width * height * sizeof(T));

}

#endif

/* a simple image class

filename: image.h */

#ifndef IMAGE_H

#define IMAGE_H

#include <cstring>

template <class T>

class image {

public:

/* create an image */

image(const int width, const int height, const bool init = true);

/* delete an image */

~image();

/* init an image */

void init(const T &val);

/* copy an image */

image<T> *copy() const;

/* get the width of an image. */

int width() const { return w; }

/* get the height of an image. */

int height() const { return h; }

/* image data. */

T *data;

/* row pointers. */

T **access;

private:

int w, h;

};

/* use imRef to access image data. */

#define imRef(im, x, y) (im->access[y][x])

/* use imPtr to get pointer to image data. */

#define imPtr(im, x, y) &(im->access[y][x])

template <class T>

image<T>::image(const int width, const int height, const bool init) {

w = width;

h = height;

data = new T[w * h]; // allocate space for image data

access = new T*[h]; // allocate space for row pointers

// initialize row pointers

for (int i = 0; i < h; i++)

access[i] = data + (i * w);

if (init)

memset(data, 0, w * h * sizeof(T));

}

template <class T>

image<T>::~image() {

delete [] data;

delete [] access;

}

template <class T>

void image<T>::init(const T &val) {

T *ptr = imPtr(this, 0, 0);

T *end = imPtr(this, w-1, h-1);

while (ptr <= end)

*ptr++ = val;

}

template <class T>

image<T> *image<T>::copy() const {

image<T> *im = new image<T>(w, h, false);

memcpy(im->data, data, w * h * sizeof(T));

return im;

}

#endif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: