当前位置: 首页 > 工具软件 > PPM Doc > 使用案例 >

PPM / PGM / PBM 图像文件格式

司寇嘉茂
2023-12-01

下面将详细介绍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

转载于:https://www.cnblogs.com/hnrainll/archive/2011/06/08/2075629.html

 类似资料: