17#ifndef __image_registration_metric_local_cross_correlation_h__
18#define __image_registration_metric_local_cross_correlation_h__
29 namespace Registration
33 template <
typename ImageType1,
typename ImageType2>
35 template <
typename MaskType,
typename ImageType3>
39 Eigen::Vector3d pos (mask.index(0), mask.index(1), mask.index(2));
40 out.index(0) = pos[0];
41 out.index(1) = pos[1];
42 out.index(2) = pos[2];
46 Eigen::VectorXd n1 = Eigen::VectorXd(nmax);
47 Eigen::VectorXd n2 = Eigen::VectorXd(nmax);
50 in1.index(0) = pos[0];
51 in1.index(1) = pos[1];
52 in1.index(2) = pos[2];
54 if (value_in1 != value_in1){
59 in2.index(0) = pos[0];
60 in2.index(1) = pos[1];
61 in2.index(2) = pos[2];
63 if (value_in2 != value_in2){
74 mask.index(0) = niter.index(0);
75 mask.index(1) = niter.index(1);
76 mask.index(2) = niter.index(2);
79 in1.index(0) = niter.index(0);
80 in1.index(1) = niter.index(1);
81 in1.index(2) = niter.index(2);
86 in2.index(0) = niter.index(0);
87 in2.index(1) = niter.index(1);
88 in2.index(2) = niter.index(2);
94 n1[cnt] =
in1.value();
95 n2[cnt] =
in2.value();
100 mask.index(0) = out.index(0);
101 mask.index(1) = out.index(1);
102 mask.index(2) = out.index(2);
105 throw Exception (
"FIXME: neighbourhood does not contain centre");
112 out.row(3) = ( Eigen::Matrix<default_type,5,1>() << value_in1 - m1, value_in2 - m2, n1.adjoint() * n2, n1.adjoint() * n1, n2.adjoint() * n2 ).finished();
136 void set_weights (Eigen::Matrix<default_type, Eigen::Dynamic, 1> weights) {
137 assert (
"FIXME: set_weights not implemented");
140 template <
class ParamType>
142 INFO (
"precomputing cross correlation data...");
144 using Im1Type =
decltype(parameters.im1_image);
145 using Im2Type =
decltype(parameters.im2_image);
146 using Im1MaskType =
decltype(parameters.im1_mask);
147 using Im2MaskType =
decltype(parameters.im2_mask);
148 using ProcessedImageValueType =
typename ParamType::ProcessedValueType;
149 using ProcessedMaskType =
typename ParamType::ProcessedMaskType;
150 using ProcessedMaskInterpolatorType =
typename ParamType::ProcessedMaskInterpType;
151 using CCInterpType =
typename ParamType::ProcessedImageInterpType;
153 Header midway_header (parameters.midway_image);
159 auto cc_image_header = Header::scratch (midway_header);
160 cc_image_header.ndim() = 4;
161 cc_image_header.size(3) = 5;
162 ProcessedMaskType cc_mask;
163 auto cc_mask_header = Header::scratch (parameters.midway_image);
169 if (parameters.im1_mask.valid() or parameters.im2_mask.valid())
170 cc_mask = cc_mask_header.template get_image<bool>();
171 if (parameters.im1_mask.valid() and !parameters.im2_mask.valid())
172 Filter::reslice <Interp::Nearest> (parameters.im1_mask, cc_mask, parameters.transformation.get_transform_half());
173 else if (!parameters.im1_mask.valid() and parameters.im2_mask.valid())
174 Filter::reslice <Interp::Nearest> (parameters.im2_mask, cc_mask, parameters.transformation.get_transform_half_inverse(),
Adapter::AutoOverSample);
175 else if (parameters.im1_mask.valid() and parameters.im2_mask.valid()){
181 auto both = [](
decltype(cc_mask)& cc_mask,
decltype(mask_reslicer1)& m1,
decltype(mask_reslicer2)& m2) {
182 cc_mask.value() = ((m1.value() + m2.value()) / 2.0) > 0.5 ? true :
false;
184 ThreadedLoop (cc_mask).run (both, cc_mask, mask_reslicer1, mask_reslicer2);
187 Adapter::Reslice<Interp::Linear, Im1Type> interp1 (parameters.im1_image, midway_header, parameters.transformation.get_transform_half(), NoOversample, std::numeric_limits<typename Im1Type::value_type>::quiet_NaN());
189 Adapter::Reslice<Interp::Linear, Im2Type> interp2 (parameters.im2_image, midway_header, parameters.transformation.get_transform_half_inverse(), NoOversample, std::numeric_limits<typename Im2Type::value_type>::quiet_NaN());
191 const auto extent = parameters.get_extent();
196 if (!cc_mask.valid()){
197 cc_mask = cc_mask_header.template get_image<bool>();
198 ThreadedLoop (cc_mask).run([](
decltype(cc_mask)& m) {m.value()=
true;}, cc_mask);
200 parameters.processed_mask = cc_mask;
201 parameters.processed_mask_interp.reset (
new ProcessedMaskInterpolatorType (parameters.processed_mask));
202 auto loop =
ThreadedLoop (
"precomputing cross correlation data...", parameters.processed_mask);
204 parameters.processed_image = cc_image;
205 parameters.processed_image_interp.reset (
new CCInterpType (parameters.processed_image));
210 template <
class Params>
213 Eigen::Matrix<default_type, Eigen::Dynamic, 1>& gradient) {
216 if (params.processed_mask.valid()) {
217 assign_pos_of(iter, 0, 3).to(params.processed_mask);
218 if (!params.processed_mask.value())
222 assign_pos_of(iter).to(params.processed_image);
223 assert (params.processed_image.index(0) == iter.
index(0));
224 assert (params.processed_image.index(1) == iter.
index(1));
225 assert (params.processed_image.index(2) == iter.
index(2));
227 params.processed_image.index(3) = 2;
229 params.processed_image.index(3) = 3;
231 params.processed_image.index(3) = 4;
234 params.processed_image.index(3) = 0;
236 if (A_BC != A_BC || A_BC == 0.0) {
241 params.processed_image_interp->voxel(pos);
242 typename Params::Im1ValueType val1;
243 typename Params::Im2ValueType val2;
244 Eigen::Matrix<typename Params::Im1ValueType, 1, 3> grad1;
245 Eigen::Matrix<typename Params::Im2ValueType, 1, 3> grad2;
248 params.processed_image_interp->index(3) = 0;
249 params.processed_image_interp->value_and_gradient_wrt_scanner(val1, grad1);
253 WARN (
"FIXME: val1 is nan");
257 params.processed_image_interp->index(3) = 1;
258 params.processed_image_interp->value_and_gradient_wrt_scanner(val2, grad2);
261 WARN (
"FIXME: val2 is nan");
269 Eigen::Vector3d derivWRTImage = - A_BC * ((val2 - A/B * val1) * grad1 - 0.0 * (val1 - A/C * val2) * grad2);
271 const Eigen::Vector3d midway_point = midway_v2s * pos;
272 const auto jacobian_vec = params.transformation.get_jacobian_vector_wrt_params (midway_point);
273 gradient.segment<4>(0) += derivWRTImage(0) * jacobian_vec;
274 gradient.segment<4>(4) += derivWRTImage(1) * jacobian_vec;
275 gradient.segment<4>(8) += derivWRTImage(2) * jacobian_vec;
an Image providing interpolated values from another Image
a dummy image to iterate over, useful for multi-threaded looping.
const ssize_t & index(size_t axis) const
a dummy image to iterate over a certain neighbourhood, useful for multi-threaded looping.
default_type operator()(Params ¶ms, const Iterator &iter, Eigen::Matrix< default_type, Eigen::Dynamic, 1 > &gradient)
void set_weights(Eigen::Matrix< default_type, Eigen::Dynamic, 1 > weights)
default_type precompute(ParamType ¶meters)
const vector< uint32_t > AutoOverSample
MR::default_type value_type
List contiguous_along_axis(size_t axis)
convenience function to get volume-contiguous strides
double default_type
the default type used throughout MRtrix
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.
LCCPrecomputeFunctorMasked_Naive(const vector< size_t > &ext, ImageType1 &adapter1, ImageType2 &adapter2)
MEMALIGN(LCCPrecomputeFunctorMasked_Naive< ImageType1, ImageType2 >) template< typename MaskType
ImageType3 void operator()(MaskType &mask, ImageType3 &out)