Javaプログラミング

トップページ      |      目次
←前へ      次へ→

  • 画像処理
    • 画像の取り扱い
    • Javaで画像を取り扱うにはJava2Dのようなライブラリを使うなど様々な方法があるが、ここではgetRGB,setRGBメソッドを使って 直接画素値を操作する。

      BufferedImage内の画素にアクセスするにはgetRGB()メソッドとsetRGB()を使う。書式はそれぞれ
      public int getRGB(int x,int y) public void setRGB(int x,int y,int rgb) 座標値x,yにある画素値をそれぞれ取得、設定する。 使用例は以下の通りである。

      
      int[][] rgb=new rgb[height][width];
      //画素の取得
      for(int v=0;v < height;v++) {
        for(int u=0;u < width;u++) {
          rgb[v][u]=image2.getRGB(u, v);
        }
      }
      //画素の設定
      for(int v=0;v<height;v++) {
        for(int u=0;u<width;u++) {
          image1.setRGB(u, v, rgb[v][u]);
        }
      }
          
        
      画素値はint型で返され、デフォルトでは、アルファ値付きのRGB カラーモデル (TYPE_INT_ARGB)になっている。
      RGBカラーモデルとは赤(R:Red)、緑(G:Green)、青(B:Blue)で構成される色表現で通常各要素に256段階(8ビット)毎が割り当てられている。
      アルファ値(A:Alpha)とは透明度を表すパラメータで256段階で0が透明、255が完全な不透明になる。
      例えば不透明な赤(R=255,G=0,B=0,A=255)をint型のpixに直接代入するにはは次の値をとる
      
      int pix=0xffff0000;
          
          
      このままだと扱いづらいのでR成分、G成分、B成分に分解・合成する。
      
      int rgba;
      int r;
      int g;
      int b;
      int a;
      
      //分解
      a=rgba>>24;
      r=rgba>>16&0xff;
      g=rgba>>8&0xff;
      b=rgba&0xff;
      
      //合成
      rgba=(a<<24)|(r<<16)|(g<<8)|b;
          
          

      • サンプルプログラム
        • 画像ファイルを読み込み、ネガポジ反転して、書き出すプログラム
          (※ファイル書き込みが発生するので実行には注意が必要)
        • 
                      //画素の取得
                      for(int v=0;v<height;v++) {
                        for(int u=0;u<width;u++) {
                          rgba[v][u]=image2.getRGB(u, v);
                        }
                      }
                  
                      //画素の分解
                      for(int v=0;v<height;v++) {
                        for(int u=0;u<width;u++) {
                          a[v][u]=rgba[v][u]>>24&0xff;
                          r[v][u]=rgba[v][u]>>16&0xff;
                          g[v][u]=rgba[v][u]>>8&0xff;
                          b[v][u]=rgba[v][u]&0xff;
                        }
                      }
                  
                      //ネガポジ反転(255-画素値)
                      for(int v=0;v<height;v++) {
                        for(int u=0;u<width;u++) {
                          r[v][u]=255-r[v][u];
                          g[v][u]=255-g[v][u];
                          b[v][u]=255-b[v][u];
                        }
                      }
                  
                      //画素の合成
                      for(int v=0;v<height;v++) {
                        for(int u=0;u<width;u++) {
                          rgba[v][u]=(a[v][u]<<24)+(r[v][u]<<16)+(g[v][u]<<8)+b[v][u];
                        }
                      }
                  
                      //画素の設定
                      for(int v=0;v<height;v++) {
                        for(int u=0;u<width;u++) {
                          image1.setRGB(u, v, rgba[v][u]);
                        }
                      }
                  
        • 実行結果
        • 実行結果 サンプルコード

    • 画素を扱う上での注意事項
      • 計算誤差
      • コンピュータを扱う上で避けて通れないのが計算誤差である。画像処理においても例外ではなく計算誤差によって結果が大きく異なることがある。 特に乗算や除算を含む計算には注意が必要である。 次のコードは画素値に定数で割って、その後同じ数を掛けている。 一見すると問題ない処理に見えるが、意図的でなければ推奨されない。(ただし2次元配列r,g,bは整数型とする)
        
              
        for(int v=0;v<height;v++) {
          for(int u=0;u<width;u++) {
            r[v][u]/=128;
            g[v][u]/=128;
            b[v][u]/=128;
        
            r[v][u]*=128;
            g[v][u]*=128;
            b[v][u]*=128;
          }
        }
              
      • 実行結果
      • 実行結果 右が元画像で左が上の処理の実行結果である。除算によって大きく情報が損なわれていることがわかる。 一方で同じ処理を一度実数型のデータに入れて計算するとどうなるか
        
              
        for(int v=0;v<height;v++) {
          for(int u=0;u<width;u++) {
            double pr=r[v][u];
            double pg=g[v][u];
            double pb=b[v][u];
        
            pr/=128.0;
            pg/=128.0;
            pb/=128.0;
        
            pr*=128.0;
            pg*=128.0;
            pb*=128.0;
        
            r[v][u]=(int)pr;
            g[v][u]=(int)pg;
            b[v][u]=(int)pb;
        
          }
        }
                
      • 実行結果
      • 実行結果 右が元画像で左が上の処理の実行結果である。多少の計算誤差はあるにせよ、整数型ほどには情報の損失は見られない。 計算の過程で整数が混じることがあるが誤差を避けるため、実数型の表記が必要(例えばaを2で割る時はa/2ではなくa/2.0にするなど)
      • オーバーフロー
      • オーバーフローとは割り当てられたビット数に対して計算結果の絶対値が大きくなる状態で何らかの処置を施さない場合、予期しない結果となりうる。 1画素に割り当てられた1色あたりのビット数は8ビットなので画素値の最大値は255となる。何らかの計算によりこの値を超えた場合オーバーフローとなり、 そのまま放置すると画像に悪影響を及ぼす。 以下のコードは画素値に20を加えることで輝度をあげる操作をしている。
        
        
        for(int v=0;v<height;v++) {
          for(int u=0;u<width;u++) {
            double pr=r[v][u];
            double pg=g[v][u];
            double pb=b[v][u];
        
            pr+=20.0;
            pg+=20.0;
            pb+=20.0;
        
            r[v][u]=(int)pr;
            g[v][u]=(int)pg;
            b[v][u]=(int)pb;
        
          }
        }
              
      • 実行結果
      • 実行結果 右が元画像で左が上の処理の実行結果である。オーバーフロー処置をしていないため画像に悪影響を及ぼしている。
        以下の処置をすることによりオーバーフローによる画像への悪影響を回避できる。
        
        
        	public double clip(double x) {
        		x=x<0?0:x;
        		x=x>255?255:x;
        
        		return x;
        	}
              
        画素の値が255より大きい場合は255、0より小さいばあいは0に設定するという簡単な処理。 今回は最大値を超えた場合だが当然計算の結果が負になることもありうる(負のオーバーフロー)
        
        
                  for(int v=0;v<height;v++) {
                    for(int u=0;u<width;u++) {
                      double pr=r[v][u];
                      double pg=g[v][u];
                      double pb=b[v][u];
              
                      pr+=20.0;
                      pg+=20.0;
                      pb+=20.0;
              
                      r[v][u]=(int)clip(pr);
                      g[v][u]=(int)clip(pg);
                      b[v][u]=(int)clip(pb);
              
                    }
                  }
                        
      • 実行結果
      • 実行結果 右が元画像で左が上の処理の実行結果。オーバーフロー対策により画像の損失が回避されている。

    ←前へ      次へ→