PPM图片格式详解 | 青训营

354 阅读2分钟

什么是PPM图片

ppm格式是一种图片格式,全名 Portable PixelMap。ppm格式是还有两个兄弟一个是pgm和pbm。

详情请参考Netpbm

PPM可以干嘛

PPM格式的图片如果用文本编辑器打开会发现都是ASCII码而不是二进制。所以这种格式的图片方便操作,但是文件大小可能不是很友好。

前置知识

  • 任何一种颜色可以通过RGB(Red Green Blue)这三种颜色混合而成。
  • 一般的图片都是位图,位图是由一个个有颜色的像素点组成的。
  • 一般RGB是有3个0-255的整数表示的

文件组成

对于一个PPM格式的文件,我们先要声明他的类型(他在家族中的排名)

从这里可以知道我们的ppm格式有p3和p6两种

这里我们要用可读性更好的ASCII码版,所以是p3

然后我们要声明一个图片的长和宽

然后要声明我们颜色的范围,一般是255

举个例子:

test.ppm

P3 
# ./test.ppm 
2 2 
255 
255 0 0 
255 255 0 
0 255 0 
0 0 255

从维基百科上讲,每行长度推荐不超过76个字符包括空格

这里我们一行就表示一个像素

那这个ppm图片如下

可以看出他的顺序是从上往下,从左往右。

这个格式是个简单的图片格式,比较适合用来做一些小程序的验证,但是由于文件大小太大,所以不适合传输。

放一个我自己写的ppm的C++写入器,需要依赖fmt(fmt太好用了)(我懒得改成C++标准库版的了)

Color.h

#ifndef COLOR_H 
#define COLOR_H 
namespace LRD { 
struct Color{ 
    int r; int g; int b; 
}; 
} 
#endif

Picture.h

#ifndef PICTURN_H
#define PICTURN_H

#include "Color.h"
#include <string>
#include <vector>

namespace LRD {


class Picture {

public:
  Picture() = delete;
  
  /**
   * @brief Construct a new Picture object
   * @brief the picture [0][0] position is on the top left, the [height][width] is on the right bottom
   * @param width the width of the picture
   * @param height the height of the picture
   * @param path the path of the file to storage the picture
   */
  Picture(unsigned int width, unsigned int height, std::string path);
  
  /**
   * @brief write the picture add to a .ppm format picture
   * 
   */
  void writeToFile();
  
  /**
   * @brief return Color at the pixel at [i][j]
   * 
   * @param i
   * @param j 
   * @return Color& 
   */
  Color &at(unsigned int i, unsigned int j);

private:
  std::vector<std::vector<Color>> data;
  unsigned int width;
  unsigned int height;
  std::string path;
};
}
#endif

Picture.cpp

#include "Picture.h"
#include "fmt/core.h"
#include <fstream>


LRD::Picture::Picture(unsigned int width, unsigned int height, std::string path)
    : width(width), height(height), path(path) 
{
  data.assign(height, std::vector<Color>(width, {0, 0, 0}));
}

LRD::Color &LRD::Picture::at(unsigned int i, unsigned int j) { return data[i][j]; }

void LRD::Picture::writeToFile() {
  std::ofstream fs(path, std::ios::out);
  fs << "P3\n";
  fs << "# " << path << "\n";
  fs << fmt::format("{} {}\n", width, height);
  fs << 255 << "\n";
  unsigned int cnt = 0;
  for (auto vec : data) {
    for (auto col : vec) {
      cnt++;
      fs << fmt::format("{:>4}{:>4}{:>4}{}", col.r, col.g, col.b, (cnt%4==0?'\n':' '));
    }
  }
  fs.close();
}