memorandums

日々の生活で問題解決したこと、知ってよかったことなどを自分が思い出すために記録しています。

Savitzky-Golay フィルター

ここで公開されているJava製のSavitzky-Golay フィルターのプログラムをProcessingで動かしてみました、という話。

Savitzky-Golay フィルターとはなんぞや?という方はぐぐってください。僕はとりあえず動かしたかったので。

■準備

(1)作者のページからsgfilter_v1_2r25.jarとSGFilterVis2.zipをダウンロードします。(Google Codeだからいつか消えてしまうかも?)

(2)Processingのlibrariesフォルダ内にsgfilter_v1_2r25/libraryフォルダを新規作成します。

(3)そのフォルダの中にsgfilter_v1_2r25.jarとSGFilterVis2.zipを展開して取り出したappframework-1.0.3.jarとbeansbinding-1.2.1.jarとcommons-math.jarとliquidlnf.jarを入れます。

(4)Processingを起動します。

■サンプル

ドキュメントがないのでコード中のコメントと動かしながら使い方を探りました。あっているかわかりません。。。あしからず。

調整項としてはSGFilter.computeSGCoefficients(5, 5, 2)とSGFilter(5, 5)です。両方とも引数が似ているのでお分かりの通り同じ意味を持ちます。1番目が参照する過去のデータ点数、2番目が参照する未来のデータ点数です。3番目の引数は回帰式の次数です。この例では前後5点ずつのデータを参照して2次の回帰式でスムージングしている、という感じです。

import mr.go.sgfilter.*;

float[] data; 
float[] result; 
float[] result2; 

void setup() {
  size(800, 300);
  data = new float [width];
  randomizeData();
  mouseClicked();
}

void draw() {
  for (int i = 0; i < width; i++) {
    stroke(data[i]);
    line(i, 0, i, height / 3);
  }
  fill(0);
  stroke(0);
  rect(0, 5, 100, 20); 
  fill(255);
  text("Original", 0, 20);

  for (int i = 0; i < width; i++) {
    stroke(result[i]);
    line(i, height / 3, i, height / 3 * 2);
  }
  fill(0);
  stroke(0);
  rect(0, height / 3 + 5, 100, 20); 
  fill(255);
  text("Savitzky-Golay", 0, 20 + height / 3);

  for (int i = 0; i < width; i++) {
    stroke(result2[i]);
    line(i, height, i, height / 3 * 2);
  }
  fill(0);
  stroke(0);
  rect(0, height / 3 * 2 + 5, 100, 20); 
  fill(255);
  text("Moving Average", 0, 20 + height / 3 * 2);
}

void randomizeData() {
  for (int i = 0; i < width; i++) {
    data[i] = random(255);
  }
}

void doSGFilter() {
  double[] coeffs = SGFilter.computeSGCoefficients(5, 5, 2);
  ContinuousPadder padder1 = new ContinuousPadder();
  SGFilter sgFilter = new SGFilter(5, 5);
  sgFilter.appendPreprocessor(padder1);
  result = sgFilter.smooth(data, coeffs);
}

void doMAFilter() {
  result2 = new float [width];
  for (int i = 0; i < width; i++) {
    result2[i] = height;
    if (i < 5 || i > width - 5) continue;
    int sum = 0;
    for (int j = 0; j < 10; j++) {
      sum += data[i + j - 5];
    }
    result2[i] = sum / 10.0;
  }
}

void mouseClicked() {
  randomizeData();
  doSGFilter();
  doMAFilter();
}

ちなみに実行結果は以下の通りです。

f:id:ke_takahashi:20150629203733p:plain

移動平均よりSGフィルターの方が高周波数成分が残されている感じが確かにします。

自作しなきゃ。。。と思ってぐぐるとある。Javaのありがたいところですね。作者の方に感謝感謝。