生成图像格式:PPM

PPM图片格式介绍:

Netpbm format is any graphics format used and defined by the Netpbm project. The portable pixmap format (PPM), the portable graymap format (PGM) and the portable bitmap format (PBM) are image file formats designed to be easily exchanged between platforms. They are also sometimes referred to collectively as the portable anymap format (PNM),[4][5] not to be confused with the related portable arbitrary map format.

History[edit]

The PBM format was invented by Jef Poskanzer in the 1980s as a format that allowed monochrome bitmaps to be transmitted within an email message as plain ASCII text, allowing it to survive any changes in text formatting.[5] Poskanzer developed the first library of tools to handle the PBM format, Pbmplus, released in 1988. It mainly contained tools to convert between PBM and other graphics formats. By the end of 1988, Poskanzer had developed the PGM and PPM formats along with their associated tools and added them to Pbmplus. The final release of Pbmplus was December 10, 1991.

In 1993, the Netpbm library was developed to replace the unmaintained Pbmplus. It was simply a repackaging of Pbmplus with additions and fixes submitted by people all over the world.[6]

File format description[edit]

Each file starts with a two-byte magic number (in ASCII) that identifies the type of file it is (PBM, PGM, and PPM) and its encoding (ASCII or binary). The magic number is a capital P followed by a single-digit number.

Type Magic number Extension Colors
ASCII Binary
Portable BitMap[1] P1 P4 .pbm 0–1 (white & black)
Portable GrayMap[2] P2 P5 .pgm 0–255 (gray scale)
Portable PixMap[3] P3 P6 .ppm 0–255 (RGB)

A value of P7 refers to the PAM file format that is covered as well by the netpbm library.[7]

The ASCII formats allow for human readability and easy transfer to other platforms; the binary formats are more efficient in file size but may have native byte-order issues.

In the binary formats, PBM uses 1 bit per pixel, PGM uses 8 bits per pixel, and PPM uses 24 bits per pixel: 8 for red, 8 for green, 8 for blue.

PBM example[edit]

A simple example of the PBM format is as follows (there is a newline character at the end of each line):

P1
# This is an example bitmap of the letter "J"
6 10
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 0
0 0 0 0 1 0
1 0 0 0 1 0
0 1 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0

The string P1 identifies the file format. The number sign introduces a comment. The next two numbers give the width and the height. Then follows the matrix with the pixel values (in the monochrome case here, only zeros and ones).

Here is the resulting image: C语言编写简单图像程序-圆

Here it is again magnified 20 times: C语言编写简单图像程序-圆

Note that a 0 signifies a white pixel, and a 1 signifies a black pixel. This is in contrast to the other formats, where higher values signify brighter pixels.

The P4 binary format of the same image represents each pixel with a single bit, packing 8 pixels per byte, with the first pixel as the most significant bit. Extra bits are added at the end of each row to fill a whole byte.

PGM example[edit]

The PGM and PPM formats (both ASCII and binary versions) have an additional parameter for the maximum value (numbers of grey between black and white) after the X and Y dimensions and before the actual pixel data. Black is 0 and max value is white. There is a newline character at the end of each line.

C语言编写简单图像程序-圆
Example (magnified)
P2
# Shows the word "FEEP" (example from Netpbm man page on PGM)
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

PPM example[edit]

This is an example of a color RGB image stored in PPM format. There is a newline character at the end of each line.

C语言编写简单图像程序-圆
Image (magnified)
P3
3 2
255
# The part above is the header
# "P3" means this is a RGB color image in ASCII
# "3 2" is the width and height of the image in pixels
# "255" is the maximum value for each color
# The part below is image data: RGB triplets
255   0   0     0 255   0     0   0 255
255 255   0   255 255 255     0   0   0

The P6 binary format of the same image represents each color component of each pixel with one byte (thus three bytes per pixel) in the order red, green, then blue. The file is smaller, but the color information is difficult to read by humans.

The PPM format is not compressed, and thus requires more space and bandwidth than a compressed format would. For example, the above 192×128 PNG (Portable Network Graphics) image has a file size of 166 bytes. When converted to a 192×128 PPM image, the file size is 73,848 bytes. The PPM format is generally an intermediate format used for image work before converting to a more efficient format, for example the PNG format, without any loss of information in the intermediate step.

The image shown above using only 0 or the maximal value for the red-green-blue channels can be also encoded as:

P6
# The same image with width 3 and height 2,
# using 0 or 1 per color (red, green, blue)
3 2 1
1 0 0   0 1 0   0 0 1
1 1 0   1 1 1   0 0 0

White space including line ends and comment lines is syntactically equivalent to a single space within the PNM headers. For the plain formats P1…P3 this also affects the pixmap lines; in fact lines should be limited to 70 characters:

P6 3 2 1  1 0 0   0 1 0   0 0 1  1 1 0   1 1 1   0 0 0

16-bit extensions[edit]

The original definition of the PGM and the PPM binary formats (the P5 and P6 formats) did not allow bit depths greater than 8 bits. One can of course use the ASCII format, but this format both slows down reading and makes the files much larger. Accordingly, many programmers extended the format to allow higher bit depths. Using higher bit depths encounters the problem of having to decide on the endianness of the file. The various implementations could not agree on which byte order to use, and some connected the 16-bit endianness to the pixel packing order.[8] In Netpbm, the de facto standard implementation of the PNM formats, the most significant byte is first.[9]

Common RGB color depths include 24 = (3 × 8), 30 = (3 × 10), 32 = (4 × 8), 36 = (3 × 12), 48 = (3 × 16), and 64 = (4 × 16) (with three red–green–blue channels, and a fourth unused or RGBA alpha channel, respectively). Classic XnView can read rgb48bePPM, FFmpeg can also create rgb48be PPM.[10]

While 30 = (3 × 10) could fit into 32 bits, this is not supported by the binary PNM and PAM formats. All PPM bit depths with more than 8 bits are encoded in 48 bits. More than 8 gray bits end up as 16 bits without transparency (FFmpeg PGM or PAM pix_fmt gray16be).

PGMYUV is a PGM variant only used by FFmpeg.[citation needed]


PPM文件组成:

PPM(Portable PixMap)是portable像素图片,是有netpbm项目定义的一系列的portable图片格式中的一个。这些图片格式都相对比较容易处理,跟平台无关,所以称之为portable,简单理解,就是比较直接的图片格式,比如PPM,其实就是把每一个点的RGB分别保存起来。所以,PPM格式的文件是没有压缩的,相对比较大,但是由于图片格式简单,一般作为图片处理的中间文件(不会丢失文件信息),或者作为简单的图片格式保存。

2. PPM格式分析

netpbm的几种图片格式是通过其表示的颜色类型来区别的,PBM是位图,只有黑色和白色,PGM是灰度图片,PPM是代表完整的RGB颜色的图片。

(1) 文件头

文件头由三个部分(或者认为是四个部分)组成:这几个部分之间用回车或换行分隔(但是PPM标准中要求是空格)

第一部分是文件magic number:

每一个netpbm图片由两个字节的magic number 组成,来标识文件的类型(PMB/PGM/PPM)以及文件的编码(ASCII或binary).

C语言编写简单图像程序-圆

所以PPM格式的起始两个字节为P3或者P6.

关于编码(ASCII或binary): 其区别是ASCII编码的文件是对于阅读友好的,可以字节用文本编辑器打开,并读取其对应的图片的数据(比如RGB的值),然后中间会有空格回车等隔开。binary就是按照二进制的形式,顺序存储图片信息,没有空格回车分隔。所以很显然,binary格式的图片处理起来更快(不需要判断空格回车),而且图片会更小,但是ASCII阅读调试更为直接。

第二部分是图像宽度和高度(空格隔开),用ASCII表示。

第三部分是描述像素的最大颜色组成,允许描述超过一个字节(0-255)的颜色值。

另外,在上面的三个部分里面,都可以使用"#"插入注释,注释是#到行尾(回车或换行)部分。

(2) 图像数据部分

对于ASCII格式,就是按照RGB的顺序排列,以ASCII存储,并且,RGB中间用空格隔开,图片每一行用回车隔开。

对于binary格式,就是每一个像素点的RGB值分别顺序存储并且按二进制写入文件(fwrite),没有任何分隔。

比如下面这个图片 (一共六个像素点,图片宽度为3,高度为2):

C语言编写简单图像程序-圆

代码如下:

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. int writePPMHeader(FILE *f, char magic, int w, int h, int color) {  
  5.     if (f==NULL) {  
  6.         printf("FILE error\n");  
  7.         exit(0);  
  8.     }  
  9.   
  10.     if (magic=='A') {// ASCII  
  11.         fprintf(f, "P3\n");  
  12.     } else if (magic=='B') {  
  13.         fprintf(f, "P6\n");  
  14.     } else {  
  15.         printf("Magic can only be A(ASCII) or B(binary)\n");  
  16.         exit(0);  
  17.     }  
  18.   
  19.     fprintf(f, "%d %d\n", w, h);  
  20.     fprintf(f, "%d\n", color);  
  21.     return 0;  
  22. }  
  23.   
  24. int writePPMdataP3(FILE* f, unsigned char* img, int w, int h) {  
  25.     int i,j;  
  26.     for(i=0;i<h;i++) {   // every rwo  
  27.         for(j=0;j<w;j++) {   // every line  
  28.             fprintf(f, "%d ",img[i*w*3+3*j]);  
  29.             fprintf(f, "%d ",img[i*w*3+3*j+1]);  
  30.             fprintf(f, "%d ",img[i*w*3+3*j+2]);<span style="white-space:pre">   </span>// PS: 对于j=w-1的时候,最后一个空格可以不写,但是这里就不考虑了  
  31.         }  
  32.         fprintf(f, "\n");  
  33.     }  
  34. }  
  35.   
  36. int writePPMdataP6(FILE* f, unsigned char* img, int w, int h) {  
  37.     int i,j;  
  38.     for(i=0;i<w;i++) {  
  39.         for(j=0;j<h;j++) {  
  40.             fwrite(img, w*h, 3, f);  
  41.         }  
  42.     }  
  43. }  
  44.   
  45. #define WIDTH   3  
  46. #define HEIGHT  2  
  47. unsigned char img[WIDTH*HEIGHT*3]={255,0,0,0,255,0,0,0,255,255,255,0,255,255,255,0,0,0};  
  48.   
  49. int main() {  
  50.     char *filename1 = "testP3.ppm";  
  51.     char *filename2 = "testP6.ppm";  
  52.   
  53.     FILE *f3 = fopen(filename1, "w");  
  54.     if (f3==NULL) {  
  55.         printf("FILE error\n");  
  56.         exit(0);  
  57.     }  
  58.   
  59.     FILE *f6 = fopen(filename2, "w");  
  60.     if (f6==NULL) {  
  61.         printf("FILE error\n");  
  62.         exit(0);  
  63.     }  
  64.   
  65.     writePPMHeader(f3, 'A', WIDTH, HEIGHT, 255);  
  66.     writePPMdataP3(f3, img, WIDTH, HEIGHT);  
  67.   
  68.     writePPMHeader(f6, 'B', WIDTH, HEIGHT, 255);  
  69.     writePPMdataP6(f6, img, WIDTH, HEIGHT);  
  70.   
  71.     fclose(f3);  
  72.     fclose(f6);  
  73.   
  74.     return 0;  
  75. }  

测试如下:

C语言编写简单图像程序-圆

画圆完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


typedef struct {
  size_t width;
  size_t height;
  unsigned char *data;
} Image;


static Image *image_new (size_t width,size_t height){
  Image *image;
  image = malloc (sizeof *image);
  image->width = width;
  image->height = height;
  image->data = malloc (width * height*3);


  return image;
}


static void image_free (Image *image){
  free (image->data);
  free (image);
}


static void image_fill (Image *image,unsigned char value){
  memset(image->data, value, image->width * image->height*3);
}


/**
 * image_set_pixel:
 *
 * Sets a pixel passed in signed (x, y) coordinates, where (0,0) is at
 * the center of the image.
 **/
static void
image_set_pixel (Image *image,
                 ssize_t x,
                 ssize_t y,
                 unsigned char Rvalue,
                 unsigned char Gvalue,
                 unsigned char Bvalue){
  size_t tx, ty;
  unsigned char *p;


  tx = (image->width / 2) + x;
  ty = (image->height / 2) + y;


  p = image->data + (ty * image->width*3) + tx*3;


*p    = Rvalue;
    *(p+1) = Gvalue;
    *(p+2) = Bvalue;
}


static void image_save (const Image *image, const char *filename){

  FILE *out;
  out = fopen (filename, "wb");//创建以filename为名字的文件
  if (!out)
    return;
  fprintf (out, "P6\n");// 写入图片格式类型
  fprintf (out, "%zu %zu\n", image->width, image->height);//图片尺寸
  fprintf (out, "255\n"); //图片像素深度(depth)


  fwrite (image->data, 1, image->width * image->height*3, out);//以二进制形式存储数据


  fclose (out);
}


static void draw_circle (Image *image, int radius, unsigned char Rvalue,unsigned char Gvalue,unsigned char Bvalue){
  int x, y;
  for (y = -radius; y <= radius; y++)
    for (x = -radius; x <= radius; x++)
      if ((x * x) + (y * y) <= (radius * radius))
        image_set_pixel(image, x, y, Rvalue,Gvalue,Bvalue);
}


int main (int argc, char *argv[]){
  Image *image;
  image = image_new (600, 600);
  image_fill (image, 0xff);
  draw_circle (image, 200, 0x00,0x00,0xff);
  image_save (image, "circle.ppm");
  image_free (image);


  return 0;
}

编译路径下生成PPM文件

文件查看软件:Irfanview

C语言编写简单图像程序-圆

参考:

https://en.wikipedia.org/wiki/Netpbm_format

http://blog.csdn.net/gengshenghong/article/details/8648577#reply




相关文章:

  • 2021-09-20
  • 2021-09-20
  • 2021-04-02
  • 2021-09-11
  • 2021-06-04
  • 2021-09-29
  • 2021-11-05
猜你喜欢
  • 2022-01-18
  • 2021-07-05
  • 2022-01-01
  • 2021-12-05
  • 2021-04-07
  • 2021-04-24
  • 2021-06-12
相关资源
相似解决方案