気に入っている猫の写真を加工してみたので、
ソースコードを置いておきます。
そのソースコードが具体的に何をしているのかということを推測してみたので、
それも書いておきます。
画像加工やPHP言語はまるで専門ではないので、
信憑性はあまりありませんが、
基本的な考え方や方向性は外していないと思います。
これを
こんなふうに、
「ラフにトレースしたものをスキャナで取り込んだような感じ」
にしてみました。
本当のところは輪郭線だけを取り込みたかったのですが、
まあ、これはこれで味があるからいいかと思いました。
<?php //ヘッダ処理 header ("content-type: image/png");
//元画像読み込み
//加工
//表示 |
|
最初はこれです。
imagefilter ( $image , IMG_FILTER_EDGEDETECT ); |
エッジ抽出をすることにより、
こうなります。
imagefilter ( $image , IMG_FILTER_GRAYSCALE ); |
念のためにグレースケールに変換しておきます。
あまり変化したようには見えませんが、
たまに変化する画像もあります。
imagefilter ( $image , IMG_FILTER_SMOOTH , 8 ); |
スムージングをして、ゴミを取り除く努力をします。
少しぼやけてくれました。
imagefilter ( $image , IMG_FILTER_BRIGHTNESS , 20 ); imagefilter ( $image , IMG_FILTER_CONTRAST , -255 ); |
この二行で二値化をします。
白と黒に多分きっぱり分かれてくれています。
imagefilter ( $image , IMG_FILTER_EDGEDETECT ); |
この一行だけで最終出力が出てくるかと期待したのですが、
駄目でした。
PHPの中身はよく分かりません。
画像を見るかぎりでは、こんなフィルタをかけているのかなと思いました。
ちょっとこの「フィルタ」という概念について、
細かく説明します。
コンピュータ上での画像は、ピクセルごとに輝度値が与えられています。
赤・緑・青ごとに値が割り当てられています。
(6×6ピクセルというとても小さな画像だと思ってください。)
この画像の赤で示した部分に、フィルタをかけるとすると、
こんな計算をすることになります。
この計算結果の値を
「この赤い部分にフィルタをかけたときの出力」
とします。
なんのことか分からないと思いますが、
まあ、とにかく、
「かけ算をして」「合計している」
ということを頭に入れておいてください。
(内積の求め方と同じです。)
具体的な意味はこれから説明していきます。
縦線です。
横線や斜めも考えられますが、とりあえずは縦です。
そして、エッジは決してのっぺりとはしていません。
だから、典型的にエッジでない画像の輝度はこんな感じになります。
ここで、さきほどのような3×3のフィルタをかけて、
「典型的なエッジ」の出力値を大きくして、
「典型的にエッジでないやつ」の出力値を小さくしたいのです。
そうすると、慣れてくるとこんなフィルタが思いつきます。
このフィルタを実際に適用してみるとこうなります。
見事に、縦線は大きな出力になり、
のっぺりな画像はゼロになってくれました。
縦線については逆向きも考えねばなりませんが、
これは、絶対値をとって解決します。
さらに横線はこういうフィルタを使って絶対値をとります。
90度傾けただけです。
さて、斜めはどうするかという問題ですが、
ピタゴラスの定理で斜辺の長さを求めるような感じで解決します。
(数学的には「L2ノルム」などというと通じます。)
これで、縦横を利用して斜めも解決されています。
この式だと絶対値をとる必要はなくなります。
というわけで、こういうフィルタを「Sobelフィルタ」というらしいです。
エッジの部分で輝度が高くなるはずですが、
PHPのエッジ抽出フィルタでは、
それをさらに白黒反転させているように見えます。
そして、エッジでないと判断した部分を
灰色にしているようにも見受けられます。
(つまりSobelフィルタよりも複雑なことをしているようです。)
PHPの中身まではよく分かりません。
それから、PHPがSobelフィルタを使っているかどうかも知りません。
また、RGBのカラーごとにフィルタを使っているのか、
グレースケールに直してからフィルタを使っているのかも分かりません。
ただ、考える方向性はだいたい似たようなもののはずです。
とにかくエッジ抽出で、猫の画像はこうなりました。
imagefilter ( $image , IMG_FILTER_GRAYSCALE ); |
白黒の濃淡画像にする方法は多分誰でも分かると思います。
赤と緑と青の輝度値を足して3で割ります。
猫の画像はこうなりましたが、
あまり変わっているようには見えません。
(別の画像では変わりました。)
imagefilter ( $image , IMG_FILTER_SMOOTH , 8 ); |
ここでもまた、フィルタを使います。
このフィルタはPHPがどういうものを使っているのか推測ができません。
スムージングのためのフィルタは種類がたくさんあるからです。
おそらく、最も簡単なものは、以下のものです。
3×3ピクセルの平均をとることになります。
平均をとるとなだらかになって、
ごましおのようなノイズがとれるということは、
直観的に分かると思います。
また、「メディアンフィルタ」というフィルタもあります。
これは注目ピクセルの近くのピクセルの中央値をとるフィルタです。
先ほどまでとは違い、かけ算や足し算をしません。
スムージングをした結果、ちょっとぼやけました。
実は、PHPには二値化専用の関数があるのではないかと期待したのですが、
見当たりませんでした。
もしかしたら、あるかもしれません。
ちなみに、二値化の基本は「閾値(しきいち・いきち)」を設定して、
それよりも上なら白く、それよりも下なら黒くする、というものです。
この閾値は人間が入力することもありますし、
機械が自動設定することもあります。
今回は自動設定をしませんでした。
とにかく、二値化のための関数が見当たらなかったので、
コントラストを調節する関数で代用しました。
コントラストというのは「輝度のくっきり具合」です。
コントラストを最大に調整すると白と黒にはっきり分かれてくれます。
imagefilter ( $image , IMG_FILTER_CONTRAST , -255 ); |
と思ってこの行を書いてみたのですが、
どうにも灰色の部分が残ります。
おそらく、エッジ抽出のときにPHPが勝手に作った灰色だろうと思います。
この灰色を消すべく、輝度を調整しました。
(このあたり、泥臭いです。)
imagefilter ( $image , IMG_FILTER_BRIGHTNESS , 20 ); imagefilter ( $image , IMG_FILTER_CONTRAST , -255 ); |
輝度をいくらか明るくすれば、灰色が白に近づいてくれるので、
二値化したときに白くなってくれるだろうという算段です。
(数学的には二値化のための閾値を変えるという意味になります。)
結果の画像はこの輝度調節の値にかなり左右されるのですが、
何枚か試してみた感じでは、
この値でそこそこうまく動いてくれるっぽいです。
というわけで、二値化した結果はこうなりました。
「トレースしてスキャナで取り込んだ感じ」の完成です。