チーム15C3
課題名
さまざまな処理のグラフ描画
研究者名
3年14組46番 Tsukasa Morita
3年14組47番 Daiki Hirato
目的
フーリエ変換や、フィルタの処理を行うことがどのような結果になるのかを、グラフの描画によって視覚的に理解する。
概要
初期の波形データ(方形波、ギザギザ、ワンポイント、ランダム等)を表示されたボタンで決定し、
その波形の源波形、フーリエ変換、逆フーリエ変換、アベレージフィルタ、ハイパスフィルター、ローパスフィルターを表示する。
使用言語
java
ソースコード
/**フーリエ変換の描画による理解
* @author morita, hirato * @date 2015/07/13 */
package hardware;
import static java.lang.Math.*;
import java.awt.Button;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**Hardwareクラス
* @neme Hardware */
public class Hardware extends Frame implements ActionListener {
//計算用変数 static double data[] = new double[200]; //フーリエ計算用変数 double[] x = new double[data.length]; double[] y = new double[data.length]; double[] z = new double[data.length]; double max = 0;
// フレームの幅と高さを表す定数 private static final int WIDTH = 500; private static final int HEIGHT = 600;
/**averageFilterの数値計算クラス * @name averageFilter */ public static double averageFilter(int i){ //0から2は平均を取れないので0にする if(0 <= i && i <= 2){ return(0); }
//平均を計算し返す else{ return(( data[i] + data[i-1] + data[i-2] + data[i-3] ) / 4); } }
/**highPassFilterの数値計算クラス * @name highPassFilter */ public static double highPassFilter(int i){ //0と199は計算できないので0にする if(i == 0 | | i == 199){ return(0); }
//現在の値からローパスフィルタの結果を引けば、ハイパスフィルタの結果になる else{ return(data[i] - ((data[i-1] + data[i] + data[i+1]) / 3)); } }
/**lowPassFilterの数値計算クラス * @name lowPassFilter */ public static double lowPassFilter(int i){ //0と199は計算できないので0にする if(i == 0 | | i == 199){ return(0); }
//平均を計算し返す else{ return(( data[i-1] + data[i] + data[i+1] ) / 3); } }
/**calculate * フーリエ変換、逆フーリエ変換の計算、および処理結果の表示 * @name calculate */ public void calculate() { //フーリエ変換 for(int f = 0; f < data.length; ++ f){ //周期fについてのフーリエを計算 x[f] = 0; y[f] = 0; for(int i = 0; i < data.length; ++i){ double theta = 2 * PI * f * i / data.length; x[f] += data[i] * cos(theta) - data[i] * sin(theta); y[f] += data[i] * sin(theta) + data[i] * cos(theta); } x[f] /= data.length; y[f] /= data.length; } //振幅スペクトルの計算 for(int f = 0; f < data.length; ++ f){ z[f] = sqrt(x[f] * x[f] + y[f] * y[f]); if(max < z[f]) max = z[f]; }
//逆フーリエ変換の計算 double proc[] = new double[data.length]; for(int f = 0; f < data.length; ++f){ for(int i = 0; i < data.length; ++i){ double theta = 2 * PI * f * i / data.length; proc[i] += x[f] * cos(theta) + y[f] * sin(theta); } }
//フレームの作成 BufferedImage img = new BufferedImage(WIDTH+150, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g = (Graphics2D) img.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, WIDTH+200, HEIGHT); g.setColor(Color.BLACK); GeneralPath gp = new GeneralPath();
//基本線(グラフの横線、縦線、中央線) for(int i = 0; i < 6; i++){ if(i != 1){ g.setColor(Color.RED); g.drawLine(30, 50 + i*100, 640, 50 + i*100); g.setColor(Color.BLACK); g.drawLine(30, 20 + i*100, 30, 80 + i*100); g.drawLine(30, 80 + i*100, 640, 80 + i*100); } }
//源波形のグラフの表示 g.setColor(Color.BLACK); g.drawString("Original", 10, 15); g.setColor(Color.BLUE); gp.moveTo(30, 50); for(int i = 0; i < data.length; ++i){ double a = i * 3 + 30; double b = data[i] * 30 + 50; gp.lineTo(a, b); } g.draw(gp);
//フーリエ変換のグラフの表示 g.setColor(Color.BLACK); g.drawString("Fourier Transformation", 10, 100); g.setColor(Color.BLUE); for(int i = 0; i < x.length / 2; ++i){ int h = (int) (z[i] * 85 / max); g.fillRect(i * 6 + 30, 190 - h, 4, h + 1); }
//逆フーリエ変換のグラフの表示 g.setColor(Color.BLACK); g.drawString("Inverse Fourier Transformation", 10, 210); g.setColor(Color.BLUE); gp = new GeneralPath(); gp.moveTo(30, 250); for(int i = 0; i < data.length; ++i){ double a = i * 3 + 30; double b = proc[i] * 30 + 250; gp.lineTo(a, b); }
//Average Filterのグラフの表示 g.setColor(Color.BLACK); g.drawString("Average Filter", 10, 310); g.setColor(Color.BLUE); gp.moveTo(30, 350); for(int i = 0; i < data.length; ++i){ double a = i * 3 + 30; double b = averageFilter(i) * 30 + 350; gp.lineTo(a, b); }
//High Pass Filterのグラフの表示 g.setColor(Color.BLACK); g.drawString("High Pass Filter", 10, 410); g.setColor(Color.BLUE); gp.moveTo(30, 450); for(int i = 0; i < data.length; ++i){ double a = i * 3 + 30; double b = highPassFilter(i) * 30 + 450; gp.lineTo(a, b); }
//Low Pass Filterのグラフの表示 g.setColor(Color.BLACK); g.drawString("Low Pass Filter", 10, 510); g.setColor(Color.BLUE); gp.moveTo(30, 550); for(int i = 0; i < data.length; ++i){ double a = i * 3 + 30; double b = lowPassFilter(i) * 30 + 550; gp.lineTo(a, b); }
g.draw(gp); g.dispose();
//フレームのタイトルの表示 JFrame f = new JFrame("Fourier Transformation"); f.setSize(WIDTH+200, HEIGHT+50); f.add(new JLabel(new ImageIcon(img))); f.setVisible(true); }
/**actionPerformed * ボタンの押された時の処理を設定する * @neme actionPerformed * @param ActionEvent */ public void actionPerformed(ActionEvent ae) { String commandName = ae.getActionCommand();
// コマンド名によってそれぞれの処理をする。 //ここでは、初期の波形を設定
//直線 if (commandName.equals("line")) { for(int i = 0; i < data.length; ++i){ data[i] = 0; } }
//sin波 if (commandName.equals("sin")) { for(int i = 0; i < data.length; ++i){ data[i] = sin(i)/4; } }
//方形波 if (commandName.equals("square")) { for(int i = 0; i < data.length; ++i){ data[i]=(i <=50)? -1 : 1 ; } }
//ギザギザ if (commandName.equals("sawtooth")) { for(int i = 0; i < data.length; ++i){ data[i] = i % 50 / 25. - 1; } }
//二点だけ飛び出る波 if (commandName.equals("point")) { for(int i = 0; i < data.length; ++i){ data[i] = (i % 100 == 50)? -1 : 0; } }
//ランダム if (commandName.equals("random")) { for(int i = 0; i < data.length; ++i){ data[i] = Math.random() * 2 - 1; } }
//プログラムの終了 else if (commandName.equals("Close")) { System.exit(0); }
//波形の初期化が終わったら、グラフの各処理を行う calculate(); }
/**Hardware文 * 初めの表示を設定する * @neme Hardware */ public Hardware() { // このフレームの大きさを設定する。 setSize(WIDTH, HEIGHT);
// 配置の方式をFlowLayout に設定 setLayout(new FlowLayout());
//"line"ボタン Button lineCommentButton = new Button("line"); add(lineCommentButton); lineCommentButton.addActionListener(this);
// "sin"ボタン Button sinButton = new Button("sin"); add(sinButton); sinButton.addActionListener(this);
//"square"ボタン Button squareCommentButton = new Button("square"); add(squareCommentButton); squareCommentButton.addActionListener(this);
// "sawtooth"ボタン Button sawtoothButton = new Button("sawtooth"); add(sawtoothButton); sawtoothButton.addActionListener(this);
//"point"ボタン Button pointCommentButton = new Button("point"); add(pointCommentButton); pointCommentButton.addActionListener(this);
//"random"ボタン Button randomCommentButton = new Button("random"); add(randomCommentButton); randomCommentButton.addActionListener(this);
// 終了ボタン Button closeButton = new Button("Close"); add(closeButton); closeButton.addActionListener(this);
//フレームを可視化する setVisible(true); }
/**main文 * オブジェクトの生成 * @neme main * @param args */ public static void main(String[] args) { //オブジェクトの生成 Hardware ce = new Hardware(); }
}
実行結果
以下のような画面で、波形を決定。
以下の画面で結果を確認できる。
考察
授業で取り扱ったフーリエ変換やフィルターをグラフとして示すことで実際にどのように処理が行われ、変化しているかの理解が深まると考えられる。
また、今回扱ったものは授業で扱ったので最初からある程度理解することができていたが、まだ習っていないものでも作成されているプログラムを使ってグラフにすることで実際の動きを簡単に理解することもできると考えられる。
もうすこし他のフィルタもグラフにしたかったが時間が足りずに作成できなかった。
- 最終更新:2015-07-13 16:03:48