2017年2月6日月曜日

Histogramフィルターの作成

今回からは画像処理の具体的な方法と実装について書いていきたいと思います。今回実装するのは画像のヒストグラムを表示するフィルターです。ヒストグラムは、各カラーの明るさのレベル別にピクセル数をグラフ化し、画像内のピクセル分布を示したものです。具体的には以下のURLをご覧ください。
https://helpx.adobe.com/jp/photoshop/using/viewing-histograms-pixel-values.html
Photoshop上では下の図のようにヒストグラムが表示されます。


 ヒストグラムを表示することで、画像処理によって色の分布がどのように変化するかを分析することができます。そのため、画像処理として最初に導入したいと思います。
 
 まず、Tutorial1Activityクラスと同じ場所に、HistogramFilterクラスを作成します。下の図のように選択してから、右クリックでNew→Java Classを選択します。
その後、クラスの内容を記入する画面では、下の図のようにHistogramFilterと記入し、OKを押します。
これでTutorial1Activityクラスと同じ場所に、HistogramFilterクラスが作成されました。
 次に、HistogramFilterクラスの内容を記入していきます。第一にfilterメソッドを作成します。比較的長いですが、これはred、green、blueそれぞれを分けて処理しているためです。各色で行っている処理は同じです。
filter
   public int[] filter(int[] imageArray, int width, int height) {
        int tone[][]=ToneCalculator(imageArray);//①
        int rate[][]=new int[3][256];
        int[] oimgArray =imageArray.clone();//②
        int red=0;
        int green=0;
        int blue=0;
        int maxr=0;
        int maxg=0;
        int maxb=0;
        //red--------------------------------------
        for(int i =0;i < 256;i++){
            if(maxr < tone[0][i]){
                maxr=tone[0][i];//③
            }
        }
        for(int i =0;i < 256;i++){
            rate[0][i]=(tone[0][i]*50)/maxr;//④
        }
        for(int i =0;i < 50;i++){
            oimgArray[(height-2-i -102)*width]=255*16777216;//⑤
            for(int n =0;n < 256;n++){
                oimgArray[(height-2-i-102)*width+n+1]=255*16777216+255*65536+255*256+255;//⑥
            }

        }
        for(int n =0;n < 256;n++){
            red=rate[0][n];
            if(red > 0){
                for(int rn=1;rn < red;rn++){
                    oimgArray[(height-1-rn-102)*width+n+1]=255*16777216+255*65536;//⑦
                }
            }
        }
        for(int n =0;n < 257;n++){
            oimgArray[(height-1-102)*width+n]=255*16777216;//⑧
        }
        //green--------------------------------------
        for(int i =0;i < 256;i++){
            if(maxg < tone[1][i]){
                maxg=tone[1][i];
            }
        }
        for(int i =0;i < 256;i++){
            rate[1][i]=(tone[1][i]*50)/maxg;
        }
        for(int i =0;i < 50;i++){
            oimgArray[(height-2-i-51)*width]=255*16777216;
            for(int n =0;n < 256;n++){
                oimgArray[(height-2-i-51)*width+n+1]=255*16777216+255*65536+255*256+255;
            }

        }
        for(int n =0;n < 256;n++){
            green=rate[1][n];
            if(green > 0){
                for(int gn=1;gn < green;gn++){
                    oimgArray[(height-1-gn-51)*width+n+1]=255*16777216+255*256;
                }
            }
        }
        for(int n =0;n < 257;n++){
            oimgArray[(height-1-51)*width+n]=255*16777216;
        }
        //blue--------------------------------------
        for(int i =0;i < 256;i++){
            if(maxb < tone[2][i]){
                maxb=tone[2][i];
            }
        }
        for(int i =0;i < 256;i++){
            rate[2][i]=(tone[2][i]*50)/maxb;
        }
        for(int i =0;i < 50;i++){
            oimgArray[(height-2-i)*width]=255*16777216;
            for(int n =0;n < 256;n++){
                oimgArray[(height-2-i)*width+n+1]=255*16777216+255*65536+255*256+255;
            }

        }
        for(int n =0;n < 256;n++){
            blue=rate[2][n];
            if(blue > 0){
                for(int bn=0;bn < blue;bn++){
                    oimgArray[(height-1-bn)*width+n+1]=255*16777216+255;
                }
            }
        }
        for(int n =0;n < 257;n++){
            oimgArray[(height-1)*width+n]=255*16777216;
        }
        return oimgArray;
    }

①では、3色256諧調分の配列を作り、画像のRGB各色の値について配列の対応する場所を増やしていく処理を行います。下にToneCalculatorメソッドを記載します。
    public int[][] ToneCalculator(int[] imageArray){
        int tone[][]=new int[3][256];
        int length = imageArray.length;
        int rgb;
        int red,green,blue;
        for(int i =0;i < length;i++){
            rgb = imageArray[i];
            red = (rgb >> 16) & 0xff;
            green = (rgb >> 8) & 0xff;
            blue = (rgb) & 0xff;
            tone[0][red]+=1;
            tone[1][green]+=1;
            tone[2][blue]+=1;
        }
        return tone;
    }

 ②では出力用にint配列をコピーしています。以降では赤色についてのみ述べます。
 ③では最も多く出現した色の値を求めています。この値がヒストグラム上での上限値となります。
 ④ではそれぞれの色の値(0~255)について、出現した色の値を最頻色の値で割ることで、最頻色の値に対する割合を出しています。さらに、50をかけることで0~50の51段階に変更しています。
 ⑤は画像上にヒストグラムを作る際の、左端の黒いバーを描写しています。
 ⑥は同様にヒストグラムの白い背景を描写しています。
 ⑦ではヒストグラムの描写を行っています。赤では下限位置から④でえられた各色の出現割合(0~50)の分だけ赤いピクセル(255,0,0)を描写しています。
 ⑧では下限を示す黒い横棒を描写しています。
 以上の操作を行うことにより、画像にヒストグラムを追加することができます。最後に、Tutorial1Activityクラスのint imageArray[]=ba2ia(ba);とba=ia2ba(imageArray);の間(前回の④と⑤の間)に以下の文を加えます。
 
imageArray=new HistogramFilter().filter(imageArray, imagewidth, imageheight);

今後、同じような文を同じ位置に加えていきますが、この文(HistogramFilter)は最後に記入することをお勧めします。画像処理後のヒストグラムを取得するためです。
 下に以上の編集を行った結果得られる画像を示します。画像の左下にヒストグラムが表示されます。この図ではRGB各色で白飛び(値が255)や黒つぶれ(値が0)が発生していることがわかります。

以上でヒストグラムの追加の説明を終わります。図のようにヒストグラムの画像に占める割合が大きいので、必要に応じて50段階ではなく30段階にすることや、横幅を256ではなく128にするなどの調整を行ってください。

0 件のコメント:

コメントを投稿