6#include "../../catch2/catch.hpp"
31namespace eigenCubeTest
39TEST_CASE(
"Masked eigenCube means use an inclusive good-pixel threshold",
"[improc::eigenCube]" )
42 cube.
image( 0 )( 0, 0 ) = 2.0;
43 cube.
image( 1 )( 0, 0 ) = 4.0;
44 cube.
image( 2 )( 0, 0 ) = 8.0;
45 cube.
image( 3 )( 0, 0 ) = 16.0;
48 mask.
image( 0 )( 0, 0 ) = 1;
49 mask.
image( 1 )( 0, 0 ) = 1;
50 mask.
image( 2 )( 0, 0 ) = 0;
51 mask.
image( 3 )( 0, 0 ) = 0;
55 cube.
mean( result, mask, 0.49 );
56 REQUIRE( result( 0, 0 ) == 3.0 );
58 cube.
mean( result, mask, 0.5 );
59 REQUIRE( result( 0, 0 ) == 3.0 );
61 cube.
mean( result, mask, 0.51 );
64 std::vector<double> weights{ 1.0, 3.0, 5.0, 7.0 };
65 cube.
mean( result, weights, mask, 0.5 );
66 REQUIRE( result( 0, 0 ) == 3.5 );
69 cube.
mean( result, mask, 0.0 );
72 mask.
image( 0 )( 0, 0 ) = 1;
73 cube.
mean( result, mask, 0.0 );
74 REQUIRE( result( 0, 0 ) == 2.0 );
76 for(
int plane = 0; plane < mask.planes(); ++plane )
78 mask.
image( plane )( 0, 0 ) = 1;
81 cube.
mean( result, mask, 1.0 );
82 REQUIRE( result( 0, 0 ) == 7.5 );
89TEST_CASE(
"Unmasked eigenCube combinations use every plane",
"[improc::eigenCube]" )
92 cube.
image( 0 )( 0, 0 ) = 1.0;
93 cube.
image( 1 )( 0, 0 ) = 2.0;
94 cube.
image( 2 )( 0, 0 ) = 3.0;
95 cube.
image( 3 )( 0, 0 ) = 100.0;
98 std::vector<double> weights{ 1.0, 1.0, 1.0, 3.0 };
101 REQUIRE( result( 0, 0 ) == 26.5 );
103 cube.
mean( result, weights );
104 REQUIRE( result( 0, 0 ) == 51.0 );
107 REQUIRE( result( 0, 0 ) == 2.5 );
110 REQUIRE( result( 0, 0 ) == 26.5 );
112 cube.
sigmaMean( result, weights, 100.0 );
113 REQUIRE( result( 0, 0 ) == 51.0 );
123TEST_CASE(
"Float eigenCube combinations clear and combine HCI images",
"[improc::eigenCube]" )
127 for(
int plane = 0; plane < cube.planes(); ++plane )
129 REQUIRE( cube.
image( plane )( 0, 0 ) == 0.0F );
132 cube.
image( 0 )( 0, 0 ) = 1.0F;
133 cube.
image( 1 )( 0, 0 ) = 2.0F;
134 cube.
image( 2 )( 0, 0 ) = 7.0F;
135 cube.
image( 3 )( 0, 0 ) = 10.0F;
139 REQUIRE( result( 0, 0 ) == 5.0F );
142 REQUIRE( result( 0, 0 ) == 4.5F );
145 mask.
image( 0 )( 0, 0 ) = 1;
146 mask.
image( 1 )( 0, 0 ) = 0;
147 mask.
image( 2 )( 0, 0 ) = 1;
148 mask.
image( 3 )( 0, 0 ) = 0;
149 cube.
median( result, mask, 0.5 );
150 REQUIRE( result( 0, 0 ) == 4.0F );
152 auto values = cube.
pixel( 0, 0 );
154 valueMask << 1.0F, 0.0F, 1.0F, 0.0F;
155 std::vector<float> work;
165TEST_CASE(
"Masked eigenCube median honors its mask and threshold",
"[improc::eigenCube]" )
168 cube.
image( 0 )( 0, 0 ) = 1.0;
169 cube.
image( 1 )( 0, 0 ) = 100.0;
170 cube.
image( 2 )( 0, 0 ) = 5.0;
171 cube.
image( 3 )( 0, 0 ) = 9.0;
174 mask.
image( 0 )( 0, 0 ) = 1;
175 mask.
image( 1 )( 0, 0 ) = 0;
176 mask.
image( 2 )( 0, 0 ) = 1;
177 mask.
image( 3 )( 0, 0 ) = 0;
181 cube.
median( result, mask, 0.5 );
182 REQUIRE( result( 0, 0 ) == 3.0 );
184 cube.
median( result, mask, 0.5001 );
188 cube.
median( result, mask, 0.0 );
191 for(
int plane = 0; plane < mask.planes(); ++plane )
193 mask.
image( plane )( 0, 0 ) = 1;
196 cube.
median( result, mask, 1.0 );
197 REQUIRE( result( 0, 0 ) == 7.0 );
205TEST_CASE(
"Masked eigenCube sigma means use an inclusive good-pixel threshold",
"[improc::eigenCube]" )
208 cube.
image( 0 )( 0, 0 ) = 2.0;
209 cube.
image( 1 )( 0, 0 ) = 4.0;
210 cube.
image( 2 )( 0, 0 ) = 8.0;
211 cube.
image( 3 )( 0, 0 ) = 16.0;
214 mask.
image( 0 )( 0, 0 ) = 1;
215 mask.
image( 1 )( 0, 0 ) = 1;
216 mask.
image( 2 )( 0, 0 ) = 0;
217 mask.
image( 3 )( 0, 0 ) = 0;
220 std::vector<double> weights{ 1.0, 3.0, 5.0, 7.0 };
222 cube.
sigmaMean( result, mask, 100.0, 0.5 );
223 REQUIRE( result( 0, 0 ) == 3.0 );
225 cube.
sigmaMean( result, weights, mask, 100.0, 0.5 );
226 REQUIRE( result( 0, 0 ) == 3.5 );
228 cube.
sigmaMean( result, mask, 100.0, 0.51 );
237TEST_CASE(
"eigenCube combinations validate masks weights and thresholds",
"[improc::eigenCube]" )
249 std::vector<double> weights{ 1.0, 1.0, 1.0, 1.0 };
250 std::vector<double> wrongWeights{ 1.0, 1.0, 1.0 };
254 cube.
mean( result, wrongMask );
255 FAIL(
"mismatched mask dimensions were accepted" );
264 cube.
mean( result, wrongWeights );
265 FAIL(
"mismatched weights were accepted" );
274 cube.
sigmaMean( result, wrongWeights, 3.0 );
275 FAIL(
"mismatched sigma-mean weights were accepted" );
284 cube.
mean( result, mask, -0.01 );
285 FAIL(
"a negative threshold was accepted" );
294 cube.
median( result, mask, 1.01 );
295 FAIL(
"a threshold above one was accepted" );
304 cube.
sigmaMean( result, mask, 3.0, std::numeric_limits<double>::quiet_NaN() );
305 FAIL(
"a NaN threshold was accepted" );
314 cube.
sigmaMean( result, weights, mask, 3.0, std::numeric_limits<double>::infinity() );
315 FAIL(
"an infinite threshold was accepted" );
329TEST_CASE(
"eigenCube copy and move operations preserve ownership",
"[improc::eigenCube]" )
332 source.
image( 0 ) << 1.0, 2.0;
333 source.
image( 1 ) << 3.0, 4.0;
336 REQUIRE( copied.data() != source.data() );
337 REQUIRE( copied.
image( 1 )( 0, 1 ) == 4.0 );
338 copied.
image( 0 )( 0, 0 ) = 10.0;
339 REQUIRE( source.
image( 0 )( 0, 0 ) == 1.0 );
343 REQUIRE( assigned.data() != source.data() );
344 REQUIRE( assigned.
image( 1 )( 0, 0 ) == 3.0 );
347 REQUIRE( copied.data() ==
nullptr );
348 REQUIRE( copied.rows() == 0 );
349 REQUIRE( moved.
image( 0 )( 0, 0 ) == 10.0 );
352 moveAssigned = std::move( assigned );
353 REQUIRE( assigned.data() ==
nullptr );
354 REQUIRE( assigned.planes() == 0 );
355 REQUIRE( moveAssigned.
image( 1 )( 0, 1 ) == 4.0 );
358 transferred.shallowCopy( source,
true );
359 REQUIRE( source.data() ==
nullptr );
360 REQUIRE( source.cols() == 0 );
361 REQUIRE( transferred.
image( 0 )( 0, 1 ) == 2.0 );
363 transferred.shallowCopy( transferred,
true );
364 REQUIRE( transferred.
image( 1 )( 0, 0 ) == 3.0 );
374TEST_CASE(
"eigenCube storage views and covariance preserve layout",
"[improc::eigenCube]" )
376 double externalData[4]{ 1.0, 2.0, 3.0, 4.0 };
378 REQUIRE( externalCube.data() == externalData );
379 REQUIRE( externalCube.
image( 1 )( 0, 1 ) == 4.0 );
382 borrowed.shallowCopy( externalCube,
false );
383 REQUIRE( borrowed.data() == externalData );
384 REQUIRE( externalCube.data() == externalData );
386 externalCube = externalCube;
387 externalCube = std::move( externalCube );
388 REQUIRE( externalCube.data() == externalData );
389 REQUIRE( externalCube.rows() == 1 );
390 REQUIRE( externalCube.cols() == 2 );
391 REQUIRE( externalCube.planes() == 2 );
393 REQUIRE( externalCube.
cube().rows() == 2 );
394 REQUIRE( externalCube.
cube().cols() == 2 );
395 REQUIRE( externalCube.
asVectors()( 1, 1 ) == 4.0 );
397 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> covariance;
398 externalCube.
Covar( covariance );
399 REQUIRE( covariance.rows() == 2 );
400 REQUIRE( covariance.cols() == 2 );
401 REQUIRE( covariance( 0, 0 ) == 5.0 );
402 REQUIRE( covariance( 0, 1 ) == 11.0 );
403 REQUIRE( covariance( 1, 0 ) == 11.0 );
404 REQUIRE( covariance( 1, 1 ) == 25.0 );
407 REQUIRE( borrowed.data() ==
nullptr );
408 REQUIRE( externalData[3] == 4.0 );
411 resized.resize( 2, 3 );
412 REQUIRE( resized.rows() == 2 );
413 REQUIRE( resized.cols() == 3 );
414 REQUIRE( resized.planes() == 1 );
Augments an exception with the source file and line.
An image cube with an Eigen-like API.
void Covar(Eigen::Matrix< dataT, Eigen::Dynamic, Eigen::Dynamic > &cv)
Calculate the covariance matrix of the images in the cube.
void sigmaMean(eigenT &mim, Scalar sigma)
Calculate the sigma clipped mean image of the cube.
void clear()
De-allocate and set all sizes to 0.
Eigen::Map< Eigen::Array< dataT, Eigen::Dynamic, Eigen::Dynamic >, Eigen::Unaligned, Eigen::Stride< Eigen::Dynamic, Eigen::Dynamic > > pixel(Index i, Index j)
Returns an Eigen::Eigen::Map-ed vector of the pixels at the given coordinate.
void median(eigenT &mim)
Calculate the median image of the cube.
Eigen::Map< Eigen::Array< dataT, Eigen::Dynamic, Eigen::Dynamic > > cube()
Returns a 2D Eigen::Eigen::Map pointed at the entire cube.
void mean(eigenT &mim)
Calculate the mean image of the cube.
Eigen::Map< Eigen::Array< dataT, Eigen::Dynamic, Eigen::Dynamic > > asVectors()
Return an Eigen::Eigen::Map of the cube where each image is a vector.
Eigen::Map< Eigen::Array< dataT, Eigen::Dynamic, Eigen::Dynamic > > image(Index n)
Returns a 2D Eigen::Eigen::Map pointed at the specified image.
An image cube with an Eigen API.
TEST_CASE("Masked eigenCube means use an inclusive good-pixel threshold", "[improc::eigenCube]")
Verify masked mean combinations accept an inclusive good-pixel fraction and use the cube scalar senti...
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
imageT::Scalar imageMedian(const imageT &mat, const maskT *mask, std::vector< typename imageT::Scalar > *work=0)
Calculate the median of an Eigen-like array.
@ sizeerr
A size was invalid or calculated incorrectly.
@ invalidarg
An argument was invalid.
bool isInvalidPixel(realT value)
Check whether a value represents an invalid image pixel.