Skip to content
Snippets Groups Projects
Commit 6cfd8486 authored by Timothee Chabat's avatar Timothee Chabat Committed by Kitware Robot
Browse files

Merge topic 'fixvtkFFTNaming'


61363123 Cleanup and improve variables naming in vtkTableFFT
05b5bb10 Fix and improve vtkTableFFT documentation
8b61eb6b Fix vtkTableFFT::SetAverageFft setter
9de3b4dc Restore array naming behavior of vtkTableFFT

Acked-by: default avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !9312
parents 75957da5 61363123
No related branches found
No related tags found
No related merge requests found
......@@ -86,6 +86,9 @@ Changes made since VTK 9.1.0 include the following.
using the `Degree` flag.
- `vtkTemporalDataSetCache` no longer crashes when a nonexistent timestep is
requested.
- `vtkTableFFT` no longer prefixes output array names with "FFT_" like it was in 9.1
and just keep the same name as the input like it was doing before 9.1. A new API
has been added to keep 9.1 behavior when needed.
[](#changes-interaction)
### Interaction
......
......@@ -142,8 +142,9 @@ int vtkTableFFT::RequestData(vtkInformation* vtkNotUsed(request),
dataArray->GetNumberOfComponents() == 1 && !array->IsA("vtkIdTypeArray"))
{
vtkSmartPointer<vtkDataArray> fft = this->DoFFT(dataArray);
auto namefft = std::string("FFT_").append(arrayName);
fft->SetName(namefft.c_str());
std::string newArrayName =
this->PrefixOutputArrays ? std::string("FFT_").append(arrayName) : arrayName;
fft->SetName(newArrayName.c_str());
output->AddColumn(fft);
}
// else pass the array to the output
......@@ -193,7 +194,7 @@ int vtkTableFFT::RequestData(vtkInformation* vtkNotUsed(request),
void vtkTableFFT::Initialize(vtkTable* input)
{
// Find time array and compute sample rate
std::size_t nTimestep = input->GetNumberOfRows();
std::size_t nsamples = input->GetNumberOfRows();
vtkDataArray* timeArray = nullptr;
vtkIdType numColumns = input->GetNumberOfColumns();
for (vtkIdType col = 0; col < numColumns; col++)
......@@ -215,53 +216,52 @@ void vtkTableFFT::Initialize(vtkTable* input)
}
// Check if we can average and compute the size of the windowing function
std::size_t actualSize = nTimestep;
std::size_t nfft = nsamples;
this->Internals->Average = this->AverageFft;
if (this->AverageFft)
{
actualSize = vtkMath::NearestPowerOfTwo(static_cast<int>(this->BlockSize));
if (actualSize > (nTimestep - this->NumberOfBlock))
nfft = vtkMath::NearestPowerOfTwo(static_cast<int>(this->BlockSize));
if (nfft > (nsamples - this->NumberOfBlock))
{
vtkWarningMacro(
"Cannot average FFT per block : block size is too large compared to the input. "
<< "Computing FFT on the whole input.");
this->Internals->Average = false;
actualSize = nTimestep;
nfft = nsamples;
}
}
// Generate windowing function
// We're caching the windowing function for more efficiency when applying this filter
// on different tables multiple times
if (this->Internals->WindowLastUpdated < this->Internals->WindowTimeStamp.GetMTime())
if (this->Internals->WindowLastUpdated < this->Internals->WindowTimeStamp.GetMTime() ||
nfft != this->Internals->Window.size())
{
this->Internals->UpdateWindow(this->WindowingFunction, actualSize);
this->Internals->UpdateWindow(this->WindowingFunction, nfft);
this->Internals->WindowLastUpdated = this->Internals->WindowTimeStamp.GetMTime();
}
// Get output size
const std::size_t nfft = this->Internals->Window.size();
this->Internals->OutputSize = this->OptimizeForRealInput ? (nfft / 2) + 1 : nfft;
}
//------------------------------------------------------------------------------
vtkSmartPointer<vtkDataArray> vtkTableFFT::DoFFT(vtkDataArray* input)
{
const std::size_t nvalues = input->GetNumberOfValues();
const std::size_t nsamples = input->GetNumberOfValues();
const std::size_t nblocks =
this->Internals->Average ? static_cast<std::size_t>(std::max(1, this->NumberOfBlock)) : 1;
const double blockCoef = 1.0 / nblocks;
const std::size_t nfft = this->Internals->Window.size();
const std::size_t outSize = this->Internals->OutputSize;
const std::size_t stepSize = (nblocks <= 1) ? 0 : (nvalues - nfft - 1) / (nblocks - 1);
const std::size_t stepSize = (nblocks <= 1) ? 0 : (nsamples - nfft - 1) / (nblocks - 1);
std::vector<vtkFFT::ScalarNumber> block(nfft);
std::vector<vtkFFT::ComplexNumber> resFft(outSize, vtkFFT::ComplexNumber{ 0.0, 0.0 });
for (std::size_t b = 0; b < nblocks; ++b)
{
// Copy data from input to block
vtkIdType startBlock = b * stepSize;
std::vector<vtkFFT::ScalarNumber> block(nfft);
for (std::size_t i = 0; i < nfft; ++i)
{
block[i] = input->GetTuple1(startBlock + i);
......@@ -323,7 +323,7 @@ void vtkTableFFT::SetAverageFft(bool _arg)
vtkDebugMacro(<< this->GetClassName() << " (" << this << "): setting AverageFft to " << _arg);
if (this->AverageFft != _arg)
{
this->BlockSize = _arg;
this->AverageFft = _arg;
this->Internals->WindowTimeStamp.Modified();
this->Modified();
}
......
......@@ -25,8 +25,13 @@
* @brief FFT for table columns
*
* vtkTableFFT performs the Fast Fourier Transform on the columns of a table.
* It can perform the FFT per block in order to resample the input and have a
* smaller output, and also offer a interface for windowing the input signal.
* It can perform the FFT per block : this performs something close to the
* Welch method but it uses raw FFTs instead of periodograms. This allows to
* reduce the impact of noise as well as speeding up the filter when the input
* signal is too big.
*
* It is also possible to apply a window on the input signal. If performing
* the FFT per block then the window will be applied to each block instead.
*
* The filter will look for a "Time" array (case insensitive) to determine the
* sampling frequency. "Time" array is considered to have the same frequency
......@@ -39,6 +44,7 @@
#ifndef vtkTableFFT_h
#define vtkTableFFT_h
#include "vtkDeprecation.h" // For VTK_DEPRECATED_IN_9_2_0
#include "vtkFiltersGeneralModule.h" // For export macro
#include "vtkSmartPointer.h" // For internal method.
#include "vtkTableAlgorithm.h"
......@@ -63,30 +69,37 @@ public:
MAX_WINDOWING_FUNCTION
};
//@{
///@{
/**
* Specify if the output should be normalized.
* Specify if the output should be normalized. This has 2 consequences :
* first is that for each block the mean signal value is removed from the input
* signal before doing the actual FFT. Second is that it will take the norm of
* the resulting imaginary values and normalize it using Parseval's theorem.
*
* Default is false
*/
vtkGetMacro(Normalize, bool);
vtkSetMacro(Normalize, bool);
vtkBooleanMacro(Normalize, bool);
//@}
///@}
//@{
///@{
/**
* Specify if the input should be split in multiple blocks to compute
* an average fft across all blocks.
* an average fft across all blocks. It uses the Welch method except
* that it averages raw FFTs instead of periodograms.
*
* @see vtkTableFFT::SetNumberOfBlock(int)
* @see vtkTableFFT::SetBlockSize(int)
*
* Default is false
*/
vtkGetMacro(AverageFft, bool);
virtual void SetAverageFft(bool);
vtkBooleanMacro(AverageFft, bool);
//@}
///@}
//@{
///@{
/**
* Specify if the filter should use the optimized discrete fourier transform for
* real values. This will cause output columns to have from n to ((n / 2) + 1) rows.
......@@ -96,72 +109,93 @@ public:
vtkGetMacro(OptimizeForRealInput, bool);
vtkSetMacro(OptimizeForRealInput, bool);
vtkBooleanMacro(OptimizeForRealInput, bool);
//@}
///@}
//@{
///@{
/**
* Specify if the filter should create a frequency column based on a column
* named "time" (not case sensitive). An evenly-spaced time array is expected.
*
* @see vtkTableFFT::SetDefaultSampleRate(double)
*
* Default is false
*/
vtkGetMacro(CreateFrequencyColumn, bool);
vtkSetMacro(CreateFrequencyColumn, bool);
vtkBooleanMacro(CreateFrequencyColumn, bool);
//@}
///@}
//@{
///@{
/**
* If the "Time" column is not found then this value will be used.
* Expressed in Hz.
*
* Default is 10000 (Hz)
* Default is 10'000 (Hz)
*/
vtkGetMacro(DefaultSampleRate, double);
vtkSetMacro(DefaultSampleRate, double);
//@}
///@}
//@{
///@{
/**
* Only used if @c AverageFft is true
*
* Specify the number of blocks to use when computing the average fft over
* the whole input sample array. If NumberOfBlock == 1, no average is done
* and we only compute the fft on the first @c BlockSize samples of the input data.
* the whole input sample array. Blocks can overlap if @c NumberOfBlock times
* @c BlockSize is superior to the input signal size.
*
* This parameter is ignored if @c BlockSize is superior
* to the number of samples of the input array.
*
* @see vtkTableFFT::SetAverageFft(bool)
*
* Default is 2
*/
vtkGetMacro(NumberOfBlock, int);
vtkSetMacro(NumberOfBlock, int);
//@}
///@}
//@{
///@{
/**
* Only used if @c AverageFft is true
*
* Specify the number of samples to use for each block. This should be a power of 2.
* If not, the closest power of two will be used anyway.
*
* @see vtkTableFFT::SetAverageFft(bool)
*
* Default is 1024
*/
vtkGetMacro(BlockSize, int);
virtual void SetBlockSize(int);
//@}
///@}
//@{
///@{
/**
* Specify the windowing function to apply on the input.
* If @c AverageFft is true the windowing function will be
* applied per block and not on the whole input
* applied per block and not on the whole input.
*
* Default is RECTANGULAR (does nothing)
*/
vtkGetMacro(WindowingFunction, int);
virtual void SetWindowingFunction(int);
//@}
///@}
///@{
/**
* Specify if output array should be prefixed by "FFT_" or not.
* This behavior was introduced in v9.1. Default is false.
*
* Deprecated in v9.2.
*/
VTK_DEPRECATED_IN_9_2_0(
"Deprecated in favor of always keeping the output array names the same as the input.")
vtkGetMacro(PrefixOutputArrays, bool);
VTK_DEPRECATED_IN_9_2_0(
"Deprecated in favor of always keeping the output array names the same as the input.")
vtkSetMacro(PrefixOutputArrays, bool);
///@}
protected:
vtkTableFFT();
......@@ -195,6 +229,8 @@ private:
int WindowingFunction = RECTANGULAR;
double DefaultSampleRate = 1e4;
bool PrefixOutputArrays = false;
struct vtkInternal;
std::unique_ptr<vtkInternal> Internals;
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment