什么是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();
}