OpenCVjs読解
今日も今日とてOpenCVjs.昨日ロードはできたと言ったけどあれは夢だったようで,以前用意した関数でうまく行ったように見えただけでした.
でも今日で割と理解は深まった.
IplImage型
//canvasのRGBA値は0~255の値しかもてないため専用の画像データ型を用意 var IplImage = function(){ width: 0; height: 0; canvas: null; imageData: null; RGBA: null; }
OpenCVにある同名の型を再現するためのデータ型.1つの画像を canvas, imageData, RGBA の3形式で保持するという贅沢な使い方をしていることが想定される.というのも多分,以前も書いたようにJavaScriptで画像を動的にいじるには canvas しか無いわけで,一方で canvas に描かれた画像を得るには canvas.context のメソッドである getImageData が必要.しかしこれは画素値をまとめて得られる反面細かな処理がしにくい.そこで画素値を直接いじるために RGBA を用意しておく. RGBA と imageData は現状のところ使い分けてる箇所が見当たらずほぼ同じものが入っている.
各画像データの役割や変換を完結にまとめるとこんな感じだと思う.
オレンジと紫の矢印の関数は便利で,img ⇔ RGBA を自動で行ってくれる.フィルタは基本的に,iplImageを複製して表示用のデータを用意 → 画素値をいじる → cvShowImage で表示 で行われる.紫の cvGetIplImageAtImg は「ローカル環境では動かない」となっていたんだけどふつーに動いた.ローカル環境ってのはローカルページであってローカルサーバではないってことなんだろうな.
参考
createImageData, getImageData, putImageData メソッド - Canvasリファレンス - HTML5.JP
進捗
画像のロード
前述した変換を使って canvas → IplImage を行えば良いはずだけど,ウィンドウが違うせいなのか getImageData がうまく動作しなかったので,一旦 img に変換した後 img → IplImage と変換する方法をとった.(getDataUrl = function(){ //親ウィンドウがなければアラート if(!window.opener||window.opener.closed){ window.alert('no parent window'); return 'no parent window'; } else{ //親ウィンドウの canvas を取得 var srcImg = window.opener.document.getElementById("srcImg"); dataurl = srcImg.toDataURL(); //imgに配置 document.getElementById("content").src = dataurl; //img → IplImage iplImage = cvGetIplImageAtImg("content"); } })();
フィルタ
画像処理のフィルタというのはノイズを除去するようなものやコントラストを変えるなど,様々な画素値を持つ画像に対して処理するから多様な結果が得られるのであって,今回のような全体で最大8色しか登場しないような画像とはあまり相性が良くなかった.なので多様性を増すようなフィルタを自作で実装してみた.前述した様に,画像を複製 → 画素値をいじる → cvShowImage になっている.function cvChaos2(imgId, srcImage){ //画像サイズを指定して画像領域を確保 var dstImage = cvCreateImage(srcImage.width, srcImage.height); //画素値をいじる for(var i = 0 ; i < srcImage.height ; i++){ for(var j = 0 ; j < srcImage.width ; j++){ //元画像の画素値 var R = srcImage.RGBA[(j+i*srcImage.width)*CHANNELS+0]; var G = srcImage.RGBA[(j+i*srcImage.width)*CHANNELS+1]; var B = srcImage.RGBA[(j+i*srcImage.width)*CHANNELS+2]; var A = srcImage.RGBA[(j+i*srcImage.width)*CHANNELS+3]; //出力画像の画素値格納用 var r,g,b; //分岐用 var key = ((R===0)?0:1)+((G===0)?0:1)*2+(B===0?0:1)*4; dstImage.RGBA[(j + i * dstImage.width) * CHANNELS + 3] = 255; //alpha //透明は透明 if(A===0){ r=0; g=0; b=0; dstImage.RGBA[(j + i * dstImage.width) * CHANNELS + 3] = 0; //alpha } else switch(key){ //白黒はそのまま case 0: r=g=b=0;break; case 7: r=g=b=255;break; //色付きは色味を保ったまま変化 case 1: r=255;g=Math.floor(Math.random()*256);b=Math.floor(Math.random()*256);break; //中略 } dstImage.RGBA[(j + i * dstImage.width) * CHANNELS + 0] = r; //R dstImage.RGBA[(j + i * dstImage.width) * CHANNELS + 1] = g; //G dstImage.RGBA[(j + i * dstImage.width) * CHANNELS + 2] = b; //B } } //imgIdで指定したimgタグに画像を転送 cvShowImage(imgId, dstImage); }
このフィルタを通すとrgb(255,0,0)はrgb(255,0~255,0~255)に,rgb(255,255,0)はrgb(0~255,0~255,0)に変化する.特徴となる値をそのままに残りを変化させることで,色味を残したまま様々に変化し進捗の図で示したような色が得られる.
今後
・透視投影変換がまだなのでそれを実装・背景に関する処理を追加
・背景が黒になるフィルタを透明を維持する様に変更
・綺麗なフィルタをもう少し増やしたい