17#ifndef __registration_nonlinear_h__
18#define __registration_nonlinear_h__
41 namespace Registration
72 template <
class TransformType,
class Im1ImageType,
class Im2ImageType,
class Im1MaskType,
class Im2MaskType>
73 void run (TransformType linear_transform,
74 Im1ImageType& im1_image,
75 Im2ImageType& im2_image,
76 Im1MaskType& im1_mask,
77 Im2MaskType& im2_mask) {
83 INFO (
"Estimating halfway space");
96 throw Exception (
"the max number of non-linear iterations needs to be defined for each multi-resolution level (scale_factor)");
99 throw Exception (
"the lmax needs to be defined for each multi-resolution level (scale factor)");
103 for (
size_t level = 0; level <
scale_factor.size(); level++) {
108 CONSOLE (
"scale factor (init warp resolution)");
118 DEBUG (
"Resizing midway image based on multi-resolution level");
122 resize_filter.set_interp_type (1);
125 Header midway_image_header_resized = resize_filter;
126 midway_image_header_resized.ndim() = 3;
129 + midway_image_header_resized.spacing(1)
130 + midway_image_header_resized.spacing(2)) / 3.0);
132 + midway_image_header_resized.spacing(1)
133 + midway_image_header_resized.spacing(2)) / 3.0);
155 DEBUG (
"Initialising scratch images");
156 Header warped_header (midway_image_header_resized);
157 if (im1_image.ndim() == 4) {
158 warped_header.ndim() = 4;
159 warped_header.size(3) = im1_smoothed.size(3);
166 DEBUG (
"Initialising CC images");
174 Header field_header (midway_image_header_resized);
175 field_header.ndim() = 4;
176 field_header.size(3) = 3;
192 DEBUG (
"Upsampling fields");
203 ssize_t iteration = 1;
204 default_type grad_step_altered =
gradient_step * (field_header.spacing(0) + field_header.spacing(1) + field_header.spacing(2)) / 3.0;
205 default_type cost = std::numeric_limits<default_type>::max();
206 bool converged =
false;
210 DEBUG (
"smoothing update fields");
212 smooth_filter.set_stdev (update_smoothing_mm);
221 DEBUG (
"updating displacement field");
225 DEBUG (
"smoothing displacement field");
227 smooth_filter.set_stdev (disp_smoothing_mm);
228 smooth_filter.set_zero_boundary (
true);
239 DEBUG (
"warping input images");
242 Filter::warp<Interp::Linear> (im1_smoothed, im1_warped, im1_deform_field, 0.0);
243 Filter::warp<Interp::Linear> (im2_smoothed, im2_warped, im2_deform_field, 0.0);
247 DEBUG (
"Reorienting FODs");
252 DEBUG (
"warping mask images");
253 Im1MaskType im1_mask_warped;
254 if (im1_mask.valid()) {
255 im1_mask_warped = Im1MaskType::scratch (midway_image_header_resized);
257 Filter::warp<Interp::Linear> (im1_mask, im1_mask_warped, im1_deform_field, 0.0);
259 Im1MaskType im2_mask_warped;
260 if (im2_mask.valid()) {
261 im2_mask_warped = Im1MaskType::scratch (midway_image_header_resized);
263 Filter::warp<Interp::Linear> (im2_mask, im2_mask_warped, im2_deform_field, 0.0);
266 DEBUG (
"evaluating metric and computing update field");
268 size_t voxel_count = 0;
279 if (im1_image.ndim() == 4) {
280 assert (!
use_cc &&
"TODO");
282 cost_new, voxel_count, im1_warped, im2_warped, im1_mask_warped, im2_mask_warped, &
stage_contrasts);
287 cost_new, voxel_count, im_cc1, im_cc2, im1_mask_warped, im2_mask_warped);
291 cost_new, voxel_count, im1_warped, im2_warped, im1_mask_warped, im2_mask_warped);
297 display<Image<default_type>>(*im1_update_new);
302 if (cost_new < cost) {
311 DEBUG (
"inverting displacement field");
321 INFO (
" converged. cost: " +
str(cost) +
" voxel count: " +
str(voxel_count));
325 INFO (
" iteration: " +
str(iteration) +
" cost: " +
str(cost));
332 std::ostringstream oss;
336 Header hc (warped_header);
339 INFO(
"writing debug image: "+oss.str());
341 for (
auto i =
Loop(
check, 0, 3) (
check, im1_warped, im2_warped ); i; ++i) {
342 check.value() = im1_warped.value();
344 check.value() = im2_warped.value();
357 WARN (
"final warp computed is not diffeomorphic (negative jacobian determinants detected). Try increasing -nl_disp_smooth or -nl_update_smooth regularisation.");
360 template <
class InputWarpType>
361 void initialise (InputWarpType& input_warps) {
362 assert (input_warps.ndim() == 5);
364 DEBUG (
"reading linear transform from init warp field header");
368 DEBUG (
"loading initial warp fields");
371 Header field_header (input_warps);
372 field_header.ndim() = 4;
373 field_header.size(3) = 3;
376 input_warps.index(4) = 0;
381 input_warps.index(4) = 1;
386 input_warps.index(4) = 2;
391 input_warps.index(4) = 3;
403 for (
size_t level = 0; level < scalefactor.size(); ++level) {
404 if (scalefactor[level] <= 0 || scalefactor[level] > 1)
405 throw Exception (
"the non-linear registration scale factor for each multi-resolution level must be between 0 and 1");
418 void set_aPSF_directions (
const Eigen::MatrixXd& dir) {
423 void set_update_smoothing (
const default_type voxel_fwhm) {
427 void set_disp_smoothing (
const default_type voxel_fwhm) {
432 for (
size_t i = 0; i < lmax.size (); ++i)
434 throw Exception (
"the input nonlinear lmax must be even");
443 ssize_t get_lmax () {
447 std::shared_ptr<Image<default_type> > get_im1_to_mid() {
451 std::shared_ptr<Image<default_type> > get_im2_to_mid() {
455 std::shared_ptr<Image<default_type> > get_mid_to_im1() {
459 std::shared_ptr<Image<default_type> > get_mid_to_im2() {
471 Header get_output_warps_header ()
const {
473 output_header.ndim() = 5;
474 output_header.size(3) = 3;
475 output_header.size(4) = 4;
476 output_header.stride(0) = 1;
477 output_header.stride(1) = 2;
478 output_header.stride(2) = 3;
479 output_header.stride(3) = 4;
480 output_header.stride(4) = 5;
481 return output_header;
484 void write_linear_to_header (
Header& output_header)
const {
489 void write_params_to_header (
Header& output_header)
const {
491 output_header.keyval()[
"nl_niter"] =
str(
max_iter);
500 template <
class OutputType>
501 void get_output_warps (OutputType& output_warps) {
502 assert (output_warps.ndim() == 5);
503 output_warps.index(4) = 0;
505 output_warps.index(4) = 1;
507 output_warps.index(4) = 2;
509 output_warps.index(4) = 3;
513 Header get_midway_header () {
517 void metric_cc (
int radius) {
519 throw Exception (
"CC radius needs to be larger than 1");
521 INFO(
"Cross correlation radius: " +
str(radius));
525 void set_diagnostics_image (
const std::basic_string<char>& path) {
534 Filter::reslice<Interp::Linear> (image, *temp);
540 for (
auto i =
Loop (0,3) (jacobian); i; ++i) {
541 if (jacobian.value().determinant() < 0.0)
static constexpr uint8_t Float64
static Image scratch(const Header &template_header, const std::string &label="scratch image")
static Image create(const std::string &image_name, const Header &template_header, bool add_to_command_history=true)
std::basic_string< char > diagnostics_image_prefix
std::shared_ptr< Image< default_type > > im1_update
vector< MultiContrastSetting > stage_contrasts
std::shared_ptr< Image< default_type > > mid_to_im2
Eigen::MatrixXd aPSF_directions
std::shared_ptr< Image< default_type > > im1_update_new
std::shared_ptr< Image< default_type > > im2_update_new
vector< size_t > cc_extent
std::shared_ptr< Image< default_type > > im1_to_mid_new
std::shared_ptr< Image< default_type > > im2_to_mid
vector< uint32_t > fod_lmax
transform_type im1_to_mid_linear
bool has_negative_jacobians(Image< default_type > &field)
Header midway_image_header
vector< MultiContrastSetting > contrasts
vector< default_type > scale_factor
default_type update_smoothing
default_type gradient_step
std::shared_ptr< Image< default_type > > reslice(Image< default_type > &image, Header &header)
vector< uint32_t > max_iter
default_type disp_smoothing
std::shared_ptr< Image< default_type > > mid_to_im1
std::shared_ptr< Image< default_type > > im2_to_mid_new
transform_type im2_to_mid_linear
std::shared_ptr< Image< default_type > > im1_to_mid
std::shared_ptr< Image< default_type > > im2_update
FORCE_INLINE void invert_displacement(Image< default_type > &disp_field, Image< default_type > &inv_disp_field, size_t max_iter=50, default_type error_tolerance=0.0001)
FORCE_INLINE LoopAlongAxes Loop()
std::enable_if< std::is_fundamental< ValueType >::value &&sizeof(ValueType)==1, ValueType >::type swap(ValueType v)
bool check(int VERSION, Header &H, const size_t num_axes, const vector< std::string > &suffixes)
void cc_precompute(Im1ImageType &im1_image, Im2ImageType &im2_image, Im1MaskType &im1_mask, Im2MaskType &im2_mask, DerivedImageType &A, DerivedImageType &B, DerivedImageType &C, DerivedImageType &im1_meansubtr, DerivedImageType &im2_meansubtr, const vector< size_t > &extent)
FORCE_INLINE void update_displacement_scaling_and_squaring(Image< default_type > &input, Image< default_type > &update, Image< default_type > &output, const default_type step=1.0)
FORCE_INLINE void compose_linear_displacement(const transform_type &transform, DisplacementFieldType &disp_in, DeformationFieldType &deform_out)
transform_type parse_linear_transform(InputWarpType &input_warps, std::string name)
void deformation2displacement(ImageType &input, ImageType &output)
void displacement2deformation(ImageType &input, ImageType &output)
FORCE_INLINE ImageType multi_resolution_lmax(ImageType &input, const default_type scale_factor, const bool do_reorientation=false, const uint32_t lmax=0)
const App::OptionGroup nonlinear_options
double default_type
the default type used throughout MRtrix
std::string str(const T &value, int precision=0)
Eigen::Transform< default_type, 3, Eigen::AffineCompact > transform_type
the type for the affine transform of an image:
ThreadedLoopRunOuter< decltype(Loop(vector< size_t >()))> ThreadedLoop(const HeaderType &source, const vector< size_t > &outer_axes, const vector< size_t > &inner_axes)
Multi-threaded loop object.
Header compute_minimum_average_header(const vector< Header > &input_headers, const vector< Eigen::Transform< default_type, 3, Eigen::Projective > > &transform_header_with, int voxel_subsampling=1, Eigen::Matrix< default_type, 4, 1 > padding=Eigen::Matrix< default_type, 4, 1 >(1.0, 1.0, 1.0, 1.0))
void threaded_copy(InputImageType &source, OutputImageType &destination, const vector< size_t > &axes, size_t num_axes_in_thread=1)