【C#】フーリエ変換(FFT, DFT)プログラム

以前、Excelのマクロを使って、データ個数に応じて高速フーリエ変換(FFT)と離散フーリエ変換(DFT)の処理を自動で切り替えるマクロを作成したのですが、Excelではデータ数が多い時など、使いにくい場合もあるので、今度は、C#でフーリエ変換部分をライブラリ(*.dll)にし、そのライブラリを使ったプログラムを作成しました。

 

実行ブログラムはこちら FourierTransform.zip

ソースコードは、すべてGitHubで公開しています。

https://github.com/ImagingSolution/FourierCSharp

 

使用方法は以下の通りです。

 

プログラムの実行

zipファイルを解凍すると、

FourierTransform.exe

FourierCSharp.dll

sampledata

となっていますので、FourierTransform.exeをダブルクリックして実行してください。

 

フーリエ変換できるデータフォーマット

データは1行あたりに1つのデータのCSVファイルとなります。

例)

1
1.30939551
1.59096344
1.820538079
1.980767038
2.06331351
2.069803727
2.01139008
1.906994542
1.780482109
1.65716389
1.560123288
1.506883103
1.506883103
1.560123288

入力データに実数部と虚数部が含まれる場合は、1行あたりに

実数部、虚数部

とします。

例)

50, 0
0, -25
0, 0
0, -12.5
0, 0
0, 0

サンプルのデータとして、ZIPファイル内の sampledataフォルダ内にいくつかCSVファイルを入れてありますので、そちらを参考にしてください。

 

データの読込

メニューの File → LoadData をクリックし、CSVファイルを指定することで、データが読み込まれ、フーリエ変換の結果(フーリエ変換後の各周波数の大きさ)が表示されます。

 

フーリエ変換はデータの個数が2のn乗個の場合:FFT、その他の場合:DFTを行います。

 

フーリエ変換、逆フーリエ変換の切替

メニューの Fourier direction → Forward もしくは Backward をクリックすることで、離散フーリエ変換、逆離散フーリエ変換が切り替わります。

 

離散フーリエ変換は

$$F(t)=\sum _{ x=0 }^{ N-1 }{ f(x){ e }^{ -i\frac { 2\pi tx }{ N } } } $$

逆離散フーリエ変換は

$$f(t)=\frac { 1 }{ N } \sum _{ t=0 }^{ N-1 }{ F(t){ e }^{ i\frac { 2\pi xt }{ N } } } $$

で計算しています。

 

窓関数

メニューの Window→ Hamming, Hanning, Blackman のいづれかをクリックすることで、それぞれの窓関数を通します。

↓ 窓関数

 

ただし、窓関数を通すと、元に戻せないため、元に戻す場合は、再度、データ読込を行って下さい。

 

免責事項

ここに公開されているプログラムは自由に使って頂いて構いませんが、バグ等による責任は待てませんので、自己責任において参照下さい。

 

フリーウェアへ戻る

【C#】複素数の計算(Complex構造体)

C#では複素数を扱う Complex構造体(名前空間:System.Numerics)が用意されています。

ただし、プロジェクトの初期状態では、使えないため、プロジェクトの参照を右クリックし、参照の追加System.Numerics を追加してください。

 

 

コード例

var complex1 = new System.Numerics.Complex(Math.Sqrt(3), 1.0);
Console.WriteLine($"complex1.Magnitude = {complex1.Magnitude}");
Console.WriteLine($"complex1.Phase = {complex1.Phase * 180.0 / Math.PI}");

var complex2 = new System.Numerics.Complex(1.0, Math.Sqrt(3));
Console.WriteLine($"complex2.Magnitude = {complex2.Magnitude}");
Console.WriteLine($"complex2.Phase = {complex2.Phase * 180.0 / Math.PI}");

var complex3 = complex1 * complex2;
Console.WriteLine($"complex3.Magnitude = {complex3.Magnitude}");
Console.WriteLine($"complex3.Phase = {complex3.Phase * 180.0 / Math.PI}");

var complex4 = complex1 + complex2;
Console.WriteLine($"complex4.Magnitude = {complex4.Magnitude}");
Console.WriteLine($"complex4.Phase = {complex4.Phase * 180.0 / Math.PI}");

// 結果
//complex1.Magnitude = 2
//complex1.Phase = 30
//complex2.Magnitude = 2
//complex2.Phase = 60
//complex3.Magnitude = 4
//complex3.Phase = 90
//complex4.Magnitude = 3.86370330515627
//complex4.Phase = 45

 

複素数の大きさは Magnitudeプロパティで、位相はPhaseプロパティで拾えるし、複素数の演算(加算、減算、乗算、除算)も可能です。

複数の演算は、複素数と複素数、複素数と実数の演算が可能です。

 

これが出来るという事はDFT(離散フーリエ変換)も簡単に出来ちゃいますね。

 

関連ページ

複素数のイメージ

複素数の計算

離散フーリエ変換(DFT)の直感的理解

 

参考ページ

https://docs.microsoft.com/ja-jp/dotnet/api/system.numerics.complex?view=netframework-4.8

 

← C# へ戻る

【C#3.0~】自動実装プロパティ

C#3.0(Visual Studio 2008)からは、自動実装プロパティなる物が使えるようになりました。

 

C#2.0までのプロパティの実装は、こんな感じ↓

class Contrast
{
    private double _scale = 1.0;
    public double Scale
    {
        get
        {
            return _scale;
        }
        set
        {
            _scale = value;
        }
    }

    private double _offset = 0.0;
    public double Offset
    {
        get
        {
            return _offset;
        }
        set
        {
            _offset = value;
        }
    }
}

 

で行っていたかと思いますが、C#3.0からは、自動実装プロパティでは、

class Contrast
{
    public double Scale { get; set; }

    public double Offset { get; set; }
}

のように書くことができます。

これで、ずいぶんシンプルで見やすくなるかと思います。

 

また、プロパティを読み取り専用にするには set; の前に private を付けて

class Contrast
{
    public double Scale { get; set; }

    public double Offset { get;  private set; }
}

とします。

 

C#6.0(Visual Studio 2015)からは自動プロパティを以下のようにすることで、初期化を行う事ができます。

class Contrast
{
    public double Scale { get; set; } = 1.0;

    public double Offset { get; set; } = 0.0;
}

さらにC#6.0からは、読み取り専用にプロパティを get; のみの記述で可能となります。

ただし、値を変更できるのは、コンストラクタのみとなります。(他のメソッド等からは変更できません。)

class Contrast
{
    public double Scale { get; set; } = 1.0;

    public double Offset { get; } = 0.0;

    public Contrast(double scale, double offset)
    {
        this.Scale = scale;
        this.Offset = offset;
    }
}

 

パフォーマンスの比較

C#2.0まで、処理速度が必要な部分では、プロパティの参照は遅いので、クラス内での処理ではプロパティを参照せず、フィールドの値を参照するようにしていたのですが、フィールドの無い、自動プロパティではどうか?確認してみました。

 

以下のように、C#2.0の時のプロパティを使った Class1 と、自動実装プロパティを使った Class2 を作成し、ただ、値を取得するだけのメソッド(GetValue)を作成しました。

class Class1
{
    private double _a = 1.0;
    public double A
    {
        get
        {
            return _a;
        }
        set
        {
            _a = value;
        }
    }

    public double GetValue()
    {
        return _a;
    }
}

class Class2
{
    public double A { get; set; } = 1.0;

    public double GetValue()
    {
        return this.A;
    }
}

このGetValue()メソッドをそれぞれ、10億回繰り返した時の処理時間を5回計測したときの平均処理時間は、以下の通りでした。

 

Class1 273.6 msec
Class2 552 msec

 

プロパティ参照は、プロパティの内部でエラー処理などを行っている場合も多いので、そのために遅い場合もありますが、単純にプロパティ参照するだけでも遅い結果でした。

 

そのため、パフォーマンスが必要な場合は、昔の書き方もありですね。

 

参考ページ

自動実装するプロパティ (C# プログラミング ガイド)

自動実装するプロパティを使用して簡易クラスを実装する方法 (C# プログラミング ガイド)

 

← C#2.0からの脱却 へ戻る

LibTorch(C++版PyTorch)の使用方法

[2020.2.5]LibTorch Ver.1.4.0対応版に修正

昨年はPFNがPyTorchへ移行するというビックニュースがありましたが、それもあって、個人的に注目度の増したPyTorch

DeepLearningについては、最終的にはC#から使いたいのですが、C#に正式に対応しているのは、おそらくMicrosoftのCognitive Toolkitだけ?ですが、MicrosoftもCognitive Toolkitは今後、メンテナンスのみ行い、新しくは開発を行わないと言っているので、いつまで続くか?ちょっと不安。

C#がダメなら、ある程度C++で処理を書いて、C#から処理を呼び出す方向にしようと思い、C++に対応しているPyTorchについて調べてみました。

PyTorchのC++版はLibTorchと言うらしい。

 

ただし、PyTorchのC++のページ

https://pytorch.org/cppdocs/

にも、こんな文言↓が書いてあり、今後、変わる可能性も大な状況です。

 

開発環境

Windows10

Visual Studio 2017

LibTorchバージョン 1.4.0 (2020年2月月時点の最新)

となります。

Visual Studio 2015でも試してみましたが、エラーが多発し、VS2017にしました。

 

LibTorchのダウンロード

ビルド済みのLibTorchは下記、ページよりダウンロードできます。

https://pytorch.org/

 

今回は使用方法の評価をしたいので、Windows、C++版のCUDAなしのDebug版をダウンロードします。

実際に使う場合はRelease版を入手してください。

 

 

zipファイル(libtorch-win-shared-with-deps-debug-1.4.0.zip)を解凍すると、ファイル構成は以下のようになっています。

重要な部分を展開して表示しています。

 

└─libtorch
│ build-hash

├─bin

├─cmake

├─include
│ │ clog.h
│ │ cpuinfo.h
│ │ fp16.h
│ │ psimd.h
│ │
│ ├─ATen
│ ├─c10
│ ├─caffe2
│ ├─nomnigraph
│ ├─pybind11
│ ├─TH
│ ├─THCUNN
│ ├─THNN
│ └─torch
│ └─csrc
│ ├─api
│ │ ├─include
│ │ │ └─torch
│ │ │ │ all.h
│ │ │ │ arg.h
│ │ │ │ autograd.h
│ │ │ │ cuda.h
│ │ │ │ data.h
│ │ │ │ enum.h
│ │ │ │ expanding_array.h
│ │ │ │ jit.h
│ │ │ │ nn.h
│ │ │ │ optim.h
│ │ │ │ ordered_dict.h
│ │ │ │ python.h
│ │ │ │ serialize.h
│ │ │ │ torch.h
│ │ │ │ types.h
│ │ │ │ utils.h
│ │ │ │
│ │ │ ├─data
│ │ │ ├─detail
│ │ │ ├─nn
│ │ │ ├─optim
│ │ │ ├─python
│ │ │ │ init.h
│ │ │ │
│ │ │ └─serialize
│ │ │
│ │ └─src
│ ├─autograd
│ ├─cuda
│ ├─distributed
│ ├─generic
│ ├─jit
│ ├─multiprocessing
│ ├─onnx
│ ├─tensor
│ └─utils

├─lib
│ c10.dll
│ c10.lib
│ c10.pdb
│ caffe2_module_test_dynamic.dll
│ caffe2_module_test_dynamic.lib
│ caffe2_module_test_dynamic.pdb
│ clog.lib
│ cpuinfo.lib
│ libiomp5md.dll
│ libiompstubs5md.dll
│ libprotobuf-lited.lib
│ libprotobufd.lib
│ libprotocd.lib
│ torch.dll
│ torch.lib
│ torch.pdb

├─share
└─test

 

使用方法

使用方法については、こちらのページ↓

https://mc.ai/building-pytorch-c-integration-libtorch-with-ms-visual-studio-2017/

を参考にしながら、少し手をいれました。

 

1.  C++プロジェクトの作成

今回はC++のコンソールアプリケーションを作成します。

Visual Studio 2017を起動し、 新しいプロジェクトの作成 をクリックします。

Visual C++ → Windowsデスクトップ → コンソールアプリ と選択し、名前や場所を指定して、OKボタンをクリックします。

すると最小限のコンソールアプリ Hello World! が作成されます。

2.  プラットフォームを64ビット用(x64)に変更

Visual Studioの上部のソリューションプラットフォーム部分を x64 に変更します。

3.  インクルードディレクトリの設定

使用するヘッダファイル(*.h)の場所(ディレクトリ)を設定します。

プロジェクトのプロパティより、 C/C++ → 全般 → 追加のインクルードディレクトリ
の編集をクリックします。

libtorch\include
libtorch\include\torch\csrc\api\include
の2つのディレクトリを指定します。
下図の D:PyTorch140の部分は各自、解凍したファイルを配置した場所に合わせて下さい。

 

4.  ライブラリディレクトリの設定

使用するライブラリファイル(*.lib)の場所(ディレクトリ)を設定します。

プロジェクトのプロパティより、 C/C++ → 全般 → 追加のライブラリディレクトリ
の編集をクリックします。

 

libtorch\lib

のディレクトリを指定します。
下図の D:PyTorch140の部分は各自、解凍したファイルを配置した場所に合わせて下さい。

 

5.  ライブラリファイルの設定

使用するライブラリファイル(*.lib)を設定します。

プロジェクトのプロパティより、 C/C++ → 入力 → 追加の依存ファイル
の編集をクリックします。

 

ここで、使用するライブラリファイル(*.lib)を設定するのですが、参考にしたページのファイル名が少し違っていたので、とりあえず、下記ファイルを追加しました。

c10.lib
caffe2_module_test_dynamic.lib
torch.lib

 

libファイルを追加したらOKボタンをクリックし、戻ったプロパティページの 適用 をクリックします。

 

6.  準拠モードの設定

次に、参考にしたページに従って、 C/C++ → 言語 → 準拠モード

はい から いいえ に変更します。

プロパティページの 適用、OK をクリックし設定画面を閉じます。

 

サンプルプログラムの作成

ここでも参考にしたページにしたがって、Hello World! のプログラムにiniclude

#include “torch/torch.h”

を追加してみます。

 

 

 

 

 

この状態でソリューションをビルドすると、エラーが1つ、警告はたくさん出てきます。

 

警告はあまりに多いので、エラーだけを直します。

 

E1866 属性はどのエンティティにも適用されません

E1866 属性はどのエンティティにも適用されませんの部分をダブルクリックすると、ArrayRef.hファイルへ飛びます。

 

この C10_DEFINE_DEPRECATED_USING の部分が何をしているか?分からないのですが、ヒントに従って、新しいヒントファイル(cpp.hint)を追加してみたものの、修正できず。

とりあえず、コメントに Use IntArrayRef instead! と書いてあったので、コメントアウトして様子見。

 

これで、エラーが出なくなりました! コメントアウトした部分が怪しいけど。。

 

[2020.2.24]追記

C10と付くエラーはCUDA10に関するエラーのようです。

動かすプログラムからCUDAに関連する関数を呼ぶと、C10と付くエラーが表示されるため、本ページではCUDA無しのバイナリを使用していますが、CUDA対応のバイナリを使用した方がいいかも?しれません。