2017年2月9日木曜日

誤差拡散法(ErrorDiffusionFilter)

 今回はディザリング手法の一つである誤差拡散法フィルタを作成します。誤差拡散法は画素をある閾値で白黒に変換したとき、変換後の値と元の値
との誤差を次の画素の変換で使うものです。例えば、閾値が127のとき、200の画素は白(255)に変換されますが、誤差として-55が発生します。この誤差を次の画素の元の値に伝播させていきます。
伝播の方法はいくつかありますが、今回はフロイド-スタインバーグ・ディザリング(Floyd–Steinberg dithering)を用いたいと思います。フロイド-スタインバーグ・ディザリングでは以下の図のように誤差を伝播させます。
http://koujinz.cocolog-nifty.com/blog/2009/04/post-a316.html
先ほどの誤差-55ならば、一つ右の画素には -24が伝播されます。その他の誤差拡散法の手法については以下のページ(英語)を参照してください。
https://en.wikipedia.org/wiki/Dither

Tutorial1Activityクラスと同じ場所にErrorDiffusionFilterクラスを作成します。その後、filterメソッドに以下のコードを入力します。
    public int[] filter(int[] imageArray, int width, int height) {
        int length = width*height;
        int[] oimgArray = new int[imageArray.length];
        int rgb;
        int red,green,blue;
        int y_val;
        for(int i =0;i < length;i++){
            rgb = imageArray[i];
            red = (rgb >> 16) & 0xff;
            green = (rgb >> 8) & 0xff;
            blue = (rgb) & 0xff;
            y_val = ( 2 * red + 4 * green + blue ) / 7;//①
            oimgArray[i]=y_val;
        }
        int point=0;
        int error=0;
        for(int y=0;y < height; y++){
            for(int x=0;x < width;x++){
                point=x+y*width;
                if(oimgArray[point] > 127){//②
                    error=oimgArray[point]-255;//③
                    oimgArray[point]=255;
                }else{
                    error=oimgArray[point];//④
                    oimgArray[point]=0;
                }
                if((x+1) < width){
                    oimgArray[point+1]+=error*7/16;//⑤
                }
                if((y+1) < height){
                    if((x-1) >= 0){
                        oimgArray[point-1+width]+=error*3/16;
                    }
                    oimgArray[point+width]+=error*5/16;
                    if((x+1) < width){
                        oimgArray[point+1+width]+=error*1/16;
                    }
                }
            }
        }
        for(int i =0;i < length;i++){
            y_val = oimgArray[i];
            oimgArray[i]=255*16777216+y_val*65536+y_val*256+y_val;
        }
        return oimgArray;
    }

 ①ではまず画像をグレイスケール変換しています。
 ②ではグレイスケールの色の値を127と比較し、それより大きい場合には白(255)小さい場合には黒(0)に変換しています。 そして、白の場合には③で色の値ー255をエラー値として、黒の場合には④で色の値そのものをエラー値として取得します。
 ⑤以降では③でフロイド-スタインバーグ・ディザリングの図にもとづいてエラー値を伝播させます。
 最後に、 Tutorial1Activityクラスのint imageArray[]=ba2ia(ba);の下に以下の文を記入します。
imageArray=new ErrorDiffusionFilter().filter(imageArray, imagewidth, imageheight);

以下に実機での実行結果を示します。白黒の二値であるにもかかわらず、グレイスケールにかなり近いように見えます。ただし、白や黒が連続しているところでは元画像にはなかった点が発生しています。

0 件のコメント:

コメントを投稿