19#ifndef elxCoreMainGTestUtilities_h
20#define elxCoreMainGTestUtilities_h
28#include <itkImageBase.h>
29#include <itkImageBufferRange.h>
30#include <itkImageRegionRange.h>
37#include <initializer_list>
47#include <gtest/gtest.h>
56template <
typename TNested>
73 what() const noexcept
override
81#define ELX_GTEST_EXPECT_FALSE_AND_THROW_EXCEPTION_IF(condition) \
84 EXPECT_FALSE(true) << "Expected to be false: " #condition; \
85 throw ::elastix::CoreMainGTestUtilities::Exception("Exception thrown because " #condition); \
87 static_assert(true, "Expect a semi-colon ';' at the end of a macro call")
89template <
typename TSmartPo
inter>
93 static_assert(!std::is_pointer_v<TSmartPointer>,
"For raw pointers, use itk::Deref instead!");
97 throw Exception(
"DerefSmartPointer error: the (smart) pointer should not be null!");
109 if (container.empty())
111 throw Exception(
"Front error: the container should be non-empty!");
113 return container.front();
121 static_assert(std::is_same<
decltype(T::New()), itk::SmartPointer<T>>{},
122 "T::New() must return an itk::SmartPointer<T>!");
124 const auto ptr = T::New();
127 throw Exception(
"New() error: should not return null!");
133template <
typename TPixel,
unsigned int VImageDimension>
136 const itk::Index<VImageDimension> & regionIndex,
137 const itk::Size<VImageDimension> & regionSize)
139 const itk::ImageRegionRange<itk::Image<TPixel, VImageDimension>> imageRegionRange{
140 image, itk::ImageRegion<VImageDimension>{ regionIndex, regionSize }
142 std::fill(std::begin(imageRegionRange), std::end(imageRegionRange), 1);
147template <
typename TPixel,
unsigned int VImageDimension>
150 const itk::Index<VImageDimension> & regionIndex,
151 const itk::Size<VImageDimension> & regionSize)
153 const itk::ImageRegionRange<itk::Image<TPixel, VImageDimension>> imageRegionRange{
154 image, itk::ImageRegion<VImageDimension>{ regionIndex, regionSize }
156 std::iota(std::begin(imageRegionRange), std::end(imageRegionRange), TPixel{ 1 });
162inline std::vector<double>
165 std::vector<double> vectorOfDouble(strings.size());
167 std::transform(strings.cbegin(), strings.cend(), vectorOfDouble.begin(), [](
const std::string & str) {
169 const auto result = std::stod(str, &index);
172 EXPECT_EQ(index, str.size());
176 return vectorOfDouble;
181template <std::
size_t VDimension>
182itk::Offset<VDimension>
187 itk::Offset<VDimension> result;
190 for (
const double value : doubles)
192 const auto roundedValue = std::round(value);
194 EXPECT_GE(roundedValue, std::numeric_limits<itk::OffsetValueType>::min());
195 EXPECT_LE(roundedValue, std::numeric_limits<itk::OffsetValueType>::max());
197 result[i] =
static_cast<itk::OffsetValueType
>(roundedValue);
205inline std::map<std::string, std::vector<std::string>>
206CreateParameterMap(std::initializer_list<std::pair<std::string, std::vector<std::string>>> initializerList)
208 std::map<std::string, std::vector<std::string>> result;
210 for (
const auto & pair : initializerList)
212 EXPECT_TRUE(result.insert(pair).second);
218inline std::map<std::string, std::vector<std::string>>
221 std::map<std::string, std::vector<std::string>> result;
223 for (
const auto & pair : initializerList)
225 EXPECT_TRUE(result.insert({ pair.first, { pair.second } }).second);
231template <
unsigned VImageDimension>
232std::map<std::string, std::vector<std::string>>
235 std::map<std::string, std::vector<std::string>> result =
CreateParameterMap(initializerList);
237 for (
const auto & key : {
"FixedImageDimension",
"MovingImageDimension" })
239 EXPECT_TRUE(result.insert({ key, { std::to_string(VImageDimension) } }).second);
245inline ParameterObject::Pointer
250 return parameterObject;
258 parameterObject->SetParameterMap(parameterMap);
259 return parameterObject;
263inline std::vector<double>
267 EXPECT_EQ(transformParameterMaps.size(), 1);
269 if (transformParameterMaps.empty())
271 throw Exception(
"Error: GetTransformParametersFromMaps should not return an empty ParameterMap!");
274 const auto & transformParameterMap = transformParameterMaps.front();
275 const auto found = transformParameterMap.find(
"TransformParameters");
277 if (found == transformParameterMap.cend())
279 throw Exception(
"Error: GetTransformParametersFromMaps did not find TransformParameters!");
285template <
typename TFilter>
289 const auto transformParameterObject = filter.GetTransformParameterObject();
290 const auto & transformParameterMaps = itk::Deref(transformParameterObject).GetParameterMaps();
304template <
unsigned int VDimension>
331 ,
index(image.GetLargestPossibleRegion().GetIndex())
332 ,
size(image.GetLargestPossibleRegion().GetSize())
334 ,
origin(image.GetOrigin())
344 ,
index(initialIndex)
352 ToImage(itk::ImageBase<VDimension> & image)
const
384 return !(lhs == rhs);
389template <
typename TRandomNumberEngine>
393 return (randomNumberEngine() % 2 == 0) ? -1 : 1;
397template <
unsigned int VImageDimension>
403 const auto createRandomDirection = [&randomNumberEngine] {
404 using DirectionType =
typename ImageDomainType::DirectionType;
405 auto randomDirection = DirectionType::GetIdentity();
408 const auto randomRotation = std::uniform_real_distribution<>{ -M_PI, M_PI }(randomNumberEngine);
409 const auto cosRandomRotation = std::cos(randomRotation);
410 const auto sinRandomRotation = std::sin(randomRotation);
412 randomDirection[0][0] = cosRandomRotation;
413 randomDirection[0][1] = sinRandomRotation;
414 randomDirection[1][0] = -sinRandomRotation;
415 randomDirection[1][1] = cosRandomRotation;
417 return randomDirection;
419 const auto createRandomIndex = [&randomNumberEngine] {
420 typename ImageDomainType::IndexType randomIndex{};
424 std::generate(randomIndex.begin(), randomIndex.end(), [&randomNumberEngine] {
425 return std::uniform_int_distribution{ std::numeric_limits<int>::min() / 2,
426 std::numeric_limits<int>::max() / 2 }(randomNumberEngine);
430 const auto createRandomSmallImageSize = [&randomNumberEngine] {
431 typename ImageDomainType::SizeType randomImageSize{};
432 std::generate(randomImageSize.begin(), randomImageSize.end(), [&randomNumberEngine] {
433 return std::uniform_int_distribution<itk::SizeValueType>{ minimumImageSizeValue,
434 2 * minimumImageSizeValue }(randomNumberEngine);
436 return randomImageSize;
438 const auto createRandomSpacing = [&randomNumberEngine] {
439 typename ImageDomainType::SpacingType randomSpacing{};
440 std::generate(randomSpacing.begin(), randomSpacing.end(), [&randomNumberEngine] {
443 return std::uniform_real_distribution<itk::SpacePrecisionType>{ 0.1, 10.0 }(randomNumberEngine);
445 return randomSpacing;
447 const auto createRandomPoint = [&randomNumberEngine] {
448 typename ImageDomainType::PointType randomPoint{};
449 std::generate(randomPoint.begin(), randomPoint.end(), [&randomNumberEngine] {
453 return std::uniform_real_distribution<itk::SpacePrecisionType>{
454 std::numeric_limits<int>::min(), std::numeric_limits<int>::max()
455 }(randomNumberEngine);
460 return ImageDomainType{ createRandomDirection(),
462 createRandomSmallImageSize(),
463 createRandomSpacing(),
464 createRandomPoint() };
469template <
typename TPixel,
unsigned VImageDimension>
473 const auto image = itk::Image<TPixel, VImageDimension>::New();
474 image->SetRegions(imageSize);
475 image->AllocateInitialized();
480template <
typename TPixel,
unsigned VImageDimension>
484 const auto image = itk::Image<TPixel, VImageDimension>::New();
486 image->AllocateInitialized();
492template <
typename TPixel,
unsigned VImageDimension>
496 using ImageType = itk::Image<TPixel, VImageDimension>;
497 const auto image = ImageType::New();
500 const itk::ImageBufferRange<ImageType> imageBufferRange{ *image };
501 std::iota(imageBufferRange.begin(), imageBufferRange.end(), TPixel{ 1 });
507template <
typename TPixel,
unsigned VImageDimension>
static std::vector< std::string > ToVectorOfStrings(const TContainer &container)
Simple exception class, to be used by unit tests.
const char * what() const noexcept override
Exception(const char *const message)
itk::SmartPointer< Self > Pointer
std::map< ParameterKeyType, ParameterValueVectorType > ParameterMapType
#define ELX_GTEST_EXPECT_FALSE_AND_THROW_EXCEPTION_IF(condition)
Expect the specified condition to be false, and throw an exception if it is true.
auto CreateImageFilledWithSequenceOfNaturalNumbers(const ImageDomain< VImageDimension > &imageDomain)
decltype(auto) DerefSmartPointer(const TSmartPointer &ptr)
auto CreateRandomImageDomain(std::mt19937 &randomNumberEngine)
std::vector< double > GetTransformParametersFromMaps(const std::vector< ParameterObject::ParameterMapType > &transformParameterMaps)
std::string GetNameOfTest(const testing::Test &)
std::string GetCurrentBinaryDirectoryPath()
std::map< std::string, std::vector< std::string > > CreateParameterMap(std::initializer_list< std::pair< std::string, std::vector< std::string > > > initializerList)
decltype(T().front()) Front(T &container)
std::vector< double > GetTransformParametersFromFilter(TFilter &filter)
constexpr itk::SizeValueType minimumImageSizeValue
itk::SmartPointer< T > CheckNew()
std::string GetDataDirectoryPath()
int GenerateRandomSign(TRandomNumberEngine &randomNumberEngine)
void FillImageRegion(itk::Image< TPixel, VImageDimension > &image, const itk::Index< VImageDimension > ®ionIndex, const itk::Size< VImageDimension > ®ionSize)
Fills the specified image region with pixel values 1.
ParameterObject::Pointer CreateParameterObject(std::initializer_list< std::pair< std::string, std::string > > initializerList)
auto CreateImage(const itk::Size< VImageDimension > &imageSize)
itk::Offset< VDimension > ConvertToOffset(const std::vector< double > &doubles)
void FillImageRegionWithSequenceOfNaturalNumbers(itk::Image< TPixel, VImageDimension > &image, const itk::Index< VImageDimension > ®ionIndex, const itk::Size< VImageDimension > ®ionSize)
Fills the specified image region with pixel values 1, 2, 3, ...
std::vector< double > ConvertStringsToVectorOfDouble(const std::vector< std::string > &strings)
typename ImageBaseType::IndexType IndexType
ParameterObject::ParameterMapType AsParameterMap() const
friend bool operator==(const ImageDomain &lhs, const ImageDomain &rhs)
typename ImageBaseType::SpacingType SpacingType
typename ImageBaseType::DirectionType DirectionType
friend bool operator!=(const ImageDomain &lhs, const ImageDomain &rhs)
typename ImageBaseType::PointType PointType
ImageDomain(const DirectionType &initialDirection, const IndexType &initialIndex, const SizeType &initialSize, const SpacingType &initialSpacing, const PointType &initialOrigin)
ImageDomain(const ImageBaseType &image)
void ToImage(itk::ImageBase< VDimension > &image) const
typename ImageBaseType::SizeType SizeType
ImageDomain(const SizeType &initialSize)
itk::ImageBase< VDimension > ImageBaseType
Eases passing a type as argument to a generic lambda.