vtk-m/vtkm/worklet/wavelets/WaveletFilter.h
Kenneth Moreland d1a4aecc59 Improvements to moving data into ArrayHandle
We have made several improvements to adding data into an `ArrayHandle`.

## Moving data from an `std::vector`

For numerous reasons, it is convenient to define data in a `std::vector`
and then wrap that into an `ArrayHandle`. It is often the case that an
`std::vector` is filled and then becomes unused once it is converted to an
`ArrayHandle`. In this case, what we really want is to pass the data off to
the `ArrayHandle` so that the `ArrayHandle` is now managing the data and
not the `std::vector`.

C++11 has a mechanism to do this: move semantics. You can now pass
variables to functions as an "rvalue" (right-hand value). When something is
passed as an rvalue, it can pull state out of that variable and move it
somewhere else. `std::vector` implements this movement so that an rvalue
can be moved to another `std::vector` without actually copying the data.
`make_ArrayHandle` now also takes advantage of this feature to move rvalue
`std::vector`s.

There is a special form of `make_ArrayHandle` named `make_ArrayHandleMove`
that takes an rvalue. There is also a special overload of
`make_ArrayHandle` itself that handles an rvalue `vector`. (However, using
the explicit move version is better if you want to make sure the data is
actually moved.)

## Make `ArrayHandle` from initalizer list

A common use case for using `std::vector` (particularly in our unit tests)
is to quickly add an initalizer list into an `ArrayHandle`. Now you can
by simply passing an initializer list to `make_ArrayHandle`.

## Deprecated `make_ArrayHandle` with default shallow copy

For historical reasons, passing an `std::vector` or a pointer to
`make_ArrayHandle` does a shallow copy (i.e. `CopyFlag` defaults to `Off`).
Although more efficient, this mode is inherintly unsafe, and making it the
default is asking for trouble.

To combat this, calling `make_ArrayHandle` without a copy flag is
deprecated. In this way, if you wish to do the faster but more unsafe
creation of an `ArrayHandle` you should explicitly express that.

This requried quite a few changes through the VTK-m source (particularly in
the tests).

## Similar changes to `Field`

`vtkm::cont::Field` has a `make_Field` helper function that is similar to
`make_ArrayHandle`. It also features the ability to create fields from
`std::vector`s and C arrays. It also likewise had the same unsafe behavior
by default of not copying from the source of the arrays.

That behavior has similarly been depreciated. You now have to specify a
copy flag.

The ability to construct a `Field` from an initializer list of values has
also been added.
2020-07-23 10:53:38 -06:00

217 lines
6.9 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_worklet_wavelets_waveletfilter_h
#define vtk_m_worklet_wavelets_waveletfilter_h
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/worklet/wavelets/FilterBanks.h>
#include <vtkm/Math.h>
namespace vtkm
{
namespace worklet
{
namespace wavelets
{
enum WaveletName
{
CDF9_7,
CDF5_3,
CDF8_4,
HAAR,
BIOR4_4, // the same as CDF9_7
BIOR3_3, // the same as CDF8_4
BIOR2_2, // the same as CDF5_3
BIOR1_1 // the same as HAAR
};
// Wavelet filter class;
// functionally equivalent to WaveFiltBase and its subclasses in VAPoR.
class WaveletFilter
{
public:
// constructor
WaveletFilter(WaveletName wtype)
: Symmetricity(true)
, FilterLength(0)
, LowDecomposeFilter(nullptr)
, HighDecomposeFilter(nullptr)
, LowReconstructFilter(nullptr)
, HighReconstructFilter(nullptr)
{
if (wtype == CDF9_7 || wtype == BIOR4_4)
{
this->FilterLength = 9;
this->AllocateFilterMemory();
this->wrev(vtkm::worklet::wavelets::hm4_44, LowDecomposeFilter, FilterLength);
this->qmf_wrev(vtkm::worklet::wavelets::h4, HighDecomposeFilter, FilterLength);
this->verbatim_copy(vtkm::worklet::wavelets::h4, LowReconstructFilter, FilterLength);
this->qmf_even(vtkm::worklet::wavelets::hm4_44, HighReconstructFilter, FilterLength);
}
else if (wtype == CDF8_4 || wtype == BIOR3_3)
{
this->FilterLength = 8;
this->AllocateFilterMemory();
this->wrev(vtkm::worklet::wavelets::hm3_33, LowDecomposeFilter, FilterLength);
this->qmf_wrev(vtkm::worklet::wavelets::h3 + 6, HighDecomposeFilter, FilterLength);
this->verbatim_copy(vtkm::worklet::wavelets::h3 + 6, LowReconstructFilter, FilterLength);
this->qmf_even(vtkm::worklet::wavelets::hm3_33, HighReconstructFilter, FilterLength);
}
else if (wtype == CDF5_3 || wtype == BIOR2_2)
{
this->FilterLength = 5;
this->AllocateFilterMemory();
this->wrev(vtkm::worklet::wavelets::hm2_22, LowDecomposeFilter, FilterLength);
this->qmf_wrev(vtkm::worklet::wavelets::h2 + 6, HighDecomposeFilter, FilterLength);
this->verbatim_copy(vtkm::worklet::wavelets::h2 + 6, LowReconstructFilter, FilterLength);
this->qmf_even(vtkm::worklet::wavelets::hm2_22, HighReconstructFilter, FilterLength);
}
else if (wtype == HAAR || wtype == BIOR1_1)
{
this->FilterLength = 2;
this->AllocateFilterMemory();
this->wrev(vtkm::worklet::wavelets::hm1_11, LowDecomposeFilter, FilterLength);
this->qmf_wrev(vtkm::worklet::wavelets::h1 + 4, HighDecomposeFilter, FilterLength);
this->verbatim_copy(vtkm::worklet::wavelets::h1 + 4, LowReconstructFilter, FilterLength);
this->qmf_even(vtkm::worklet::wavelets::hm1_11, HighReconstructFilter, FilterLength);
}
this->MakeArrayHandles();
}
// destructor
~WaveletFilter()
{
this->LowDecomType.ReleaseResources();
this->HighDecomType.ReleaseResources();
this->LowReconType.ReleaseResources();
this->HighReconType.ReleaseResources();
if (LowDecomposeFilter)
{
delete[] LowDecomposeFilter;
LowDecomposeFilter = HighDecomposeFilter = LowReconstructFilter = HighReconstructFilter =
nullptr;
}
}
vtkm::Id GetFilterLength() { return this->FilterLength; }
bool isSymmetric() { return this->Symmetricity; }
using FilterType = vtkm::cont::ArrayHandle<vtkm::Float64>;
const FilterType& GetLowDecomposeFilter() const { return this->LowDecomType; }
const FilterType& GetHighDecomposeFilter() const { return this->HighDecomType; }
const FilterType& GetLowReconstructFilter() const { return this->LowReconType; }
const FilterType& GetHighReconstructFilter() const { return this->HighReconType; }
private:
bool Symmetricity;
vtkm::Id FilterLength;
vtkm::Float64* LowDecomposeFilter;
vtkm::Float64* HighDecomposeFilter;
vtkm::Float64* LowReconstructFilter;
vtkm::Float64* HighReconstructFilter;
FilterType LowDecomType;
FilterType HighDecomType;
FilterType LowReconType;
FilterType HighReconType;
void AllocateFilterMemory()
{
LowDecomposeFilter = new vtkm::Float64[static_cast<std::size_t>(FilterLength * 4)];
HighDecomposeFilter = LowDecomposeFilter + FilterLength;
LowReconstructFilter = HighDecomposeFilter + FilterLength;
HighReconstructFilter = LowReconstructFilter + FilterLength;
}
void MakeArrayHandles()
{
LowDecomType =
vtkm::cont::make_ArrayHandle(LowDecomposeFilter, FilterLength, vtkm::CopyFlag::Off);
HighDecomType =
vtkm::cont::make_ArrayHandle(HighDecomposeFilter, FilterLength, vtkm::CopyFlag::Off);
LowReconType =
vtkm::cont::make_ArrayHandle(LowReconstructFilter, FilterLength, vtkm::CopyFlag::Off);
HighReconType =
vtkm::cont::make_ArrayHandle(HighReconstructFilter, FilterLength, vtkm::CopyFlag::Off);
}
// Flipping operation; helper function to initialize a filter.
void wrev(const vtkm::Float64* arrIn, vtkm::Float64* arrOut, vtkm::Id length)
{
for (vtkm::Id count = 0; count < length; count++)
{
arrOut[count] = arrIn[length - count - 1];
}
}
// Quadrature mirror filtering operation: helper function to initialize a filter.
void qmf_even(const vtkm::Float64* arrIn, vtkm::Float64* arrOut, vtkm::Id length)
{
if (length % 2 == 0)
{
for (vtkm::Id count = 0; count < length; count++)
{
arrOut[count] = arrIn[length - count - 1];
if (count % 2 != 0)
{
arrOut[count] = -1.0 * arrOut[count];
}
}
}
else
{
for (vtkm::Id count = 0; count < length; count++)
{
arrOut[count] = arrIn[length - count - 1];
if (count % 2 == 0)
{
arrOut[count] = -1.0 * arrOut[count];
}
}
}
}
// Flipping and QMF at the same time: helper function to initialize a filter.
void qmf_wrev(const vtkm::Float64* arrIn, vtkm::Float64* arrOut, vtkm::Id length)
{
qmf_even(arrIn, arrOut, length);
vtkm::Float64 tmp;
for (vtkm::Id count = 0; count < length / 2; count++)
{
tmp = arrOut[count];
arrOut[count] = arrOut[length - count - 1];
arrOut[length - count - 1] = tmp;
}
}
// Verbatim Copying: helper function to initialize a filter.
void verbatim_copy(const vtkm::Float64* arrIn, vtkm::Float64* arrOut, vtkm::Id length)
{
for (vtkm::Id count = 0; count < length; count++)
{
arrOut[count] = arrIn[count];
}
}
}; // class WaveletFilter.
} // namespace wavelets.
} // namespace worklet
} // namespace vtkm
#endif