ノートの切れ端

その日やったことを書くだけ。

PNMという画像フォーマットが使いやすい

Intro

画像データを扱いたいがフォーマットとかが複雑そうで、勉強する気がわかなかったが、pnmという形式によって一気にはかどったのでその話を。

PNMとはなんぞや

画像フォーマットの一つ。(他の画像フォーマットは知らないが、)データをテキストでも扱えるため、計算結果を画像フォーマットに整形するために色々考える必要がない。以下のような種類がある。

バイナリ・ビットマップ
情報がバイナリ形式。また表せる色も、白or黒。
バイナリ・グレースケール
情報がバイナリ形式。表せる色は、白からグレー、黒。
バイナリ・RGB
情報がバイナリ形式。色はRGBを使える。
テキスト・ビットマップ
情報がテキスト形式。また表せる色も、白or黒。
テキスト・グレースケール←これを使用
情報がテキスト形式。表せる色は、白からグレー、黒。
テキスト・RGB
情報がテキスト形式。色はRGBを使える。

どう使う?

matlabとかで計算させたい画像を持ってくる。今回は、NxM画素、グレースケールの"test.png"とおく。

PNG to PNM

png2pnmというコマンドを準備する。自分はarchを使っているが、libpngというパッケージをインストールした(気がする)。

png2pnm -n test.png > test.pnm

で、pnm形式に変換してくれる。ただし書式ルールで、一行の文字数が決まってるらしく、各行M個のフィールドがあるわけではないので、整形に関して後述する。

PNM to PNG

pnm2pngというコマンドを準備する。これもlibpngに入ってたかな。

pnm2png test.pnm > test2.png

png形式に変換してくれる。

PNM形式 to 行列

NxM画素の画像データだから、NxM行列で扱いたいと思うのが一般だと思う。NxM行列としてoctaveやらに読み込めるように、以下の手順を踏む。

  1. PNM形式 to すべてのフィールドを一行にまとめた、test.tmp ( step1.awk
  2. test.tmp to 各行のフィールド数がM個にした、test.mat ( step2.awk )
  3. octaveでは、dlmread("test.mat")をNxM行列として読み込めるため、完了。

Step1

PNM形式の、今回使う、テキスト・グレースケールのフォーマットを簡単に説明する。

  • 一行目は、6種類のうち、どの形式で書かれているか。今回はテキスト・グレースケールのため、"P2"
  • 二行目は、画像の大きさ。NxM画素のため、 "N M"
  • 三行目は、グレースケールでの最大値。恐らくいくつでもいいと思うが、pngを変換した結果、"65535"
  • 四行目以下は、画像左上からの、各画素の値。区切り文字はスペース。

例はこんな感じ。実際はもっと長いが途中省略する。

P2
16 16
65535
32632 28557 31507 33464
33510 42367 46113 48265
49762 51108 51933 53282
42071 37332 36162 17002
30002 27844 30897 32782
...

四行目以降に注目して、(2,2)の値を見ると"42367"となっているが、実際にはこの値は(1,6)のものであることに注意する必要がある。これを、以下みたいな感じにするスクリプトを書く。

16 16
32632 28557 31507 33464 33510 42367 46113 48265 ...

実際に書いたスクリプトはこれ。

NR == 2 {
    print $0
}

NR > 3 {
    for(i=1; i<=NF; i++)
        printf("%d ", $i)
}

END {
    printf("\n")
}

これを"step1.awk"と名づけて以下のコマンドを実行すると、step1の結果が出てくる。

awk -f step1.awk test.pnm > step1.txt

Step2

以下のスクリプトで、各行にM個のフィールドがあるよう整形します。

NR == 1 {
    row = $1
    column = $2
}

NR > 1 {
    for(i=1; i<=NF; i++)
        printf("%d%s", $i, i%column==0 ? "\n" : " ")
}

これを"step2.awk"と名付け、以下のコマンドを実行します。

awk -f step2.awk step1.txt > test.mat

これで、PNM形式からNxM行列の形に整形できたので、octaveやらに読み込ませることができます。