https://www.cnblogs.com/ahfuzhang/p/11083423.html
opencv-3.4.3 \ modules \ core \ include \ opencv2 \ core.hpp:2157
CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
2.関数の実装
opencv-3.4.3 \ modules \ core \ src \ dxt.cpp:3315
void cv::dft( InputArray _src0, OutputArray _dst, int flags, int nonzero_rows )
{
CV_INSTRUMENT_REGION()
#ifdef HAVE_CLAMDFFT
CV_OCL_RUN(ocl::haveAmdFft() && ocl::Device::getDefault().type() != ocl::Device::TYPE_CPU &&
_dst.isUMat() && _src0.dims() <= 2 && nonzero_rows == 0,
ocl_dft_amdfft(_src0, _dst, flags))
#endif
#ifdef HAVE_OPENCL
CV_OCL_RUN(_dst.isUMat() && _src0.dims() <= 2,
ocl_dft(_src0, _dst, flags, nonzero_rows))
#endif
Mat src0 = _src0.getMat(), src = src0;
bool inv = (flags & DFT_INVERSE) != 0;
int type = src.type();
int depth = src.depth();
CV_Assert( type == CV_32FC1 || type == CV_32FC2 || type == CV_64FC1 || type == CV_64FC2 );
// Fail if DFT_COMPLEX_INPUT is specified, but src is not 2 channels.
CV_Assert( !((flags & DFT_COMPLEX_INPUT) && src.channels() != 2) );
if( !inv && src.channels() == 1 && (flags & DFT_COMPLEX_OUTPUT) )
_dst.create( src.size(), CV_MAKETYPE(depth, 2) );
else if( inv && src.channels() == 2 && (flags & DFT_REAL_OUTPUT) )
_dst.create( src.size(), depth );
else
_dst.create( src.size(), type );
Mat dst = _dst.getMat();
int f = 0;
if (src.isContinuous() && dst.isContinuous())
f |= CV_HAL_DFT_IS_CONTINUOUS;
if (inv)
f |= CV_HAL_DFT_INVERSE;
if (flags & DFT_ROWS)
f |= CV_HAL_DFT_ROWS;
if (flags & DFT_SCALE)
f |= CV_HAL_DFT_SCALE;
if (src.data == dst.data)
f |= CV_HAL_DFT_IS_INPLACE;
Ptr<hal::DFT2D> c = hal::DFT2D::create(src.cols, src.rows, depth, src.channels(), dst.channels(), f, nonzero_rows);
c->apply(src.data, src.step, dst.data, dst.step);
}
3.openclを呼び出す
#ifdef HAVE_OPENCL
CV_OCL_RUN(_dst.isUMat() && _src0.dims() <= 2,
ocl_dft(_src0, _dst, flags, nonzero_rows))
#endif
oclの関数実装:
opencv-3.4.3 \ modules \ core \ src \ dxt.cpp:2161
static bool ocl_dft(InputArray _src, OutputArray _dst, int flags, int nonzero_rows)
{
int type = _src.type(), cn = CV_MAT_CN(type), depth = CV_MAT_DEPTH(type);
Size ssize = _src.size();
bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0;
if (!(cn == 1 || cn == 2)
|| !(depth == CV_32F || (depth == CV_64F && doubleSupport))
|| ((flags & DFT_REAL_OUTPUT) && (flags & DFT_COMPLEX_OUTPUT)))
return false;
// if is not a multiplication of prime numbers { 2, 3, 5 }
if (ssize.area() != getOptimalDFTSize(ssize.area()))
return false;
UMat src = _src.getUMat();
bool inv = (flags & DFT_INVERSE) != 0 ? 1 : 0;
if( nonzero_rows <= 0 || nonzero_rows > _src.rows() )
nonzero_rows = _src.rows();
bool is1d = (flags & DFT_ROWS) != 0 || nonzero_rows == 1;
FftType fftType = determineFFTType(cn == 1, cn == 2,
(flags & DFT_REAL_OUTPUT) != 0, (flags & DFT_COMPLEX_OUTPUT) != 0, inv);
UMat output;
if (fftType == C2C || fftType == R2C)
{
// complex output
_dst.create(src.size(), CV_MAKETYPE(depth, 2));
output = _dst.getUMat();
}
else
{
// real output
if (is1d)
{
_dst.create(src.size(), CV_MAKETYPE(depth, 1));
output = _dst.getUMat();
}
else
{
_dst.create(src.size(), CV_MAKETYPE(depth, 1));
output.create(src.size(), CV_MAKETYPE(depth, 2));
}
}
bool result = false;
if (!inv)
{
int nonzero_cols = fftType == R2R ? output.cols/2 + 1 : output.cols;
result = ocl_dft_rows(src, output, nonzero_rows, flags, fftType);
if (!is1d)
result = result && ocl_dft_cols(output, _dst, nonzero_cols, flags, fftType);
}
else
{
if (fftType == C2C)
{
// complex output
result = ocl_dft_rows(src, output, nonzero_rows, flags, fftType);
if (!is1d)
result = result && ocl_dft_cols(output, output, output.cols, flags, fftType);
}
else
{
if (is1d)
{
result = ocl_dft_rows(src, output, nonzero_rows, flags, fftType);
}
else
{
int nonzero_cols = src.cols/2 + 1;
result = ocl_dft_cols(src, output, nonzero_cols, flags, fftType);
result = result && ocl_dft_rows(output, _dst, nonzero_rows, flags, fftType);
}
}
}
return result;
}
4. ocl_dft()のrow / colの呼び出し関数
関数プロトタイプ:
static bool ocl_dft_rows(InputArray _src, OutputArray _dst, int nonzero_rows, int flags, int fftType)
static bool ocl_dft_cols(InputArray _src, OutputArray _dst, int nonzero_cols, int flags, int fftType)
それらの1つのソースコードを見てください:
static bool ocl_dft_rows(InputArray _src, OutputArray _dst, int nonzero_rows, int flags, int fftType)
{
int type = _src.type(), depth = CV_MAT_DEPTH(type);
Ptr<OCL_FftPlan> plan = OCL_FftPlanCache::getInstance().getFftPlan(_src.cols(), depth);
return plan->enqueueTransform(_src, _dst, nonzero_rows, flags, fftType, true);
}
5.fftによって計算されたオブジェクトプール
特定のサイズの各fft計算の前に、一連の初期化データを確立する必要があります。これらの初期化データが毎回同じサイズで確立される場合、それは明らかに無駄です。
そのため、オブジェクトプールが確立され、fftによって計算された新しいサイズが表示されるたびにオブジェクトがキャッシュされます。時間のスペース(ただし、長期実行シナリオではメモリ消費に注意を払う必要があります)。
Ptr<OCL_FftPlan> OCL_FftPlanCache::getFftPlan(int dft_size, int depth)
{
int key = (dft_size << 16) | (depth & 0xFFFF);
std::map<int, Ptr<OCL_FftPlan> >::iterator f = planStorage.find(key);
if (f != planStorage.end())
{
return f->second;
}
else
{
Ptr<OCL_FftPlan> newPlan = Ptr<OCL_FftPlan>(new OCL_FftPlan(dft_size, depth));
planStorage[key] = newPlan;
return newPlan;
}
}
6.fftオブジェクト
opencv-3.4.3 \ modules \ core \ src \ dxt.cpp:1881
struct OCL_FftPlanはコンストラクターで
初期化されます:OCL_FftPlan(int _size、int _depth)
このメソッドを使用した計算:bool enqueueTransform(InputArray _src、OutputArray _dst、int num_dfts、 int flags、int fftType、bool rows = true)const
メソッドのメインコードは、カーネル関数のコンパイルパラメーターを作成することです。
6.1コンパイル、バインディングパラメータ、openclカーネル関数の実行
enqueueTransform()メソッドのコアコードは次のとおりです。
ocl::Kernel k(kernel_name.c_str(), ocl::core::fft_oclsrc, options);
if (k.empty())
return false;
k.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::WriteOnly(dst), ocl::KernelArg::ReadOnlyNoSize(twiddles), thread_count, num_dfts);
return k.run(2, globalsize, localsize, false);
ocl :: Kernelオブジェクトは、openclカーネル関数をコンパイルするために使用されます。
ocl :: KernelArgは、カーネル関数の実行パラメーターをバインドするために使用されます。
k.run()はカーネル関数を実行します。
6.2カーネル関数の定義
ocl :: core :: fft_oclsrcこの定数オブジェクトは、カーネル関数のソースコードを定義します。すべての.h、.hpp、および.cppを検索しましたが、定義が見つかりませんでした。
ソースコードのこの部分は、コンパイルプロセス中に生成されます。
定義:
opencv-3.4.3 / build / modules / core / opencl_kernels_core.hpp:21
extern struct cv::ocl::internal::ProgramEntry fft_oclsrc;
実現:
opencv-3.4.3 / build / modules / core / opencl_kernels_core.cpp:770
struct cv::ocl::internal::ProgramEntry fft_oclsrc={moduleName, "fft",
"#define SQRT_2 0.707106781188f\n"
openclカーネル関数コードをC ++文字列に変換するためにスクリプトのみが使用されているようです。
6.3カーネル関数の定義ファイル
最後に、opencl fftカーネル関数ファイルが見つかりました:opencv
-3.4.3 \ modules \ core \ src \ opencl \ fft.cl
ここには明らかな問題があります。カーネル関数は、呼び出されるたびにコンパイルする必要があります。コンパイルされた結果がどこにキャッシュされているのかわかりません。
7.cv :: dft()可能な最適化ポイント
- すべてのカーネル関数呼び出しをコンパイルし、ocl :: Kernelオブジェクトをキャッシュする必要があります
- C関数のスタイルをオブジェクト指向のスタイルに変更し、UMatデータのアップロード/コア関数の操作/ UMatデータのダウンロードを非同期キューに追加します。これにより、複数のdft()を連続して計算するときに、CPUがGPUの結果を待機するのを回避できます。