【解説】PNMのフォーマット + viewer

PNMという画像をご存知でしょうか。
データ構造が非常にシンプルで，プログラミング演習としてちょっとした画像生成をするのにも向いています。
本記事ではPNMの**2025年3月時点で最新の**データ仕様に関してまとめます。
共通事項が複数ありますが，情報の散らばりをなくすために同じことでも略すことなく記載します。

# PNMの種類
PNMには大きく6種類あり，それらはマジックナンバー（`P数字`と表記されるもの）で区別されます。

| マジックナンバー | タイプ | タイプ略 | エンコード |
|:-:|:-:|:-:|:-:|
| P1  | Portable bitmap | PBM |  ASCII |
| P2  | Portable graymap | PGM  | ASCII  |
| P3  | Portable pixmap | PPM  | ASCII  |
| P4  | Portable bitmap | PBM  |  Binary |
| P5  | Portable graymap | PGM  |  Binary | 
| P6  | Portable pixmap | PPM  |  Binary | 

（表参考：[Wikipedia](https://ja.wikipedia.org/wiki/PNM_(%E7%94%BB%E5%83%8F%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88))）

## PBM
マジックナンバーが`P1`または`P4`のものはPBMで，**黒と白の二値画像**です。ですので，データは0と1だけで保存されます。
### P1
**＜データフォーマット＞**
|項目 | 中身 | 備考 |
|:-:|---|---|
|1 |  `P1` | ASCII文字。 |
| 2| 1つ以上の区切り文字  | 区切り文字とは，<br>**空白**，**タブ（TAB）**，**復帰（CR）**，**改行（LF）**<br>のこと。 |
| 3| 画像の幅  | 10進数で，ASCII文字で表記。 |
| 4| 1つ以上の区切り文字  |  |
| 5| 画像の高さ  | 10進数で，ASCII文字で表記。 |
| 6| **1つのみ**の区切り文字 | 通常は改行（LF）が入る。|
|7 | ラスターデータ[^1] | `0`か`1`のASCIIだけで構成。<br>`0`は白，`1`は黒。<br>各ASCIIの前後には**0つ以上**の区切り文字。<br>データの格納順番は，画像の左上から右下に向かって，上から下，左から右へと格納していく（以下の表で1から16を辿るように）。<br><table><tr><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td></tr></table>|
|||
|1~5の前後| コメント | `'#'`から次の復帰（CR）・改行（LF）・EOF**手前まで**の任意の文字群はコメントとして扱い，無視する（CR，LF，EOFはコメントに含まない）。<br><font color="Red">2024年6月以前は復帰（CR）・改行（LF）・EOFもコメントの一部として無視する仕様になっていた。</font><br>コメントを取り除いた結果，ヘッダー部が項目1〜6に沿ったフォーマットになっていれば良い。 |
|7の後|ジャンク|ラスターデータの後は空白文字で始まっていれば，好きなジャンクを置くことができる。|
|全項目||ファイルの各行は70文字を超えてはならない。|

[^1]: 画像本体となるデータ群のこと。

:::note info
上のデータフォーマット表の項目6にて，区切り文字は「1つのみ」とありますが，実際は2つ以上あっても問題ありません。
2つ以上あった場合，2つ目以降の区切り文字はラスターデータ（項目7）中の
>各ASCIIの前後には0つ以上の区切り文字

に属するためです。
:::

サンプル画像（かなり拡大したもの）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/c502fb1a-9fbf-4195-853f-69bec2d830ee.png" width="200px" />


`P1`はエンコードがASCIIであるので，ファイルの中身を直接見ることができます。
```plaintext
P1
20 10
# this is PBM
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1

```

:::note info
上のデータフォーマット表に沿っていれば良いので，極論，以下のようなデータであっても問題ありません。
```plaintext
P1         20
      10
# this is PBM


000 011110     00011110000
111  1000011110000  1111
...
```
:::

文字列検索で0または1を染めてみるとラスター部からどのような画像であるのかが想像がつきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/d895c3ee-20a7-48e4-829b-832f0996674f.png" width="300px" />


<hr>

### P4
**＜データフォーマット＞**
|項目 | 中身 | 備考 |
|:-:|---|---|
|1 |  `P4` | ASCII文字。 |
| 2| 1つ以上の区切り文字  | 区切り文字とは，<br>**空白**，**タブ（TAB）**，**復帰（CR）**，**改行（LF）**<br>のこと。 |
| 3| 画像の幅  | 10進数で，ASCII文字で表記。 |
| 4| 1つ以上の区切り文字  |  |
| 5| 画像の高さ  | 10進数で，ASCII文字で表記。 |
| 6| **1つのみ**の区切り文字 | 通常は改行（LF）が入る。|
|7 | ラスターデータ[^1] | **画像の各行ごと**に，`0`か`1`の**1ビット**のバイナリが順に並んだデータ構造（つまり1バイト中には8つのデータが並ぶ）。<br>各行の最後の1バイトが8ビットで埋まらない場合（つまり幅が8の倍数ではない場合），空いているビットを適当に埋める。例えば，ある行のデータが`01101`であった場合，その行のデータ数は5ビットになるので，適当に8-5=3ビットを追加して`01101000`（最後の`000`が適当に埋めた3ビット）のようにする。<br>ビット数合わせのために各行の最後の1バイト中に追加した適当なビットは一切使われない。<br>`0`は白，`1`は黒。<br>データの格納順番は，左上から右下に向かって，上から下，左から右へと格納していく（以下の表で1から16を辿るように）。<br><table><tr><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td></tr></table>|
|||
|1~5の前後| コメント | `'#'`から次の復帰（CR）・改行（LF）・EOF**手前まで**の任意の文字群はコメントとして扱い，無視する（CR，LF，EOFはコメントに含まない）。<br><font color="Red">2024年6月以前は復帰（CR）・改行（LF）・EOFもコメントの一部として無視する仕様になっていた。</font><br>コメントを取り除いた結果，ヘッダー部が項目1〜6に沿ったフォーマットになっていれば良い。 |

:::note warn
1つのみの区切り文字（項目6）前（＝項目5の後）までコメントを入れることができますが，以下のような場合は，注意が必要です（[LF]は改行コード（\n）を明記したもの）。
```plaintext
P4[LF]
20 10[LF]
# this is PBM[LF]
[ラスター（バイナリ）データ]
```
このデータからコメントを取り除くと以下のようになります。
```plaintext
P4[LF]
20 10[LF]
[LF]
[ラスター（バイナリ）データ]
```
これは，画像の高さ（項目5）`10`直後の`[LF]`が区切り文字として処理され，それ以降はラスターデータとして認識されます。つまりラスターデータ直前の`[LF]`もラスターデータの一部として処理されてしまいます。
:::

:::note warn
テキストエディタ等で編集する際，改行コードには注意してください。
CRLF（CRとLFがセットで改行する）だと，
```plaintext
P4[CR][LF]
20 10[CR][LF]
[ラスター（バイナリ）データ]
```
このようになり，1つ上の注意事項と同じように，`10`直後の`[CR]`が区切り文字として処理され，ラスターデータ直前の`[LF]`もラスターデータの一部として処理されてしまいます。
改行コードはLFにしましょう。
実際，本注意部の下に示すサンプル画像の改行コードをCRLFにした場合，以下のようになりました。画像の幅が8の倍数ではないため，[LF]の1つ分がずれるだけではなく，ビット数合わせ用に適当に埋めたビットも画像に反映されてしまいます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/06c16071-7bb9-4a98-8cc4-705466f0f79f.png" width="200px" />
:::

サンプル画像（P1と同様のもの，かなり拡大したもの）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/c502fb1a-9fbf-4195-853f-69bec2d830ee.png" width="200px" />


`P4`はエンコードがBinaryであるので，ファイルの中身を直接見ることができません。普通のエディタでファイルの中身を開いても画像データの概形を掴むことはできません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/ec074f9a-ccec-41be-a0c4-e2ab88b8845a.png" width="300px" />

ここで，VSCodeの拡張機能 [Hex Editor](https://marketplace.visualstudio.com/items?itemName=ms-vscode.hexeditor) 等を用いて少し中身を見てみましょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/ccc57821-8a56-48ea-a866-0386763f857b.png" width="700px" />
0x08 が[LF]に相当するASCIIコードで，これがデータフォーマット表の項目6に相当します。よって，0x09（青い部分）以降がラスターデータとなります。
0x09に関して，Data Inspectorのbinaryを見ると，`00001111`となっているのがわかると思います。これは，`白白白白黒黒黒黒`の部分です。実際，[P1で示したサンプル画像の中身](#p1)を見るとラスターデータの最初の部分は`0 0 0 0 1 1 1 1`となっており，一致しているのが確認できると思います。


## PGM
マジックナンバーが`P2`または`P5`のものはPGMで，**グレースケール画像**です。
### P2
**＜データフォーマット＞**
|項目 | 中身 | 備考 |
|:-:|---|---|
|1 |  `P2` | ASCII文字。 |
| 2| 1つ以上の区切り文字  | 区切り文字とは，<br>**空白**，**タブ（TAB）**，**復帰（CR）**，**改行（LF）**<br>のこと。 |
| 3| 画像の幅  | 10進数で，ASCII文字で表記。 |
| 4| 1つ以上の区切り文字  |  |
| 5| 画像の高さ  | 10進数で，ASCII文字で表記。 |
| 6| 1つ以上の区切り文字| |
| 7| データ中の最大輝度（グレー値） | 10進数で，ASCII文字で表記。<br>0超65536未満である必要がある。|
| 8| **1つのみ**の区切り文字 | 通常は改行（LF）が入る。|
| 9| ラスターデータ[^1] | 0 (`0`)から最大輝度（項目7）までの10進数を，ASCII文字で表記したデータで構成。<br>`0`が黒（#000），最大輝度が白（#FFF）。<br>各値の前後には**1つ以上**の区切り文字。<br>データの格納順番は，画像の左上から右下に向かって，上から下，左から右へと格納していく（以下の表で1から16を辿るように）。<br><table><tr><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td></tr></table>|
|||
|1~7の前後| コメント |  `'#'`から次の復帰（CR）・改行（LF）・EOF**手前まで**の任意の文字群はコメントとして扱い，無視する（CR，LF，EOFはコメントに含まない）。<br><font color="Red">2024年6月以前は復帰（CR）・改行（LF）・EOFもコメントの一部として無視する仕様になっていた。</font><br>コメントを取り除いた結果，ヘッダー部が項目1〜8に沿ったフォーマットになっていれば良い。 |
|全項目||ファイルの各行は70文字を超えてはならない。|


:::note info
上のデータフォーマット表の項目8にて，区切り文字は「1つのみ」とありますが，実際は2つ以上あっても問題ありません。
2つ以上あった場合，2つ目以降の区切り文字はラスターデータ（項目9）中の
>各値の前後には1つ以上の区切り文字

に属するためです。
:::

:::note info
PBMのドキュメントにはラスターデータの後にはジャンクを置いても良いと明記されていますが，PGMのドキュメントには明記されていません（ですがおそらくジャンクを置いても問題ないと思います）。
:::


`P2`はエンコードがASCIIであるので，ファイルの中身を直接見ることができます。
しかしPBMと違い，`0`か`1`で構成はされていないため画像データの概形を掴むのは難しいです。
```plaintext
P2
20 10
50
# this is PGM
10 10 10 10 0 0 0 0 30 30 30 30 0 0 0 0 50 50 50 50
0 0 0 0 20 20 20 20 0 0 0 0 40 40 40 40 0 0 0 0
10 10 10 10 0 0 0 0 30 30 30 30 0 0 0 0 50 50 50 50
0 0 0 0 20 20 20 20 0 0 0 0 40 40 40 40 0 0 0 0
10 10 10 10 0 0 0 0 30 30 30 30 0 0 0 0 50 50 50 50
0 0 0 0 20 20 20 20 0 0 0 0 40 40 40 40 0 0 0 0
10 10 10 10 0 0 0 0 30 30 30 30 0 0 0 0 50 50 50 50
0 0 0 0 20 20 20 20 0 0 0 0 40 40 40 40 0 0 0 0
10 10 10 10 0 0 0 0 30 30 30 30 0 0 0 0 50 50 50 50
0 0 0 0 20 20 20 20 0 0 0 0 40 40 40 40 0 0 0 0

```

:::note info
上のデータフォーマット表に沿っていれば良いので，極論，以下のようなデータであっても問題ありません。
```plaintext
P2         20
      10
    50
# this is PGM
10 10 10 10  0  0  0  0   30 30 30 30  0  0  0  0 50 50 50 50
 0  0  0  0 20 20 20 20    0  0  0  0 40 40 40 40 0 0 0     0
...
```
:::

上の画像データを開くと以下のように見えます（かなり拡大）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/6a1578df-3075-49a4-83e2-4103d37fd8d7.png" width="200px" />

<hr>


### P5
**＜データフォーマット＞**
|項目 | 中身 | 備考 |
|:-:|---|---|
|1 |  `P5` | ASCII文字。 |
| 2| 1つ以上の区切り文字  | 区切り文字とは，<br>**空白**，**タブ（TAB）**，**復帰（CR）**，**改行（LF）**<br>のこと。 |
| 3| 画像の幅  | 10進数で，ASCII文字で表記。 |
| 4| 1つ以上の区切り文字  |  |
| 5| 画像の高さ  | 10進数で，ASCII文字で表記。 |
| 6| 1つ以上の区切り文字| |
| 7| データ中の最大輝度（グレー値） | 10進数で，ASCII文字で表記。<br>0超65536未満である必要がある。|
| 8| **1つのみ**の区切り文字 | 通常は改行（LF）が入る。|
|9 | ラスターデータ[^1] | 0から最大輝度（項目7）までの10進数を，バイナリで順に並べたデータで構成。<br>最大輝度（項目7）が256未満の場合，各値1バイト，それ以外の場合は各値2バイトを使う[^2]。2バイトを使う場合，最上位バイトが先頭に来るようにする（ビッグエンディアン）。<br>0が黒（#000），最大輝度が白（#FFF）。<br>データの格納順番は，左上から右下に向かって，上から下，左から右へと格納していく（以下の表で1から16を辿るように）。<br><table><tr><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td></tr></table>|
|||
|1~7の前後| コメント | `'#'`から次の復帰（CR）・改行（LF）・EOF**手前まで**の任意の文字群はコメントとして扱い，無視する（CR，LF，EOFはコメントに含まない）。<br><font color="Red">2024年6月以前は復帰（CR）・改行（LF）・EOFもコメントの一部として無視する仕様になっていた。</font><br>コメントを取り除いた結果，ヘッダー部が項目1〜6に沿ったフォーマットになっていれば良い。 |

[^2]:1バイトは8ビット。1ビットは0か1の2択で表すので，1バイト（8ビット）で表せる値の個数（組み合わせ）は`2^8 = 256`個で，0~255を表せます。よって，最大輝度が256未満なら1バイトで表せるというわけです。

:::note warn
1つのみの区切り文字（項目8）前（＝項目7の後）までコメントを入れることができますが，以下のような場合は，注意が必要です（[LF]は改行コード（\n）を明記したもの）。
```plaintext
P5[LF]
20 10[LF]
# this is PGM[LF]
[ラスター（バイナリ）データ]
```
このデータからコメントを取り除くと以下のようになります。
```plaintext
P5[LF]
20 10[LF]
[LF]
[ラスター（バイナリ）データ]
```
これは，画像の高さ（項目5）`10`直後の`[LF]`が区切り文字として処理され，それ以降はラスターデータとして認識されます。つまりラスターデータ直前の`[LF]`もラスターデータの一部として処理されてしまいます。
:::

:::note warn
テキストエディタ等で編集する際，改行コードには注意してください。
CRLF（CRとLFがセットで改行する）だと，
```plaintext
P5[CR][LF]
20 10[CR][LF]
[ラスター（バイナリ）データ]
```
このようになり，1つ上の注意事項と同じように，`10`直後の`[CR]`が区切り文字として処理され，ラスターデータ直前の`[LF]`もラスターデータの一部として処理されてしまいます。
改行コードはLFにしましょう。
実際，本注意部の下に示すサンプル画像の改行コードをCRLFにした場合，以下のようになりました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/4a707f21-717b-46e4-847f-3a91cc573932.png" width="200px" />
:::

サンプル画像（P2と同様のもの，かなり拡大したもの）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/6a1578df-3075-49a4-83e2-4103d37fd8d7.png" width="200px" />


`P5`はエンコードがBinaryであるので，ファイルの中身を直接見ることができません。普通のエディタでファイルの中身を開いても画像データの概形を掴むことはできません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/56674f1c-132a-44ff-b4e5-4815b814b30b.png" width="350px" />

ここで，VSCodeの拡張機能 [Hex Editor](https://marketplace.visualstudio.com/items?itemName=ms-vscode.hexeditor) 等を用いて少し中身を見てみましょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/628d8feb-56e3-42cd-8cce-6bab21713a57.png" width="700px" />
0x0B が[LF]に相当するASCIIコードで，これがデータフォーマット表の項目8に相当します。よって，0x0C（青い部分）以降がラスターデータとなります。
0x0Cに関して，Data Inspectorのunit8を見ると，`10` (=16*0+10)となっているのがわかると思います。これは，画像データの左上の輝度が`10`だということです。実際，[P2で示したサンプル画像の中身](#p2)を見るとラスターデータの最初の部分は`10`となっており，一致しているのが確認できると思います。



## PPM
マジックナンバーが`P3`または`P6`のものはPPMで，**カラー画像**です。

### P3
**＜データフォーマット＞**
|項目 | 中身 | 備考 |
|:-:|---|---|
|1 |  `P3` | ASCII文字。 |
| 2| 1つ以上の区切り文字  | 区切り文字とは，<br>**空白**，**タブ（TAB）**，**復帰（CR）**，**改行（LF）**<br>のこと。 |
| 3| 画像の幅  | 10進数で，ASCII文字で表記。 |
| 4| 1つ以上の区切り文字  |  |
| 5| 画像の高さ  | 10進数で，ASCII文字で表記。 |
| 6| 1つ以上の区切り文字| |
| 7| データ中の最大カラー値 | 10進数で，ASCII文字で表記。<br>0超65536未満である必要がある。|
| 8| **1つのみ**の区切り文字 | 通常は改行（LF）が入る。|
| 9| ラスターデータ[^1] | 各ピクセルの`RGB`（<font color="Red">赤</font><font color="Green">緑</font><font color="Blue">青</font>の順）の3つの値の組を順に並べたデータ構造で，各値は0 (`0`)から最大カラー値（項目7）までの10進数を，ASCII文字で表記したもの。<br>各値に関して，`0`がその色の成分なし，最大カラー値だとその色の成分をMAXまで配分ということになる。例えば，最大カラー値が`255`のもと，あるピクセルのデータが`255 0 0`であれば，<font color="Red">R=255</font>，<font color="Green">G=0</font>，<font color="Blue">B=0</font> で赤の量100%，緑の量0%，青の量0%なのでこのピクセルは純な赤色である。`0 0 0`は黒，`255 255 255`は白。<br>各値の前後には**1つ以上**の区切り文字。<br>データの格納順番は，画像の左上から右下に向かって，上から下，左から右へと格納していく（以下の表で1から16を辿るように）。<br><table><tr><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td></tr></table>|
|||
|1~7の前後| コメント |  `'#'`から次の復帰（CR）・改行（LF）・EOF**手前まで**の任意の文字群はコメントとして扱い，無視する（CR，LF，EOFはコメントに含まない）。<br><font color="Red">2024年6月以前は復帰（CR）・改行（LF）・EOFもコメントの一部として無視する仕様になっていた。</font><br>コメントを取り除いた結果，ヘッダー部が項目1〜8に沿ったフォーマットになっていれば良い。 |
|全項目||ファイルの各行は70文字を超えてはならない。|


:::note info
上のデータフォーマット表の項目8にて，区切り文字は「1つのみ」とありますが，実際は2つ以上あっても問題ありません。
2つ以上あった場合，2つ目以降の区切り文字はラスターデータ（項目9）中の
>各値の前後には1つ以上の区切り文字

に属するためです。
:::

:::note info
PBMのドキュメントにはラスターデータの後にはジャンクを置いても良いと明記されていますが，PGMのドキュメントには明記されていません（ですがおそらくジャンクを置いても問題ないと思います）。
:::


`P3`はエンコードがASCIIであるので，ファイルの中身を直接見ることができます。
しかしPBMと違い，`0`か`1`で構成はされていないため画像データの概形を掴むのは難しいです。
```plaintext
P3
20 10
50
# this is PPM
50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 
50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 
50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 
50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 
50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 
50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 
50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 
50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 
50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50 0 0 50 0 0 50 0 0 50 
50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 0 50 50 0 50 50 0 50 50 0 50 

```

:::note info
上のデータフォーマット表に沿っていれば良いので，極論，以下のようなデータであっても問題ありません。
```plaintext
P3   20 10    50
# this is PPM
50 0 0   50 0 0   50 0 0   50 0 0
0 0 0   0 0 0   0 0 0   0 0 0
0 50 0   0 50 0   0 50 0   0 50 0
0 0 0   0 0 0   0 0 0   0 0 0
0 0 50   0 0 50   0 0 50   0 0 50 
...
```
:::

上の画像データを開くと以下のように見えます（かなり拡大）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/0b28dab4-8295-491e-8240-37aaf246cfb9.png" width="200px" />

<hr>


### P6
**＜データフォーマット＞**
|項目 | 中身 | 備考 |
|:-:|---|---|
|1 |  `P6` | ASCII文字。 |
| 2| 1つ以上の区切り文字  | 区切り文字とは，<br>**空白**，**タブ（TAB）**，**復帰（CR）**，**改行（LF）**<br>のこと。 |
| 3| 画像の幅  | 10進数で，ASCII文字で表記。 |
| 4| 1つ以上の区切り文字  |  |
| 5| 画像の高さ  | 10進数で，ASCII文字で表記。 |
| 6| 1つ以上の区切り文字| |
| 7| データ中の最大輝度 | 10進数で，ASCII文字で表記。<br>0超65536未満である必要がある。|
| 8| **1つのみ**の区切り文字 | 通常は改行（LF）が入る。|
|9 | ラスターデータ[^1] | 各ピクセルの`RGB`（<font color="Red">赤</font><font color="Green">緑</font><font color="Blue">青</font>の順）の3つの値の組を順に並べたデータ構造で，各値は0 (`0`)から最大カラー値（項目7）までの10進数を，バイナリで並べたもの。<br>最大カラー値（項目7）が256未満の場合，各値1バイト，それ以外の場合は各値2バイトを使う[^2]。2バイトを使う場合，最上位バイトが先頭に来るようにする（ビッグエンディアン）。<br>各値に関して，`0`がその色の成分なし，最大カラー値だとその色の成分をMAXまで配分ということになる。例えば，最大カラー値が`255`のもと，あるピクセルのデータが`255 0 0`であれば，<font color="Red">R=255</font>，<font color="Green">G=0</font>，<font color="Blue">B=0</font> で赤の量100%，緑の量0%，青の量0%なのでこのピクセルは純な赤色である。`0 0 0`は黒，`255 255 255`は白。<br>データの格納順番は，左上から右下に向かって，上から下，左から右へと格納していく（以下の表で1から16を辿るように）。<br><table><tr><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td></tr></table>|
|||
|1~7の前後| コメント | `'#'`から次の復帰（CR）・改行（LF）・EOF**手前まで**の任意の文字群はコメントとして扱い，無視する（CR，LF，EOFはコメントに含まない）。<br><font color="Red">2024年6月以前は復帰（CR）・改行（LF）・EOFもコメントの一部として無視する仕様になっていた。</font><br>コメントを取り除いた結果，ヘッダー部が項目1〜6に沿ったフォーマットになっていれば良い。 |


:::note warn
1つのみの区切り文字（項目8）前（＝項目7の後）までコメントを入れることができますが，以下のような場合は，注意が必要です（[LF]は改行コード（\n）を明記したもの）。
```plaintext
P6[LF]
20 10[LF]
# this is PPM[LF]
[ラスター（バイナリ）データ]
```
このデータからコメントを取り除くと以下のようになります。
```plaintext
P6[LF]
20 10[LF]
[LF]
[ラスター（バイナリ）データ]
```
これは，画像の高さ（項目5）`10`直後の`[LF]`が区切り文字として処理され，それ以降はラスターデータとして認識されます。つまりラスターデータ直前の`[LF]`もラスターデータの一部として処理されてしまいます。
:::

:::note warn
テキストエディタ等で編集する際，改行コードには注意してください。
CRLF（CRとLFがセットで改行する）だと，
```plaintext
P6[CR][LF]
20 10[CR][LF]
[ラスター（バイナリ）データ]
```
このようになり，1つ上の注意事項と同じように，`10`直後の`[CR]`が区切り文字として処理され，ラスターデータ直前の`[LF]`もラスターデータの一部として処理されてしまいます。
改行コードはLFにしましょう。
実際，本注意部の下に示すサンプル画像の改行コードをCRLFにした場合，以下のようになりました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/df99ba24-1589-46ed-a9de-e8dbd51fcdd5.png" width="200px" />
:::

サンプル画像（P3と同様のもの，かなり拡大したもの）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/0b28dab4-8295-491e-8240-37aaf246cfb9.png" width="200px" />


`P6`はエンコードがBinaryであるので，ファイルの中身を直接見ることができません。普通のエディタでファイルの中身を開いても画像データの概形を掴むことはできません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/0a79480b-04b3-43af-be08-d9edb13fac1b.png" width="300px" />

ここで，VSCodeの拡張機能 [Hex Editor](https://marketplace.visualstudio.com/items?itemName=ms-vscode.hexeditor) 等を用いて少し中身を見てみましょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981603/ec69247d-5cc1-4bf6-91f2-19d00807e41d.png" width="700px" />
0x0B が[LF]に相当するASCIIコードで，これがデータフォーマット表の項目8に相当します。よって，0x0C（青い部分）以降がラスターデータとなります。
0x0Cに関して，Data Inspectorのunit8を見ると，`50` (=16*3+2)となっているのがわかると思います。これは，画像データの左上のR（赤）成分が`50`だということです。この画像データの最大カラー値は50ですので，赤の量はMAXとなります。同様に，0x0D, 0x0Eは10進数で`0`と，画像データの左上の緑の量，青の量はともに0となります。よって，`(0x0C, 0x0D, 0x0E) = (50, 0, 0)`で，このピクセルは純な赤色だとわかります。実際，[P3で示したサンプル画像の中身](#p3)を見るとラスターデータの最初の部分は`50 0 0`となっており，一致しているのが確認できると思います。


# 参考文献
本記事は公式ドキュメントを日本語にして内容を噛み砕いたものです。
- https://netpbm.sourceforge.net/doc/pbm.html
- https://netpbm.sourceforge.net/doc/pgm.html
- https://netpbm.sourceforge.net/doc/ppm.html
- https://coskxlabsite.stars.ne.jp/html/for_students/PNM/pnmfiles.html

<br>
間違い，理解しにくい点がありましたらコメント・編集リクエストで教えていただけると幸いです。


# ビューワーを公開しました
PNMファイルはGIMP等で開きますが，ちょっと編集して再度表示，という使い方には向いていません。そのため，手軽にPNMを表示することができ，上で紹介した最新の仕様に沿って動作する，PNMビューワーを作ってみました。
以下のリンクから試してみてください。
（表示画像を右クリックでpngで保存（変換）できますが，これは全く意図したものではなく，偶然の産物です）

https://sssscccc210.github.io/PNM_viewer/