|
FORCE_INLINE LoopAlongAxes | MR::Loop () |
|
FORCE_INLINE LoopAlongAxesProgress | MR::Loop (const std::string &progress_message) |
|
FORCE_INLINE LoopAlongSingleAxis | MR::Loop (size_t axis) |
|
FORCE_INLINE LoopAlongSingleAxisProgress | MR::Loop (const std::string &progress_message, size_t axis) |
|
FORCE_INLINE LoopAlongAxisRange | MR::Loop (size_t axis_from, size_t axis_to) |
|
FORCE_INLINE LoopAlongAxisRangeProgress | MR::Loop (const std::string &progress_message, size_t axis_from, size_t axis_to) |
|
FORCE_INLINE LoopAlongStaticAxes | MR::Loop (std::initializer_list< size_t > axes) |
|
FORCE_INLINE LoopAlongStaticAxesProgress | MR::Loop (const std::string &progress_message, std::initializer_list< size_t > axes) |
|
FORCE_INLINE LoopAlongDynamicAxes | MR::Loop (const vector< size_t > &axes) |
|
FORCE_INLINE LoopAlongDynamicAxesProgress | MR::Loop (const std::string &progress_message, const vector< size_t > &axes) |
|
template<class ImageType > |
FORCE_INLINE LoopAlongDynamicAxes | MR::Loop (const ImageType &source, size_t axis_from=0, size_t axis_to=std::numeric_limits< size_t >::max(), typename std::enable_if< std::is_class< ImageType >::value &&!std::is_same< ImageType, std::string >::value, int >::type=0) |
|
template<class ImageType > |
FORCE_INLINE LoopAlongDynamicAxesProgress | MR::Loop (const std::string &progress_message, const ImageType &source, size_t axis_from=0, size_t axis_to=std::numeric_limits< size_t >::max(), typename std::enable_if< std::is_class< ImageType >::value &&!std::is_same< ImageType, std::string >::value, int >::type=0) |
|
These functions can be used to loop over any number of axes of one of more ImageType
, in any specified order, within the same thread of execution (for multi-threaded applications, see ThreadedLoop()).
Looping over a single axis
To loop over a single axis, use the following syntax:
for (auto l = loop (image); l; ++l) {
image.value() = ...
}
FORCE_INLINE LoopAlongAxes Loop()
To clarify the process:
- the Loop() method returns an opaque structure, in this case destined to loop over a single axis, as specified by
axis
(the C++11 auto
keyword is very useful here to hide the internals of the framework).
- the
operator()
method of the returned object accepts any number of ImageType
objects, each of which will have its position incremented as expected at each iteration.
- this returns another opaque object that will perfom the looping proper (again, the C++11
auto
keyword is invaluable here). This is assigned to a local variable, which can be used to test for completion of the loop (via its operator bool()
method), and to increment the position of the ImageType
objects involved (with its operator++()
methods).
Looping with smallest stride first
The looping strategy most likely to make most efficient use of the memory infrastructure is one where the innermost loop iterates over the axis with the smallest absolute stride, since voxels along this axis are most likely to be adjacent. This is most likely to optimise both throughput to and from system RAM or disk (which are typically optimised for bursts of contiguous sections of memory), and CPU cache usage.
The LoopInOrder class is designed to facilitate this. In the following example, the ImageType of interest is passed as an argument to the constructor, so that its strides can be used to compute the nesting order for the loops over the corresponding axes. Here, we assume that vox is a 3D ImageType (i.e. vox.ndim() == 3) with strides [ 2 -1 3 ]:
float sum = 0.0;
for (
auto i = Image::LoopInOrder().
run (vox); i; ++i)
sum += vox.value();
This is equivalent to:
float sum = 0.0;
for (vox.index(2) = 0; vox.index(2) < vox.size(2); ++vox.index(2))
for (vox.index(0) = 0; vox.index(0) < vox.size(0); ++vox.index(0))
for (vox.index(1) = 0; vox.index(1) < vox.size(1); ++vox.index(1))
sum += vox.value();
Looping over a specific range of axes
It is also possible to explicitly specify the range of axes to be looped over. In the following example, the program will loop over each 3D volume in the ImageType in turn using the Loop class, and use the LoopInOrder class to iterate over the axes of each volume to ensure efficient memory bandwidth use when each volume is being processed.
LoopInOrder inner (vox, 0, 3);
for (
auto i =
Loop(3).
run (vox); i; ++i) {
float sum = 0.0;
for (auto j = inner.run (vox); j; ++j) {
sum += vox.value();
}
void(* print)(const std::string &msg)
print primary output to stdout as-is.
std::string str(const T &value, int precision=0)
Arbitrary order loop
It is also possible to specify the looping order explictly, as in the following example:
LoopInOrder loop (vox,
order);
for (auto i = loop.run (vox); i; ++i)
value += std::exp (-vox.value());
VectorType::Scalar value(const VectorType &coefs, typename VectorType::Scalar cos_elevation, typename VectorType::Scalar cos_azimuth, typename VectorType::Scalar sin_azimuth, int lmax)
vector< size_t > order(const HeaderType &header, size_t from_axis=0, size_t to_axis=std::numeric_limits< size_t >::max())
sort range of axes with respect to their absolute stride.
This will iterate over the axes in the same order as the first example above, irrespective of the strides of the ImageType.
Looping over multiple ImageType objects:
As with the Loop class, it is possible to loop over more than one ImageType of the same dimensions, by passing any additional ImageType objects to be looped over to the run() member function. For example, this code snippet will copy the contents of the ImageType src into a ImageType dest (assumed to have the same dimensions as src), with the looping order optimised for the src ImageType:
LoopInOrder loop (src);
for (auto i = loop.run(src, dest); i; ++i)
dest.value() = src.value();
Displaying progress status
As in the Loop class, the LoopInOrder object can also display its progress as it proceeds, using the appropriate constructor. In the following example, the program will display its progress as it averages an ImageType:
float sum = 0.0;
LoopInOrder loop (vox, "averaging");
for (auto i = loop.run (vox); i; ++i)
sum += vox.value();
float average = sum / float (Image::voxel_count (vox));
print (
"average = " +
str (average) +
"\n");
The output would look something like this:
myprogram: [100%] averaging
average = 23.42
◆ Loop() [1/12]
◆ Loop() [2/12]
template<class ImageType >
FORCE_INLINE LoopAlongDynamicAxes MR::Loop |
( |
const ImageType & |
source, |
|
|
size_t |
axis_from = 0 , |
|
|
size_t |
axis_to = std::numeric_limits<size_t>::max() , |
|
|
typename std::enable_if< std::is_class< ImageType >::value &&!std::is_same< ImageType, std::string >::value, int >::type |
= 0 |
|
) |
| |
◆ Loop() [3/12]
◆ Loop() [4/12]
template<class ImageType >
FORCE_INLINE LoopAlongDynamicAxesProgress MR::Loop |
( |
const std::string & |
progress_message, |
|
|
const ImageType & |
source, |
|
|
size_t |
axis_from = 0 , |
|
|
size_t |
axis_to = std::numeric_limits<size_t>::max() , |
|
|
typename std::enable_if< std::is_class< ImageType >::value &&!std::is_same< ImageType, std::string >::value, int >::type |
= 0 |
|
) |
| |
◆ Loop() [5/12]
◆ Loop() [6/12]
◆ Loop() [7/12]
◆ Loop() [8/12]
◆ Loop() [9/12]
◆ Loop() [10/12]
◆ Loop() [11/12]
◆ Loop() [12/12]