約 2,922,582 件
https://w.atwiki.jp/amaeda/pages/17.html
全般 # スケーリング 変換の結果は配列のスケール倍される。 プラン生成 プラン生成は、原則的にデータの”メモリ空間”に対して行われ、 (これはFFTWのバージョン3になってから大きく変更されたポイントである。) プラン生成時には、計算時間の最適化などを行うので、多少時間がかかる。 したがって、繰り返し計算を行う時には、一度生成したプランを繰り返し使うことが推奨されている。 すると、例えば、同じサイズの入力データが複数あるとき、一度プランを生成してから、プラン生成時に指定したメモリ空間にデータをコピーして演算を行うことが考えられる。 この際、プラン用のメモリ空間が、”バッファ”として余分に消費されることになるので、多くのメモリを必要とする場合には検討が必要だ。 また、ここまででプランは特定の”メモリ空間”に対して生成されると述べたが、データサイズが同じ、などの制約下であれば、他のメモリ空間に対してプランを再利用する関数も存在する。 また、プラン生成時に入出力配列は上書きされることがある。フラグの設定をよく確認しよう。面倒だったら、入力配列はプラン生成後の初期化するよう心がけよう。 データのメモリ空間上の並び(行メジャー) FFTWのデータはrow-major(行メジャー)で考えられて作られている。 行メジャーとは、最後の配列が、もっともはやく変化する順序である。 int a[SIZEX][SIZEY]; というものがあったとすると、メモリ空間では、 a[0][0],a[0][1],・・・,a[1][0],・・・,a[SIZEX-1][SIZEY-1] のように並ぶ。 利用上は、column-major(列メジャー)でも構わないが注意が必要である。 すると、利用者は、int a[SIZEY][SIZEX]のつもりなのに、FFTWは、int a[SIZEX][SIZEY]だと思っているので、 のようにパラメータのヒントなどで誤解を招く表現が生じる。 要注意である。 このページでは、画像データなどを扱うことを考え、FFTWの推奨ではないのを承知で、列メジャーのデータを扱うコードに統一することとする。 行メジャー(row-major)ーと列メジャー(column-major) 実数- 複素数/複素数- 実数変換
https://w.atwiki.jp/amaeda/pages/14.html
Introduction このマニュアルはFFTW(the Fastest Fourier Transform in the West)バージョン3.1.2のものです.FFTWは離散フーリエ変換を計算する包括的な高速Cルーチン集であり、以下のような特徴があります. 複素数データ,実数データ,偶対称,奇対称な実数データのDFT(*1),実数データの離散Hartley変換(DHT)を計算可能 入力データは任意のサイズがとれる.O(nlogn)アルゴリズムを,素数を含む,すべてのサイズで採用. 計算の時間の次元がデータ数nに対して,nlogn 任意の多次元データをサポート SSE/SSE2/3DNow!/Altivecの命令セットをサポート FFTW3.1.2は共有メモリシステムに対しては、並列(マルチスレッド)変換が可能。FFTW3.1.2は分散メモリ並列変換ができないが、MPIバージョンをじきに実装予定 当面の間は,FFTW2.1.3に実装されたMPIが利用可能 ここでは、どんなアプリケーションに利用するかに関わらず、DFTの性質および使い方を熟知しているものとして話を進めます。もしそうでなければ、例えば、"The Fast Fourier Transform and Its Applications"(*2),を参照してください. 我々のWebページにもFFT関係の情報のリンク集があります。 FFTWを効果的に使うためには、FFTWの内部構造の基本的なコンセプトについて知っている必要があります。すなわち、FFTWはDFT変換を計算する時に固定のアルゴリズムではなく、パフォーマンスを最大化するために、細かいハードウェア条件に合わせたアルゴリズムを用います。つまり、計算は大きくわけて2段階にわけることができます。 第一にFFTWのプランナーはあなたのマシンで計算する時にもっとも速くなる方法を”学習”します。プランナーはこの情報を含んだ「プラン」と呼ばれるデータ構造体を生成します。 続いて、プランによって示された入力データ配列を変換するために、プランが実行されます。 このプランは何回でも再利用可能です。高性能なアプリケーションでは、同じサイズの変換が何度も計算されます。そのため、初期化の計算コストが比較的高くても、許容可能でしょう。 一方で、特定のサイズの一回限りの変換を行いたい時には、プラン生成にかかる時間が重要になります。この場合は、FFTWはヒューリスティックにあるいは前回計算されたプランに基づいて、高速にプランを計算します。 FFTWは任意の長さ、ランク、次元そして、一般的なメモリレイアウトの変換をサポートしています。しかし、簡単なケースではこの様に一般性を持たせることは不必要で、混乱を招くことでしょう。そういうわけで、我々は三つのレベルの一般性に対し、それぞれインターフェースを作りました。 ベーシックインターフェース 連続データの単一の変換を行う場合 アドバンストインターフェース 多次元配列、規則的な配列の変換を行う場合 グルインターフェース 最も一般的なデータ配置、次元、規則を持つ場合の変換を行う場合 我々は、大多数のユーザーがベーシックインターフェースで最も良い結果を得られることを期待しています。グルインターフェースは、諸問題を避けるために注意深くドキュメントを読む必要があります。 プランナーは、自動でプランの最適化を行ってくれる一方で、上級ユーザ向けの機能として、マニュアルでカスタマイズすることも可能です。例えば、コードの大きさが懸案事項である場合には、FFTWをリンクする際に、そのアプリケーションに必要な部分のみをリンクするツールも用意しています。反対に、標準のFFTWでは機能的に不十分で、拡張の必要があるかもしれません。例えば、標準のFFTWでは、配列サイズが、小さい素数(2,3,5,7)で素因数分解される場合に最も効率的に機能します。一方で、その他の場合には、一般的な利用を想定した遅いルーチンを使います。もし、その他の配列サイズに対し、効率的な計算をする必要があれば、FFTWのコードジェネレータが役に立ちます。コードジェネレータは高速なCプログラム(コードレット)を任意の配列サイズに対し生成します。例えば、513(=19*33)のサイズの変換がしたい場合、FFTWを因子19が効率的に計算できるようカスタマイズできます。 FFTWに関する詳細の情報に関しては、"FFTW An adaptive software arrchitecture for the FFT”(*3)や"The Fastest Fourier Transform in the West"(*4)を参照してください。 コードジェネレータについては、"A fast Fourier transform compiler"(*5)を参照してください。 これらの論文は、最新バージョンのFFTW,FAQ,性能テスト,その他のリンクと合わせてFFTWのホームページ(*6)から入手可能です。 FFTWの現在のバージョンは、過去30年間にわたるFFTの文献の様々なアイデアを反映しています。 FFTWには、何らかの形で、Cooley-Tukeyアルゴリズム(素因数のアルゴリズム)、Raderのアルゴリズム(素数サイズのアルゴリズム)、そしてsplit-radixアルゴリズム(Dan Bernsteinによる改良版)が盛り込まれてます。FFTWのコードジェネレータも我々は完全に理解できていないアルゴリズムを実装しています。適宜、引用文献のこと。 (*1)これらの対称な変換はそれぞれ、一般的に離散コサイン/サイン変換として知られている (*2) "The Fast Fourier Transform and Its Applications",E. O. Brigham, Prentice-Hall, Englewood Cliffs, NJ, 1988 (*3) FFTW An adaptive software architecture for the FFT,M. Frigo and S. G. Johnson, 23rd International Conference on Acoustics, Speech, and Signal Processing (Proc. ICASSP 1998 3, p. 1381) (*4) The Fastest Fourier Transform in the West, M. Frigo and S. G. Johnson, technical report MIT-LCS-TR-728 (Sep. ’97) (*5) A fast Fourier transform compiler, M. Frigo, in the Proceedings of the 1999 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), Atlanta, Georgia, May 1999 (*6) http //www.fftw.org/
https://w.atwiki.jp/amaeda/pages/21.html
/* * test of self compiled fftw3 * 2D version * @author maeda * @date 2008/09/09 */ #include stdio.h #define _USE_MATH_DEFINES #include math.h #include "fftw3.h" #pragma comment( lib, "fftw3.lib" ) #define SIZEX 16 #define SIZEY 8 #define SIZE (SIZEX*SIZEY) int main( void ){ fftw_complex *in = NULL; fftw_complex *out = NULL; fftw_plan p = NULL; int i,j,idx; size_t mem_size = sizeof(fftw_complex) * SIZE; in = (fftw_complex*)fftw_malloc( mem_size ); out = (fftw_complex*)fftw_malloc( mem_size ); if( !in || !out ){ fprintf( stderr, "failed to allocate %d[byte] memory(-.-)\n", (int)mem_size ); return false; } // !! row-major alignment is recommended, but here, column-major. p = fftw_plan_dft_2d( SIZEY, SIZEX, in, out, FFTW_FORWARD, FFTW_ESTIMATE ); // input data creation printf("----- INPUT -----\n"); for( j=0; j SIZEY; j++ ){ for( i=0; i SIZEX; i++ ){ idx = SIZEX*j+i; // column-major alignment in[idx][0] = 1 + 2*sin(2*M_PI*i/SIZEX) + sin(4*M_PI*j/SIZEY); in[idx][1] = 0; printf("%d %d %lf %lf\n", i, j, in[idx][0], in[idx][1] ); } } fftw_execute(p); // output is DC exchanged and scaled. double scale = 1. / SIZE; printf("\n----- RESULT -----\n"); for( j=0; j SIZEY; j++ ){ for( i=0; i SIZEX; i++ ){ idx = SIZEX*j+i; printf("%d %d %lf %lf\n", i, j, out[idx][0]*scale, out[idx][1]*scale ); } } if( p ) fftw_destroy_plan(p); if( in ) fftw_free(in); if( out ) fftw_free(out); return true; }
https://w.atwiki.jp/amaeda/pages/12.html
/* * test of self compiled fftw3 * * @author maeda * @date 2008/09/09 */ #include stdio.h #define _USE_MATH_DEFINES #include math.h #include "fftw3.h" #pragma comment( lib, "fftw3.lib" ) #define SIZE 64 int main( void ){ fftw_complex *in = NULL; fftw_complex *out = NULL; fftw_plan p = NULL; int i; size_t mem_size = sizeof(fftw_complex) * SIZE; in = (fftw_complex*)fftw_malloc( mem_size ); out = (fftw_complex*)fftw_malloc( mem_size ); if( !in || !out ){ fprintf( stderr, "failed to allocate %d[byte] memory(-.-)\n", (int)mem_size ); return false; } p = fftw_plan_dft_1d( SIZE, in, out, FFTW_FORWARD, FFTW_ESTIMATE ); // input data creation printf("----- INPUT -----\n"); for( i=0; i SIZE; i++ ){ in[i][0] = 1 + 2*sin(2*M_PI*i/SIZE) + sin(4*M_PI*i/SIZE); in[i][1] = 0; printf("%d %lf\n", i, in[i][0] ); } fftw_execute(p); // output is DC exchanged and scaled. double scale = 1. / SIZE; printf("\n----- RESULT -----\n"); for( i=0; i SIZE; i++ ){ printf("%d %lf %lfi\n", i, out[i][0]*scale, out[i][1]*scale ); } if( p ) fftw_destroy_plan(p); if( in ) fftw_free(in); if( out ) fftw_free(out); return true; }
https://w.atwiki.jp/amaeda/
- 番目のお客様 (^-^)/~~ FFTWのページ このページは、フリーのFFTライブラリFFTWが有効に活用されるように作られたページです。 マニュアルの日本語訳や、使用上の注意、サンプルプログラムがあります。 未完成のところも多いですが、常時更新していきます。 FFTWはバージョン2と3では大幅に仕様が異なるので、ご利用のバージョンにご注意ください。 ご意見や、感想を掲示板のほうへお寄せください。 動かない、分からないなどのお問い合わせは takaidohigasi あっと gmail.com へ!(回答あるかどうかは、保証しかねますが。。。) FFTW3.1.2 マニュアル FFTWとは?? FFTWの使い方 (簡単な使い方)複素1次元離散フーリエ変換 複素多次元離散フーリエ変換 実データ1次元離散フーリエ変換 実データ多次元離散フーリエ変換 その他重要なことデータの並び 多次元配列のフォーマット 知の利用-プランの保存 知の利用上の注意 FFTWリファレンス (詳細なマニュアル) データタイプとファイル プランの利用 ベーシックインターフェース サンプルプログラム programs 掲示板 BBS リンク 本家FFTW 本家マニュアル(英語) 修正/更新 実データ1次元離散フーリエ変換の対称性を利用した、計算機資源節約の節で、hermitian redundancyをハミルトニアン冗長性と誤訳していました。正しくはエルミート~。 4.2節プランの利用を追加しました。 4.3節ベーシックインターフェース4.3.2プランのフラグのところまで追加しました。 位相を求めるサンプルプログラムの間違えを修正しました。ご指摘ありがとうございます!
https://w.atwiki.jp/amaeda/pages/20.html
チュートリアル Tutorial この章では、FFTWの基本的な使い方、つまりどうやって単一の配列のフーリエ変換を求めるのか、について述べます。 この章では、全ての事柄に対しては言及しません。 詳しくいえば、ここに載っていない追加的なルーチンやフラグがFFTWには実装されています。それについては、追加的になにができるのかが記してある場所をできる限り、明示したいとおもいます。より詳細な情報については4章のFFTW Referenceを参照してください。 プログラムを使用するためには、FFTWのインストールとコンパイルが必要です。インストールの詳細については、8章のInstallation and Customizationを参照してください。 チュートリアルは、順番に読んでいくことをおすすめします。例え、最初の節で説明される一次元DFT以外に興味を持っていたとしても、少なくとも、他の節を読む前に、最初の節(Complex One-Dimensional DFTs)は目を通してください。 FFTWのバージョン2以前のユーザーは7章のUpgrading FFTW version2を参照すると良いでしょう。 複素1次元離散フーリエ変換(Complex One-Dimensional DFTs) 複素多次元離散フーリエ変換(Complex Multi-Dimensional DFTs) 実データ1次元離散フーリエ変換(One-Dimensional DFTs of Real Data) 実データ多次元離散フーリエ変換(Multi-Dimensional DFTs of Real Data)
https://w.atwiki.jp/amaeda/pages/49.html
FFTWマニュアル - FFTWリファレンス FFTWリファレンス FFTW Reference この章では、連続(シングルプロセッサの)FFTWの関数すべての、完全なマニュアルを扱います。並列変換については、5章のParallel FFTWをご覧くさい データタイプとファイル 複素数 精度 メモリの確保 プランの利用 ベーシックインターフェース
https://w.atwiki.jp/amaeda/pages/31.html
FFTWマニュアル - データの並び - SIMDの並びとfftw_malloc SIMDのならびとfftw_malloc SIMD alignment and fftw_malloc "Single Instruction Multiple Data"(単一命令複数データ流)を意味するSIMDは、 いくつかの、単一命令で複数回(通常2回または4回)同時に操作を行えるプロセッサでサポートされています。SIMD浮動小数点命令は、いくつかのポピューラーなCPUで使えます。たとえば、SSE/SSE2(単精度/倍精度)は、Pentium Ⅲ/Ⅳ以上、3DNow!(単精度)はAMD K7以上、そしてAltiVec(単精度)はPowerPC G4以上で利用できます。FFTWは、これらのいかなるシステムの対してもSIMD命令をサポートするようにコンパイルできます。 FFTWライブラリをSIMDサポートをするようにコンパイルして、リンクすると、ほとんどの複素数変換およびr2c/c2r変換で、劇的なスピードの向上が得られます。しかしながら、このスピードアップを得るためには、FFTWにわたされる複素数(または実数)データの配列は、メモリ空間で特別にならんでなければいけません(典型的には16-byte揃えです)。そして、しばしばこのならびは、mallocなどの通常のメモリ確保ルーチンよりも制約がきびしくなっています。 したがいまして、SIMDに適切な並びを保証するためには、変換に用いるデータをfftw_mallocで確保し、fftw_freeで解放するようにすることをお勧めします。これらの関数はmalloc/freeと、返り値のポインタが、必要とされる並びを保証していること以外は、まったく同じインターフェースおよび動作をします。(これは、memalignだとか、個々のOSのそれに相当する関数を呼ぶことで実現されています) しかし、絶対にfftw_mallocでメモリを確保しなければならないわけではありません。あなたの好きなように、mallocやnew(C++)や、固定サイズの配列の宣言によって確保してもよいでしょう。もし配列が適切に並んでいないようであれば、FFTWはSIMDの拡張命令を使わなくなります。
https://w.atwiki.jp/amaeda/pages/42.html
/** * @brief basic form of fftw with opencv */ #include stdio.h #include cv.h #include cxcore.h #include highgui.h #define _USE_MATH_DEFINES #include "math.h" #include "fftw3.h" #pragma comment( lib, "cv.lib" ) #pragma comment( lib, "cxcore.lib" ) #pragma comment( lib, "highgui.lib" ) #pragma comment( lib, "fftw3.lib" ) #define SIZEX 128 #define SIZEY 64 int maGetPowerSpectol2D( IplImage *src, IplImage *dst ); int maShiftOrigin2D( IplImage *src, IplImage *dst ); int main( void ) { IplImage *img = NULL; // image for data manipulation IplImage *out = NULL; // image for result visualization fftw_complex *data = NULL; fftw_plan plan= NULL; CvScalar tmp; int i,j; data = (fftw_complex*)fftw_malloc( sizeof(fftw_complex) * SIZEX * SIZEY ); img = cvCreateImageHeader( cvSize( SIZEX, SIZEY ), IPL_DEPTH_64F, 2 ); img- imageData = (char*)data; for( j=0; j SIZEY; j++ ){ for( i=0; i SIZEX; i++ ){ tmp = cvScalar( 1 + 2*sin(2*M_PI*i/SIZEX) + 4*sin(4*M_PI*j/SIZEY), 0 ); cvSet2D( img, j, i, tmp ); } } plan = fftw_plan_dft_2d( SIZEY, SIZEX, data, data, FFTW_FORWARD, FFTW_ESTIMATE ); fftw_execute( plan ); out = cvCreateImage( cvSize( SIZEX, SIZEY ), IPL_DEPTH_8U, 1 ); maGetPowerSpectol2D( img, out ); maShiftOrigin2D( out, out ); cvNamedWindow( "result" ); cvShowImage( "result", out ); cvWaitKey( 0 ); cvDestroyWindow( "result" ); if( plan ) fftw_destroy_plan( plan ); if( out ) cvReleaseImage( out ); if( img ) cvReleaseImageHeader( img ); if( data ) fftw_free( data ); return true; } /* * Get the powerspectol * src must be IPL_DEPTH_64F(double), dst must be IPL_DEPTH_8U(unsigned char) * @param src source image * @param dst destination image */ int maGetPowerSpectol2D( IplImage *src, IplImage *dst ) { IplImage *re = NULL; IplImage *im = NULL; CvSize size; double max,min; size = cvGetSize( src ); re = cvCreateImage( size, IPL_DEPTH_64F, 1 ); im = cvCreateImage( size, IPL_DEPTH_64F, 1 ); // get the log scaled powerspectol log( 1 + sqrt(re*re+im*im) ) cvSplit( src, re, im, NULL, NULL ); cvPow( re, re, 2.0 ); cvPow( im, im, 2.0 ); cvAdd( re, im, re ); cvPow( re, re, 0.5 ); cvAddS( re, cvScalarAll(1.0), re ); cvLog( re, re ); // scailing to 0-255 cvMinMaxLoc( re, min, max ); cvScale( re, re, 1.0 / (max - min), 1.0 * (-min) / (max - min ) ); cvConvertScale( re, dst, 255 ); if( re ) cvReleaseImage( re ); if( im ) cvReleaseImage( im ); return true; } /* * Shift axis origin ( FFT result, and so on ) * This function applies for both in-place and out-place transform. * Input and Output takes any size, channel, depth. They have only to be the same size, channel, depth * @param src source image * @param dst destination image */ int maShiftOrigin2D( IplImage *src, IplImage *dst ) { int cx, cy; CvMat *tmp = NULL; CvMat q1stub, q2stub; CvMat q3stub, q4stub; CvMat d1stub, d2stub; CvMat d3stub, d4stub; CvMat *q1, *q2, *q3, *q4; CvMat *d1, *d2, *d3, *d4; CvSize size = cvGetSize (src); CvSize dst_size = cvGetSize (dst); if (src == dst) tmp = cvCreateMat (size.height/2, size.width/2, cvGetElemType (src) ); cx = size.width / 2; cy = size.height / 2; q1 = cvGetSubRect (src, q1stub, cvRect (0, 0, cx, cy)); q2 = cvGetSubRect (src, q2stub, cvRect (cx, 0, cx, cy)); q3 = cvGetSubRect (src, q3stub, cvRect (cx, cy, cx, cy)); q4 = cvGetSubRect (src, q4stub, cvRect (0, cy, cx, cy)); d1 = cvGetSubRect (dst, d1stub, cvRect (0, 0, cx, cy)); d2 = cvGetSubRect (dst, d2stub, cvRect (cx, 0, cx, cy)); d3 = cvGetSubRect (dst, d3stub, cvRect (cx, cy, cx, cy)); d4 = cvGetSubRect (dst, d4stub, cvRect (0, cy, cx, cy)); if (src != dst) { cvCopy (q3, d1, 0); cvCopy (q4, d2, 0); cvCopy (q1, d3, 0); cvCopy (q2, d4, 0); } else { cvCopy (q3, tmp, 0); cvCopy (q1, q3, 0); cvCopy (tmp, q1, 0); cvCopy (q4, tmp, 0); cvCopy (q2, q4, 0); cvCopy (tmp, q2, 0); } if( tmp ) cvReleaseMat( tmp ); return true; }
https://w.atwiki.jp/amaeda/pages/29.html
実データ1次元離散フーリエ変換 One-Dimensioanal DFTs of Real Data 実際の利用上、多くの場合、入力データin[i]は実数で、その場合DFTの出力は、”エルミート冗長性”を満たします。すなわち、out[i]はout[n-i]の共役となります。この性質の利点を生かすと、大雑把にいえば、スピードの向上と、メモリ節約を図ることができます。 このスピード向上と、メモリ節約といった利点と引き換えに、ユーザーはFFTWの複素変換の簡易性を犠牲にしなければなりません。 第1に、入出力の配列が異なるサイズ、型になります。入力がn個の実数だった場合、出力は(冗長でない形で)n/2+1個の複素数となります。また、入出力が等しい、in-place変換の場合、入力の配列にわずかに”パディング”(余白)を設ける必要があります。 第2に、複素数から実数への逆変換は、デフォルトで、入力配列が変化してしまうという副作用があります。どちらの不便もユーザーに対して重大な問題は引き起こしませんが、十分意識することが重要です。 この実データ変換のルーチンは、複素変換のものとほとんど同じです。 すなわち、doubleまたは、fftw_complex型の配列のメモリを確保し(fftw_malloc推奨)、fftw_planを作成し、fftw_execute(plan)を必要な回数だけ実行し、fftw_destroy_plan(plan)によって、後処理をします(fftw_freeも)。 違いといえば、入力(または出力)がdouble型で、新しいプラン生成のルーチンがあるだけです。1次元では、 fftw_plan fftw_plan_dft_r2c_1d( int n, double *in, fftw_complex *out, unsigned flags ); fftw_plan fftw_plan_dft_c2r_1d( int n, fftw_complex *in, double *out, unsigned flags ); となります。 それぞれ、実入力から複素エルミート出力(r2c)、複素エルミート入力から実出力(c2r)の変換です。複素離散フーリエ変換プランナーとは異なり、signの引数がありません。代わりに、r2c離散フーリエ変換は、常にFFTW_FORWARDで、c2rはFFTW_BACKWARDとします。(単精度/倍倍精度の、fftwfとfftwlに対しては、doubleをそれぞれfloattとlong doubleに置き換えてください) ここで、nは離散フーリエ変換の”論理”サイズで、必ずしも配列の物理的サイズではありません。詳しくいえば、実数(double)の配列はn個の要素を持ちますが、一方でfftw_complexは、n/2+1個(切り捨て)の要素となります。入出力が同じ、in-place変換では、inとoutが別名の同じ配列で、これは入出力の両者を格納できる大きさである必要があります。したがって、実配列は、実際のところ2*(n/2+1)個の要素を持ち、最初のn個以降は使用されないパディングとなります。 複素配列のk番目の要素は、対応する複素離散フーリエ変換のk番目の要素にきっかり等しくなります。また、すべての正のnがサポートされています。もっとも、小さな素数の積が最も効率がよく、最悪の場合でもO(nlogn)アルゴリズムとなります。 上述の通り、c2r変換は、たとえ入出力が異なるout-of-place変換であっても、入力配列の値は変換前後で保持されません。これに対しては、必要であれば、FFTW_PRESERVE_INPUTオプションを、flagに設けることでこれを防ぐことができますが、残念なことにある程度パフォーマンスが低下します。このオプションは、現在は次の章の実データ多次元の離散フーリエ変換には対応していません。 実データの離散フーリエ変換に慣れている読者なら、複素出力の、0番目(DC)とn/2番目(nが偶数のとき、ナイキスト周波数)の要素は、実数になることを思い出していただけるかと思います。いくつかの実装では、入出力の配列サイズをそろえるため、上記性質を利用し、ナイキスト周波数の要素をDCの虚部に格納することがあります。しかしながら、このような実装は高次の変換に対しては一般性がなく、メモリ節約は枝葉末節であるため、FFTWではサポートしません。 1次元のr2cやc2r離散フーリエ変換の代替インターフェースとして、”r2r”というものがあります(2.5.1節 The Halfcomplex-format DFTを参照)。 これは、入力配列と同じ型、サイズの、"halfcomplex"形式の出力が用いられるものです。このインターフェースは、多次元変換に対しては大変便利というものではありませんが、よりよい性能を発揮することもしばしあります。