ここで公開されている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(); }
ちなみに実行結果は以下の通りです。
移動平均よりSGフィルターの方が高周波数成分が残されている感じが確かにします。