next up previous
Next: 課題 Up: 5 画像処理 Previous: スライダーイベント

5.4 フーリエ変換

以下に知能機械情報学専攻のTAの鈴木義久君による 画像処理プログラムの例を紹介する.
図 12: フーリエ変換 ローパスフィルタ
\includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie1.eps} \includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie2.eps}
図 13: フーリエ変換 ローパスフィルタ
\includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie3.eps} \includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie4.eps}
図 14: フーリエ変換 ハイパスフィルタ領域とその処理結果
\includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie_high_mask.eps} \includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie_high_result.eps}
図 15: フーリエ変換 ノイズ付加,ハイパスフィルタ処理,ローパスフィル タ処理
\includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie6.eps} \includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie7.eps} \includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie8.eps} \includegraphics[width=7.8cm]{/home/inaba/eps/lecture/fig/fourie9.eps}
画像のフーリエ変換プログラムの例を以下に紹介する.
// FourieTest.java
import java.applet.Applet;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.awt.event.*;
import java.awt.image.*;
import com.sun.image.codec.jpeg.*;
import com.sun.image.codec.*;
import java.lang.Math.*;

/*<applet code="FourieTest.class"
  width="512" height="290">
  <param name="input_img" value="h6_gray.jpg">
  </applet>*/

//width="391" height="294"
public class FourieTest extends Applet{
  Image input_img;
  Image img1;
  Image img2;
  boolean fourie_flag=false;
  MediaTracker mt;
  int input_w=410,input_h=307;
  int w=512,h=256;
  int[][] input_color= new int[input_w*input_h][3];
  int[][] color= new int[w*h][3];
  double[][] fourie_r= new double[h][w];
  double[][] fourie_i= new double[h][w];

  Button btn_fourie =
  new Button("フーリエ変換/逆変換");
  Button btn_low =
  new Button("ローパスフィルタ");
  Button btn_high =
  new Button("ハイパスフィルタ");
  Button btn_noise =
  new Button("ノイズ付加");
  Button btn_reset =
  new Button("リセット");


  public void init(){
    mt=new MediaTracker(this);
    int tmp;
    setBackground(Color.white);
        
    input_img =
      getImage(getDocumentBase(),
               getParameter("input_img"));
            
    getColor(input_img,
             input_color, input_w, input_h);

    double rate =(double)input_w / (double)w;
    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        /*
          if(j>=(w-input_w)/2 && j<input_w+(w-input_w)/2){
          tmp=input_color[(j-(w-input_w)/2)+i*input_w][0];
          } else{
          tmp=0;
          }

          if(j<w/2){
          tmp=128;
          } else{
          tmp=0;
          }
        */
        tmp=input_color[(int)(rate*j)+
                       (int)(rate*i)*input_w][0];

        for(int k=0;k<3;k++){
          color[j+i*w][k]= tmp;                
        }
      }
    }
    img1=makeImage(color,w,h);
    getColor(img1, color, w, h);
    img2=img1;        
        
        
    setLayout(new BorderLayout());

    Panel p = new Panel();
    p.setBackground(Color.white);
    add("South",p);

    p.add(btn_fourie);
    p.add(btn_low);
    p.add(btn_high);
    p.add(btn_noise);
    p.add(btn_reset);
        
    // フーリエ変換
    btn_fourie.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if(fourie_flag==false){
            img1 = fourie(color, fourie_r, fourie_i, w, h);
          } else{
            img1 = r_fourie(color, fourie_r, fourie_i, w, h);
          }                        
          repaint();
          getColor(img1, color, w, h);
        }
      });

    // ローパスフィルタ
    btn_low.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if(fourie_flag==true){
            img1 = lowPass(fourie_r, fourie_i, w, h);
            repaint();
            getColor(img1, color, w, h);
          }
        }
      });

    // ハイパスフィルタ
    btn_high.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if(fourie_flag==true){
            img1 = highPass(fourie_r, fourie_i, w, h);
            repaint();
            getColor(img1, color, w, h);
          }
        }
      });
        
    // ノイズ付加
    btn_noise.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          if(fourie_flag==false){
            img1 = noise(color, w, h);
            repaint();
            getColor(img1, color, w, h);
          } else{
            img1 = f_noise(fourie_r, fourie_i, w, h);
            repaint();
          }
        }
      });
        
    // リセット
    btn_reset.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          img1 = reset();
          fourie_flag=false;
          repaint();
          getColor(img1, color, w, h);
                    
        }
      });
  }

    
  public void paint(Graphics g) {
    g.drawImage(img1,0,0,this);                
  }
    
  public double distance(double d1,
                         double d2
  ){
    return Math.sqrt(d1*d1+d2*d2);
  }

  // フーリエ変換でえたスペクトラムを画像化する。
  // スペクトラムの実数部fr
  // スペクトラムの虚数部fi
  public Image spectrumImage(double fr[][],
                             double fi[][],
                             int w,   // 画像の幅
                             int h   // 画像の高さ
  ){
    int w2=w/2;
    int h2=h/2;
    int[][] out = new int[w*h][3];
    double[][] r = new double[h2+1][w2+1];
    double ave=0;
    double rate;
        
    r[0][0]=distance(fr[0][0],fi[0][0]);
    ave+=r[0][0];

    r[h2][w2]=distance(fr[h2][w2],fi[h2][w2]);
    ave+=r[h2][w2];

    for(int i=1;i<h2;i++){
      r[i][0]=2*distance(fr[i][0],fi[i][0]);
      ave+=r[i][0];

      r[i][w2]=2*distance(fr[i][w2],fi[i][w2]);
      ave+=r[i][w2];
    }

    for(int j=1;j<w2;j++){
      r[0][j]=2*distance(fr[0][j],fi[0][j]);
      ave+=r[0][j];

      r[h2][j]=2*distance(fr[h2][j],fi[h2][j]);
      ave+=r[h2][j];
    }

    for(int i=1;i<h2;i++){
      for(int j=1;j<w2;j++){
        r[i][j]=4*distance(fr[i][j],fi[i][j]);
        ave+=r[i][j];
      }
    }
    ave/=(h2+1)*(w2+1);
    if(ave!=0){
      rate=128/ave;
    } else{
      rate=0;
    }

    int tmp=0;
    double pix_h=(double)(h2)/(double)(h-1);
    double pix_w=(double)(w2)/(double)(w-1);

    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        tmp=(int)(rate*r[(int)(pix_h*i)][(int)(pix_w*j)]);
                
        if(tmp<0) tmp=0;
        if(tmp>255) tmp=255;

        for(int k=0;k<3;k++){
          out[j+i*w][k]=tmp;
        }
      }
    }                    

    return makeImage(out,w,h);
  }

  // フーリエ変換
  // 色情報(0~255)をしまう配列 color[h*w][3]
  public Image fourie(int color[][],
                      // スペクトラムの実数部
                      double fr[][],
                      // スペクトラムの虚数部
                      double fi[][],
                      int w,   // 画像の幅
                      int h   // 画像の高さ
  ){
    int w2=w/2;
    int h2=h/2;
    double[][] fr2= new double[h][w];
    double[][] fi2= new double[h][w];
    double[][] a= new double[h2][w2];
        
    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        fr[i][j]=(double)color[j+i*w][0];
        /*
          fr[i][j]=
          255.0*Math.cos(2.0*K*(Math.PI/h)*(double)i)*
          255.0*Math.cos(2.0*K*(Math.PI/h)*(double)j);
        */
      }
    }
    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        fi[i][j]=0;
      }
    }
        
    for(int i=0;i<h;i++){            
      fft(fr,fi,fr2,fi2,-2.0*(Math.PI/(double)w),i,0,w);
    }
        
    for(int i=0;i<h;i++){            
      for(int j=0;j<w;j++){            
        fr[i][j]/=w;
        fi[i][j]/=w;
      }
    }
        
    for(int i=0;i<w;i++){            
      fft2(fr,fi,fr2,fi2,-2.0*(Math.PI/(double)h),i,0,h);
    }
        
    for(int i=0;i<h;i++){            
      for(int j=0;j<w;j++){            
        fr[i][j]/=h;
        fi[i][j]/=h;
      }
    }

    fourie_flag=true;
    return spectrumImage(fr,fi,w,h);
  }
    
  // フーリエ逆変換
  // 色情報(0~255)をしまう配列 color[h*w][3]
  public Image r_fourie(int color[][],
                        double r_fr[][],
                        double r_fi[][],
                        int w,   // 画像の幅
                        int h   // 画像の高さ
  ){
    int[][] out = new int[w*h][3];
    int w2=w/2;
    int h2=h/2;
    double[][] r_fr2= new double[h][w];
    double[][] r_fi2= new double[h][w];
        
    for(int i=0;i<h;i++){            
      fft(r_fr,r_fi,r_fr2,r_fi2,
          2.0*(Math.PI/(double)w),i,0,w);
    }

    for(int i=0;i<w;i++){            
      fft2(r_fr,r_fi,r_fr2,r_fi2,
           2.0*(Math.PI/(double)h),i,0,h);
    }
    
    int tmp=0;
    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        tmp=(int)r_fr[i][j];

        if(tmp<0) tmp=0;
        if(tmp>255) tmp=255;

        for(int k=0;k<3;k++){
          out[j+w*i][k]=tmp;
        }
      }
    }                    

    fourie_flag=false;
    return makeImage(out,w,h);
  }

  // fft
  public void fft(double fr[][],
                  double fi[][],
                  double fr2[][],
                  double fi2[][],
                  double theta,
                  int y,
                  int p,
                  int N
  ) {        
        
    if(N!=1){
      int N2 = N/2;
      double c,s;
      double xr,xi;
            
      for(int i=0;i<N2;i++){
        fr2[y][p+i]=fr[y][p+i]+fr[y][p+N2+i];
        fi2[y][p+i]=fi[y][p+i]+fi[y][p+N2+i];
                
        c=Math.cos(theta*(double)i);
        s=Math.sin(theta*(double)i);
        xr=fr[y][p+i]-fr[y][p+N2+i];
        xi=fi[y][p+i]-fi[y][p+N2+i];
        fr2[y][p+N2+i]=c*xr-s*xi;
        fi2[y][p+N2+i]=c*xi+s*xr;
                
      }
      fft(fr2,fi2,fr,fi,2.0*theta,y,p,N2);
      fft(fr2,fi2,fr,fi,2.0*theta,y,p+N2,N2);
      for(int i=0;i<N2;i++){
        fr[y][p+2*i]=fr2[y][p+i];
        fi[y][p+2*i]=fi2[y][p+i];
        fr[y][p+2*i+1]=fr2[y][p+N2+i];
        fi[y][p+2*i+1]=fi2[y][p+N2+i];
      }
    }
  }

  public void fft2(double fr[][],
                   double fi[][],
                   double fr2[][],
                   double fi2[][],
                   double theta,
                   int x,
                   int p,
                   int N
  ) {        
        
    if(N!=1){
      int N2 = N/2;
      double c,s;
      double xr,xi;
            
      for(int i=0;i<N2;i++){
        fr2[p+i][x]=fr[p+i][x]+fr[p+N2+i][x];
        fi2[p+i][x]=fi[p+i][x]+fi[p+N2+i][x];
                
        c=Math.cos(theta*(double)i);
        s=Math.sin(theta*(double)i);
        xr=fr[p+i][x]-fr[p+N2+i][x];
        xi=fi[p+i][x]-fi[p+N2+i][x];
        fr2[p+N2+i][x]=c*xr-s*xi;
        fi2[p+N2+i][x]=c*xi+s*xr;
                
      }
      fft2(fr2,fi2,fr,fi,2.0*theta,x,p,N2);
      fft2(fr2,fi2,fr,fi,2.0*theta,x,p+N2,N2);
      for(int i=0;i<N2;i++){
        fr[p+2*i][x]=fr2[p+i][x];
        fi[p+2*i][x]=fi2[p+i][x];
        fr[p+2*i+1][x]=fr2[p+N2+i][x];
        fi[p+2*i+1][x]=fi2[p+N2+i][x];
      }
    }
  }
    
  //ハイパスフィルタ
  public Image highPass(double fr[][],
                        double fi[][],
                        int w,
                        int h
  ){
    int[][] out = new int[w*h][3];

    int h2=h/2;
    int w2=w/2;
    double l=1.0/4.0;
    double sum=0,pass=0;
    double rate;

    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        sum+=Math.abs(
          Math.sqrt(fr[i][j]*fr[i][j]
                    +fi[i][j]*fi[i][j]));
        if((i<l*h2 || i>(1.0-l)*h2+h2) &&
           (j<l*w2 || j>(1.0-l)*w2+w2))
          {
            pass+=Math.abs(
              Math.sqrt(fr[i][j]*fr[i][j]
                        +fi[i][j]*fi[i][j]));
            fr[i][j]=0;
            fi[i][j]=0;
          }
      }
    }
        
    if(pass!=0) {
      rate=sum/pass;
    } else{
      rate=0;
    }

    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        fr[i][j]*=rate;
        fi[i][j]*=rate;                
      }
    }

    return spectrumImage(fr,fi,w,h);
  }        

  //ローパスフィルタ
  public Image lowPass(double fr[][],
                       double fi[][],
                       int w,
                       int h
  ){
    int[][] out = new int[w*h][3];

    int h2=h/2;
    int w2=w/2;
    double l=1.0/4.0;
    double sum=0,pass=0;
    double rate;

    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        sum+=Math.abs(Math.sqrt(fr[i][j]*fr[i][j]
                                +fi[i][j]*fi[i][j]));
        if((i>l*h2 && i<(1.0-l)*h2+h2) ||
           (j>l*w2 && j<(1.0-l)*w2+w2))
          {
            pass+=Math.abs(
              Math.sqrt(fr[i][j]*fr[i][j]
                        +fi[i][j]*fi[i][j]));
            fr[i][j]=0;
            fi[i][j]=0;
          }
      }
    }
        
    if(pass!=0) {
      rate=sum/pass;
    } else{
      rate=0;
    }

    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        fr[i][j]*=rate;
        fi[i][j]*=rate;
      }
    }

    return spectrumImage(fr,fi,w,h);
  }        


// リセットボタン
  public Image reset() {
    return img2;
  }

  // ノイズ付加ボタン
  // 色情報(0~255)をしまう配列 color[h*w][3]
  public Image noise(int color[][],
                     int w,   // 画像の幅
                     int h   // 画像の高さ
  ){
    double rand;
    int[] pix2= new int[w*h];

    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        rand = Math.random();                
        if(rand<0.03){
          rand = Math.random();
          for(int k=0;k<3;k++){                
            color[j+i*w][k]= (int)(rand*255);                
          }
        }
      }
    }
    return makeImage(color, w , h);
  }

  public Image f_noise(double fr[][],
                       // スペクトラムの実数部
                       double fi[][],
                       // スペクトラムの虚数部
                       int w,   // 画像の幅
                       int h   // 画像の高さ
  ){
    double rand;
    int[] pix2= new int[w*h];
        
    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        rand = Math.random();                
        if(rand<0.03){

          rand = Math.random();
          if(rand<0.5){
            rand = Math.random();
            fr[i][j]*=(double)rand;
          } else{
            rand = Math.random();
            fr[i][j]*=-1.0*(double)rand;
          }

          rand = Math.random();
          if(rand<0.5){
            rand = Math.random();
            fi[i][j]*=(double)rand;
          } else{
            rand = Math.random();
            fi[i][j]*=-1.0*(double)rand;
          }
                    
        }
      }
    }
    return spectrumImage(fr,fi,w,h);
  }
    
  // 色情報から画像をつくる。
  // 色情報(0~255)をしまう配列 color[h*w][3]
  Image makeImage(int color[][],
                  int w,   // 画像の幅
                  int h   // 画像の高さ
  ){
    int[] pix = new int[w*h];
        
    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        pix[j+i*w]=(255<<24)|
          (color[j+i*w][0]<<16)|
          (color[j+i*w][1]<<8)|
          (color[j+i*w][2]);
      }
    }
    return createImage(
        new MemoryImageSource(w,h,pix,0,w));
  }
    
  // 画像から色情報を取り出す。
  void getColor(Image img,  // 元の画像
  // 色情報(0~255)をしまう配列 color[h*w][3]
                int color[][],
                int w,   // 画像の幅
                int h   // 画像の高さ
  ) {
    int[] pix= new int[w*h];

    PixelGrabber pg=
        new PixelGrabber(img,0,0,w,h,pix,0,w);
    try{pg.grabPixels();}catch(Exception e){}
        

    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        color[j+i*w][0]=
          (pix[j+i*w] & 0xff0000)>>16;   // 赤色
        color[j+i*w][1]=
          (pix[j+i*w] & 0xff00)>>8;   // 緑色
        color[j+i*w][2]=
          pix[j+i*w] & 0xff;   // 青色

      }
    }
  }
}


generated through LaTeX2HTML. M.Inaba 平成18年5月7日