もはや内容はどうでもよくてとりあえず数を増やそう作戦に移行しようかとしてる画像処理ブログ7回目です。
1ピクセルずつ画素をいじれるようになると、既存のメソッドを組み合わせるだけでなく、自分でメソッドを作れたり、論文を読んでそのまま実装できるようになったりと応用範囲が広がります。
ではサンプルですが、以下の256*256で真っ赤の画像を生成して表示させるプログラムは以下の通りです。
![赤](https://stat.ameba.jp/user_images/20121103/02/sakiyamak/a3/8d/j/t02200220_0256025612267616287.jpg?caw=800)
function MakeRed(imgId){
//画像サイズを指定して画像領域を確保
var width = 256;
var height = 256;
var iplImage = cvCreateImage(width, height);
//全画素に真っ赤(255, 0, 0)を代入
for(var i = 0 ; i < height ; i++){
for(var j = 0 ; j < width ; j++){
iplImage.RGBA[(j + i * iplImage.width) * CHANNELS] = 255; //R
iplImage.RGBA[1 + (j + i * iplImage.width) * CHANNELS] = 0; //G
iplImage.RGBA[2 + (j + i * iplImage.width) * CHANNELS] = 0; //B
iplImage.RGBA[3 + (j + i * iplImage.width) * CHANNELS] = 255; //alpha
}
}
//imgIdで指定したimgタグに画像を転送
cvShowImage(imgId, iplImage);
}
OpenCVjsはIplImage型で画像を扱うので、まずcvCreateImageメソッドで指定の画像サイズのIplImage型を生成しています(javascriptで型というのも変なのかもしれませんが)
そして画素はIplImage型のRGBA配列に代入されます。
RGB表色系の場合、画像を全部真っ赤にするには各ピクセルに代入する値をR=255,G=0,B=0とするのですが、IplImage型は(というよりjavascriptのcanvasは)この三原色の値に加えてalpha値というものが保存された四原色となっています。
このalpha値はそのピクセルの透明度を表しており、IplImage型だとalphaの値を0にするとそのピクセルが透明になり、128で半透明、255で色がそのまま表示されます。
基本はalpha値は全て255にしておいてください。
で、各ピクセルのアクセスですが二重for文内で
iplImage.RGBA[(j + i * iplImage.width) * CHANNELS] = 255; //R
iplImage.RGBA[1 + (j + i * iplImage.width) * CHANNELS] = 0; //G
iplImage.RGBA[2 + (j + i * iplImage.width) * CHANNELS] = 0; //B
iplImage.RGBA[3 + (j + i * iplImage.width) * CHANNELS] = 255; //alpha
のようにアクセスしているのが分かると思います。
これは最初戸惑いますが画像処理では定番のアクセス法でして、「j」と「i」がそれぞれアクセスしている画素のx,y座標を意味しています。
またCHANNELSは四原色の4を意味しています。
なぜこの計算でピクセルにアクセスできているのかといいますと、画像というものは二次元の情報ですがメモリ上では一次元の情報として扱われているのが原因です。
IplImage型では画像を左上から右下のラスタスキャン順で配列に画素を代入しており、また1画素内でもRGBA順で配列に代入されてます。
メモリに配置された画像を図で表すとこんな感じですね。
![メモリ図](https://stat.ameba.jp/user_images/20121103/02/sakiyamak/4b/ec/p/t02200053_0730017612267616288.png?caw=800)
![メモリ図](https://stat.ameba.jp/user_images/20121103/02/sakiyamak/4b/ec/p/t02200053_0730017612267616288.png?caw=800)
まあ長々と説明しましたが結局のところ(j + i * iplImage.width) * CHANNELSを覚えておけよってことですねw
ちなみにfor文の中身を次のようにすると
iplImage.RGBA[(j + i * iplImage.width) * CHANNELS] = j; //R
iplImage.RGBA[1 + (j + i * iplImage.width) * CHANNELS] = 0; //G
iplImage.RGBA[2 + (j + i * iplImage.width) * CHANNELS] = 0; //B
iplImage.RGBA[3 + (j + i * iplImage.width) * CHANNELS] = 255; //alpha
こんな画像が出力されます。
![グラデーション](https://stat.ameba.jp/user_images/20121103/02/sakiyamak/7c/08/j/t02200220_0256025612267616286.jpg?caw=800)
これで1ピクセルごとの処理が出来るようになったので、画像処理の本とかに載ってるアルゴリズム通りに数値を代入させていくと色々なメソッドが自分で実装できます。
そして、基本的なinstagram風フィルタを作るために必要なOpenCVjsの説明は終わったので、photoshopでinstagram風フィルタの作り方を再現されているデザイナの方々の手順をそのままプログラムにしてしまえば無限にお洒落フィルタが自作できます。
良いフィルタが出来たら教えてください。