グラフィックノート:怠け者なのでNetpbmという画像フォーマットを使いました
画像フォーマットといえば、BMP、PNG、JPEG、GIF、TIFFなど、多くの人が手当たり次第に手に入れることができます.しかし、このNetpbmというフォーマットは少し見慣れていないように見え、Windowsのデフォルトのシステム画像ブラウザでもサポートされていない.このようなUnix世界で最初に流行する単純な画像(ビットマップ)フォーマットは、透明画素も圧縮もExifもサポートされていない.ライブラリを外に出すのがおっくうなプログラマーが30分以内に画像フォーマットを解析する車輪を簡単に作るためだろう.
まず、この画像のフォーマットを簡単に紹介します.この画像には6つのクラシックバージョンがあります.(新しいバージョンPAMはサポートが悪いところが多い):
Type
Magic number
Format
Magic number
Format
Extension
Colors
Portable BitMap
P1
ASCII
P4
binary
.pbm
0–1 (black & white)
Portable GrayMap
P2
ASCII
P5
binary
.pgm
0–255 (gray scale)
Portable PixMap
P3
ASCII
P6
binary
.ppm
0–255 (RGB)
しかし、P 1,P 2,P 4,P 5は単色と階調のみをサポートするため、特定の場合を除いてはあまり役に立たない.P 3とP 6について詳しくお話しします.P 3はRGBに対応するASCII形式である、P 6はRGBに対応するバイナリ形式である.無駄なことを言うだけで、画像ファイルの内容を見ると違いがわかります.
同じ内容で、P 6はそんなに読めないように見えます.
これらのファイルの内容からNetpbmのフォーマットを知ることができます.第1バイトはP、第2バイトはN. の数字である.の10進数はそれぞれ幅、高さ、最大の色域を表す、例えば255がRGBを表す成分ごとに0~255をとることができる. は、この過程において、#が読み出すとコメントとなる、行全体が破棄される. Nが3である場合、次に3つの10進数整数ごとに1つの画素(それぞれ赤緑青成分を順番に表す)を表す.Nが6である、次に3バイト毎に1画素(それぞれ赤緑青成分を順番に示す)を表す.
Reference
Examples
このクラスの基本的な使い方について、3つの簡単な例を書きました.黒から白へのグラデーションの画像を出力します. 画像を入力し、半分に縮小して出力します. 有名なMandelbrot Set.
まず、この画像のフォーマットを簡単に紹介します.この画像には6つのクラシックバージョンがあります.(新しいバージョンPAMはサポートが悪いところが多い):
Type
Magic number
Format
Magic number
Format
Extension
Colors
Portable BitMap
P1
ASCII
P4
binary
.pbm
0–1 (black & white)
Portable GrayMap
P2
ASCII
P5
binary
.pgm
0–255 (gray scale)
Portable PixMap
P3
ASCII
P6
binary
.ppm
0–255 (RGB)
しかし、P 1,P 2,P 4,P 5は単色と階調のみをサポートするため、特定の場合を除いてはあまり役に立たない.P 3とP 6について詳しくお話しします.P 3はRGBに対応するASCII形式である、P 6はRGBに対応するバイナリ形式である.無駄なことを言うだけで、画像ファイルの内容を見ると違いがわかります.
P3
# RGB+Ascii. 3x2 :
#
#
3 2
255
255 0 0 0 255 0 0 0 255
255 255 0 255 0 255 0 255 255
同じ内容で、P 6はそんなに読めないように見えます.
xxd
でしかその内容を観察できません(画像はGIMPによって生成されます):0000000: 5036 0a23 2043 5245 4154 4f52 3a20 4749 P6.# CREATOR: GI
0000010: 4d50 2050 4e4d 2046 696c 7465 7220 5665 MP PNM Filter Ve
0000020: 7273 696f 6e20 312e 310a 3320 320a 3235 rsion 1.1.3 2.25
0000030: 350a ff00 0000 ff00 0000 ffff ff00 ff00 5...............
0000040: ff00 ffff ....
これらのファイルの内容からNetpbmのフォーマットを知ることができます.
Reference
struct color
は、色、すなわち画素である.cpp
struct color { uint8_t r; uint8_t g; uint8_t b; uint8_t a; // constructor color(uint8_t red = 255, uint8_t green = 255, uint8_t blue = 255, uint8_t alpha = 255); color(uint32_t pxl); // 32-bit unsigned integer operations color& operator=(uint32_t pxl); uint32_t value(); };
class image
はppmファイルの読み取りと書き込み、画素操作を提供する.class image {
public:
typedef std::vector buf_type;
typedef std::vector::iterator iter_type;
// constructors
image();
image(size_t w, size_t h);
image(const std::string& fn);
// properties getters
std::vector& buffer();
const std::vector& buffer() const;
size_t width() const;
size_t height() const;
size_t version() const;
void version(int v);
// pixel operation
color& operator()(size_t x, size_t y);
const color& operator()(size_t x, size_t y) const;
color& pixel(size_t x, size_t y);
const color& pixel(size_t x, size_t y) const;
// erase all content and create new image
void assign(const image& other);
void assign(size_t w, size_t h);
// load from and dump to file stream
void load(std::istream& fin);
void dump(std::ostream& fout) const;
}
std::istream& operator>>(std::istream& si, image& img);
std::ostream& operator<
Examples
このクラスの基本的な使い方について、3つの簡単な例を書きました.
#include
#include
#include
#include
#include "image.h"
using namespace std;
int main()
{
ofstream fout_1("image-test.out.1.ppm");
ofstream fout_2("image-test.out.2.ppm");
ofstream fout_3("image-test.out.3.ppm");
ifstream fin_2("image-test.in.2.ppm");
////////////////////////////////////////////////////////////////////////
// output a gradient from black to white
image out1(256, 256);
image in2;
image::buf_type& buf = out1.buffer();
for(size_t i = 0; i < buf.size(); i++)
buf[i] = color(i/256, i/256, i/256);
fout_1 << out1;
////////////////////////////////////////////////////////////////////////
// shrink the original image to its half
fin_2 >> in2;
image out2(in2.width() / 2, in2.height() / 2);
for(size_t i = 1; i < in2.width(); i += 2) {
for(size_t j = 1; j < in2.height(); j += 2) {
uint8_t r =(
in2.pixel(i-1, j-1).r +
in2.pixel(i , j-1).r +
in2.pixel(i-1, j ).r +
in2.pixel(i , j ).r) / 4;
uint8_t g =(
in2.pixel(i-1, j-1).g +
in2.pixel(i , j-1).g +
in2.pixel(i-1, j ).g +
in2.pixel(i , j ).g) / 4;
uint8_t b =(
in2.pixel(i-1, j-1).b +
in2.pixel(i , j-1).b +
in2.pixel(i-1, j ).b +
in2.pixel(i , j ).b) / 4;
out2(i/2, j/2) = {r, g, b, 0xff};
}
}
fout_2 << out2;
////////////////////////////////////////////////////////////////////////
// mandelbrot set
image out3(800, 600);
for(float a = 0; a < out3.width(); a ++) {
for(float b = 0; b < out3.height(); b ++) {
complex c((a - 550) / 250.0, (b - 300) / 250.0);
complex z(0, 0);
int r, max_r = 30;
for(r = 0; r < max_r && abs(z) < 2; r++) {
z = z * z + c;
}
if(r == max_r) out3(a, b) = 0xff000000;
else {
r = 256.0 - 256.0 / max_r * r;
out3(a, b) = color(r, r, r);
}
}
}
fout_3 << out3;
return 0;
}