17#ifndef __registration_transform_search_h__
18#define __registration_transform_search_h__
21#include <Eigen/Geometry>
50 namespace Registration
52 namespace RotationSearch
56 using MatType = Eigen::Matrix<default_type, 3, 3>;
57 using VecType = Eigen::Matrix<default_type, 3, 1>;
58 using QuatType = Eigen::Quaternion<default_type>;
60 template <
class MetricType = Registration::Metric::MeanSquaredNoGradient>
76 input_trafo (linear_transform),
78 centre (input_trafo.get_centre()),
79 offset (input_trafo.get_translation()),
80 global_search_iterations (
init.init_rotation.search.global.iterations),
81 rot_angles (
init.init_rotation.search.angles),
82 local_search_directions (
init.init_rotation.search.directions),
83 image_scale_factor (
init.init_rotation.search.scale),
84 global_search (
init.init_rotation.search.run_global),
85 translation_extent (
init.init_rotation.search.translation_extent),
88 local_trafo.set_centre_without_transform_update (centre);
89 local_trafo.set_translation (offset);
90 Eigen::Matrix<default_type, 3, 3> lin = input_trafo.get_transform().linear();
91 local_trafo.set_matrix_const_translation(lin);
92 INFO (
"before search:");
93 INFO (local_trafo.info());
113 void write_images (
const std::string& im1_path,
const std::string& im2_path) {
117 Header image1_midway_header (midway_image_header);
119 image1_midway_header.ndim() = im1.
ndim();
120 for (
size_t dim = 3; dim < im1.
ndim(); ++dim){
121 image1_midway_header.spacing(dim) = im1.
spacing(dim);
122 image1_midway_header.size(dim) = im1.
size(dim);
125 Header image2_midway_header (midway_image_header);
127 image2_midway_header.ndim() = im2.
ndim();
128 for (
size_t dim = 3; dim < im2.
ndim(); ++dim){
129 image2_midway_header.spacing(dim) = im2.
spacing(dim);
130 image2_midway_header.size(dim) = im2.
size(dim);
134 Filter::reslice<Interp::Cubic> (im1, image1_midway, local_trafo.get_transform_half(),
Adapter::AutoOverSample, 0.0);
135 Filter::reslice<Interp::Cubic> (im2, image2_midway, local_trafo.get_transform_half_inverse(),
Adapter::AutoOverSample, 0.0);
138 void run (
bool debug =
false ) {
139 std::string what = global_search?
"global" :
"local";
140 size_t iterations = global_search? global_search_iterations : (rot_angles.size() * local_search_directions);
141 ProgressBar progress (
"performing " + what +
" search for best rotation", iterations);
142 overlap_it.resize (iterations);
143 cost_it.resize (iterations);
144 trafo_it.reserve (iterations);
146 if (!global_search) {
147 gen_uniform_rotation_axes (local_search_directions, 180.0);
148 az_el_to_cartesian();
151 size_t iteration (0);
153 Eigen::Matrix<default_type, Eigen::Dynamic, 1> gradient (local_trafo.size());
154 Eigen::VectorXd cost = Eigen::VectorXd::Zero(1,1);
156 const Eigen::Translation<default_type, 3> Tc2 (centre - 0.5 * offset), To (offset);
158 R0.translation().fill(0);
160 Eigen::Vector3d extent(0,0,0);
161 if (translation_extent != 0) {
162 ParamType parameters = get_parameters ();
163 extent << midway_image_header.spacing(0) * translation_extent * (midway_image_header.size(0) - 0.5),
164 midway_image_header.spacing(1) * translation_extent * (midway_image_header.size(1) - 0.5),
165 midway_image_header.spacing(2) * translation_extent * (midway_image_header.size(2) - 0.5);
168 while ( ++iteration < iterations ) {
172 gen_random_quaternion ();
174 gen_local_quaternion ();
176 R0.linear() = quat.normalized().toRotationMatrix();
177 if (translation_extent != 0) {
178 gen_random_quaternion ();
179 R0.translation() = rndn () * (quat * extent);
180 DEBUG(
"translation: " +
str(R0.translation().transpose()));
183 T = Tc2 * To * R0 * Tc2.inverse();
187 ParamType parameters = get_parameters ();
192 ThreadedLoop (parameters.midway_image, 0, 3).run (kernel);
193 DEBUG (
"rotation search: iteration " +
str(iteration) +
" cost: " +
str(cost) +
" cnt: " +
str(cnt));
195 std::cout <<
str(iteration) +
" " +
str(cost) +
" " +
str(cnt) <<
" " << T.matrix().row(0) <<
" " << T.matrix().row(1) <<
" " << T.matrix().row(2) << std::endl;
199 throw Exception (
"zero voxel overlap at initialisation. input matrix wrong?");
200 WARN (
"rotation search: overlap count is zero");
202 overlap_it[iteration] = cnt;
203 cost_it[iteration] = cost(0) /
static_cast<default_type>(cnt);
204 trafo_it.push_back (T);
212 auto max_ = Eigen::MatrixXd::Constant(cost_it.rows(), 1, std::numeric_limits<default_type>::max());
216 cost_it = (overlap_it.array() > mean_overlap).select(cost_it, max_);
218 min_cost = cost_it.minCoeff(&i);
253 midway_resize_filter.set_scale_factor (image_scale_factor);
254 midway_resized_header =
Header (midway_resize_filter);
256 ParamType parameters (local_trafo, im1, im2, midway_resized_header, mask1, mask2);
257 parameters.loop_density = 1.0;
268 assert (u1 < 1.0 && u1 >= 0.0);
269 assert (u2 < 2.0 * Math::pi && u2 >= 0.0);
270 assert (u3 < 2.0 * Math::pi && u3 >= 0.0);
273 quat = Eigen::Quaternion<default_type> (a * std::sin(u2), a * std::cos(u2), b * std::sin(u3), b * std::cos(u3));
282 assert (max_cone_angle_deg > 0.0);
283 assert (max_cone_angle_deg <= 180.0);
285 const default_type golden_ratio ((1.0 + std::sqrt (5.0)) / 2.0);
288 az_el.resize (n_dir,2);
289 Eigen::Matrix<default_type, Eigen::Dynamic, 1> idx (n_dir);
290 for (
size_t i = 0; i < n_dir; ++i)
292 az_el.col(0) = idx * golden_angle;
296 az_el.col(1).array() = - a * idx.array() + 1.0;
297 for (
size_t i = 0; i < n_dir; ++i)
298 az_el(i, 1) = std::acos (az_el(i, 1));
303 xyz.resize (az_el.rows(), 3);
304 Eigen::VectorXd el_sin = az_el.col(1).array().sin();
305 xyz.col(0).array() = el_sin.array() * az_el.col(0).array().cos();
306 xyz.col(1).array() = el_sin.array() * az_el.col(0).array().sin();
307 xyz.col(2).array() = az_el.col(1).array().cos();
311 if (idx_dir == local_search_directions) {
314 assert (idx_angle < rot_angles.size());
316 quat = Eigen::Quaternion<default_type> ( Eigen::AngleAxis<default_type> (rot_angles[idx_angle], xyz.row(idx_dir)) );
321 Header midway_resized_header;
325 const Eigen::Vector3d centre;
326 const Eigen::Vector3d offset;
329 Eigen::Quaternion<default_type> quat;
331 Header midway_image_header;
335 size_t global_search_iterations;
337 size_t local_search_directions;
340 double translation_extent;
341 size_t idx_angle, idx_dir;
343 Eigen::Matrix<default_type, Eigen::Dynamic, 2> az_el;
344 Eigen::Matrix<default_type, Eigen::Dynamic, 3> xyz;
345 Eigen::Matrix<default_type, Eigen::Dynamic, 1> overlap_it, cost_it;
static constexpr uint8_t Float64
static Image create(const std::string &image_name, const Header &template_header, bool add_to_command_history=true)
default_type spacing(size_t axis) const
ssize_t size(size_t axis) const
This class provides access to the voxel intensities of an Image, using nearest-neighbour interpolatio...
implements a progress meter to provide feedback to the user
const vector< uint32_t > AutoOverSample
void init(int argc, const char *const *argv)
initialise MRtrix and parse command-line arguments
Eigen::Quaternion< default_type > QuatType
Eigen::Matrix< default_type, 3, 1 > VecType
Eigen::Matrix< default_type, 3, 3 > MatType
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))