▲ 003_数値計算(行列の前に)
■ 003_数値計算(行列の前に)
2002/08/24 Nishimura Hiromi

 それでは mc でできる数値計算について紹介しましょう。 mc は行列演算機能を持ち、数値計算も行列演算を中心に使います。となると mc の計算機能を使うには行列の勉強をしなくてはなりません・・・・・・というのは嘘で行列を知らなくても mc は使えます。というか mc を使っていると行列の事が解るようになります。というか、行列の方が人間的で、逆に普通のプログラミング言語が非人間的であることが mc を使う事によって判ると思います。

● 行列演算の前に

 行列演算を意識しないで mc を使ってみましょう。下記画像は私の部屋の工作机をデジカメで撮影した二枚の画像です。同じように撮影したつもりですがだいぶ明るさが違っているようです。下記5行のプログラムは画像ファイルを画像変数に読み込み画像の大きさを 1/6 してから表示するものです。(メニューバーのその他からカレントディレクトリを設定してから実行)

a# = loadPicture("PICT0016.JPG");

b# = loadPicture("PICT0017.JPG");

a# = changePictSize(a#,1/6);

b# = changePictSize(b#,1/6);

a#;b#;"";

.OK.

 汚い机の全面に色々な工具をぶら下げているのが分かると思います。これは HDD を分解して手に入れた磁石(ボイスコイルモータ)を利用し工具をくっつけているものです。磁石を利用すると工具が無くならないので便利です。皆さんも試してみたら如何でしょうか。

 さて、この二枚の画像は左右で一部違う所があります、どこでしょうか? 皆さんならどうやって違いを見つけますか?

 私なら左の画像を右目で、右の画像を左目で交差するように見ます。すると違う所が浮かび上がって見えます。浮かび上がって見える。いったい頭の中で何をしているのでしょう? 人間は右目に写る画像と左目に写る画像に差があると立体であると認識するようになっているようです。だから浮き上がって見えるようです(一部が消えていても立体?とは変ですね!)。

 ではコンピュータで二枚の画像の違いをどのようにして処理したら識別できるのでしょうか。実は人間と同じよう二枚の画像の差を計算すると判ります。

(b#-a#)*10;

.OK.

 上記は画像 a#,b# の差を計算し、さらに差の画像が暗いので10倍に明るくしています。ノイズが多いですが中央左側に白い所が見えます(あまりよく判らない)。これ秋葉原の秋月電子で購入したペン型デジタルテスターで、左(a#) には写っていますが右(b#)には写っていません。二枚の画像の引き算をしたことで白く見えるようになってきました。このようにコンピュータでも画像の引き算をすると違いが判るようになります。違いを見つける方法は引き算と覚えておくと便利です。

 でも上の引算画像は少し汚いですね。それに差の画像を 10 倍しているのでオーバーフローしています。もう少しはっきり、また綺麗に違いを出すため画像を数値(オーバーフローしない浮動小数点)に変換し同じ事をしてみましょう。

a = a#;

b = b#;

c = b-a;

RGB = popRGBColor(0);

densityPlot(c,|500,0|);

.OK.

 a = a# で画像を数値に変換しています。次に c = b-a で二枚の数値画像データの差を計算しています。次に densityPlot 関数で数値データを画像に変換しています。 この結果は前の b# - a# の演算と同じであまり違いがありません。でも今度は数値になっていますから色々な処理を試す事ができます。そこで smooth 関数を使って細かな余分なノイズを削除してみましょう。

a = a#;

b = b#;

c = b-a;

d = smooth(c);

densityPlot(d,|200,0|);

.OK.

 こんどははっきりしてきました(映画プレデターの赤外線画像みたいですね!)。細かなノイズを smooth 関数で削除すると違いがよく判るようになりました。

 でも実は差分よりもっとはっきり二枚の画像の違いを調べる方法があります。それは画像の除算です。差だと撮影条件により一方が明るかったり暗かったり差があるとそれだけで色々な場所に差が検出されてしまいます。でも変化率であれば本当に違いがあった部分だけが強調されます。それでは変化率の (b-a)/a を計算し表示してみましょう。

a = a#;

b = b#;

c = (b-a)/a;

d = smooth(c);

d#= densityPlot(d);

a#;b#;"";d#;"";

.OK.

 変化率で画像をみるとよりはっきり二枚の画像の違いをみつけることができました。このように画像の引算だけではできなかった処理が数値に変換すると色々な処理が自由にできるようになります。これが mc を使った画像処理です。

 上記例ではデジカメ画像ですが。例えば癌細胞の移動を経時的に追うのに利用できると思いませんか?一般には差を使っていますが実は変化率の方がより明確に目標をとらえる事ができます。

 さて、この処理の中に行列演算は使っていないように見えます。でも実はしっかり使っているのです。

● 配列と行列

 コンピュータでは複数のデータを処理する時、データを保存する配列というものを使います。この配列とはメモリでデータの格納場所を意味します。上記画像でいえば画像の画素(ピクセル)データを保存する場所が配列です。

 これに対し行列とは何でしょうか。行列も配列と同じようにデータを格納する場所を持っています。上記画像でいえば画像のピクセルデータを保存するのに配列と同じように行列を使う事ができます。だからデータを保存する点では配列も行列も違いはありません。

 ところが配列はデータを格納する場所を提供しているだけです。これに対し行列はデータを格納するだけではなくデータの演算まで定義されています。これが配列と行列の大きな違いです。

 「と言っても何のことやら?」と思うかもしれません。でも配列を使って画像処理をするにはデータ(ピクセル)一つ一つに対してプログラムで演算を定義する必要があります。これに対し行列は演算が定義されているのでプログラムする必要がありません。前記のプログラムに示すよう単に b-a で画像の差が計算できるのです。

 だから前記のプログラム例で b#-a# とか b-a とした時点で行列演算の処理をしていることになります。さて行列演算機能を持つプログラム言語と持たないものとでは、どちらが人間の感性というか自然な考えに一致しているでしょうか。画像 b# と画像 a# の差を計算するに b#-a# とした方が自然であると私は思います。それに繰り返し制御文を使わないでたった一行で計算ができます。

 処理の内容が直感で判る点、バグの温床になりやすい制御文が不要な点、プログラムが短くなる点、そしてインタープリタでも処理が高速な点、これらの特徴から私は行列が好きです。

● 行列はあまり気にしない方が良いでしょう

 以上のように、行列演算というのは何も特殊な演算ではなく人間の考えにあった素直な演算です。だから行列演算を勉強するという考えではなく、人間の素直な考えを実行させる、それが行列演算を使ったプログラミングだと思って下さい。