35 #ifndef MOSAIC_LAYOUT_COMMON_HXX_
36 #define MOSAIC_LAYOUT_COMMON_HXX_
39 #include <Core/ITKCommon/common.hxx>
40 #include <Core/ITKCommon/FFT/fft_common.hxx>
41 #include <Core/ITKCommon/grid_common.hxx>
42 #include <Core/ITKCommon/Transform/itkLegendrePolynomialTransform.h>
43 #include <Core/ITKCommon/Optimizers/itkRegularStepGradientDescentOptimizer2.h>
44 #include <Core/ITKCommon/Optimizers/itkImageMosaicVarianceMetric.h>
45 #include <Core/ITKCommon/the_utils.hxx>
48 #include <itkCommand.h>
49 #include <itkImageMaskSpatialObject.h>
50 #include <itkImageRegistrationMethod.h>
51 #include <itkMeanSquaresImageToImageMetric.h>
54 #include <boost/filesystem.hpp>
56 namespace bfs=boost::filesystem;
77 reset(
const unsigned int num_images,
78 const unsigned int max_cascade_len,
79 array3d(translate_transform_t::Pointer) & path,
80 array3d(
double) & cost);
86 template <
typename TImage>
87 affine_transform_t::Pointer
88 setup_transform(
const TImage * image,
89 const base_transform_t * t0,
90 const base_transform_t * t1 = NULL,
91 const unsigned int samples = 16)
94 #define inv_transform \
95 inverse_transform<TImage, std::vector<base_transform_t::Pointer> >
97 #define fwd_transform \
98 forward_transform<TImage, std::vector<const base_transform_t *> >
100 typedef typename TImage::IndexType index_t;
105 typename TImage::PointType origin;
106 image->TransformIndexToPhysicalPoint(i00, origin);
112 typename TImage::PointType spacing;
113 image->TransformIndexToPhysicalPoint(i11, spacing);
114 spacing[0] -= origin[0];
115 spacing[1] -= origin[1];
117 typename TImage::SizeType sz = image->GetLargestPossibleRegion().GetSize();
119 typename TImage::PointType tile_min = origin;
120 typename TImage::PointType tile_max;
121 tile_max[0] = tile_min[0] + spacing[0] *
static_cast<double>(sz[0]);
122 tile_max[1] = tile_min[1] + spacing[1] *
static_cast<double>(sz[1]);
124 double w = sz[0] * spacing[0];
125 double h = sz[1] * spacing[1];
126 double Umax = w / 2.0;
127 double Vmax = h / 2.0;
129 affine_transform_t::Pointer affine = affine_transform_t::New();
130 affine->setup(tile_min[0],
137 std::vector<const base_transform_t *> fwd_cascade(2);
138 std::vector<base_transform_t::Pointer> inv_cascade(2);
142 inv_cascade[0] = t0->GetInverseTransform();
143 inv_cascade[1] = (t1 == NULL) ? NULL : t1->GetInverseTransform();
145 const unsigned int cascade_len = (t1 == NULL) ? 1 : 2;
148 pnt2d_t center = pnt2d(tile_min[0] + (tile_max[0] - tile_min[0]) / 2.0,
149 tile_min[1] + (tile_max[1] - tile_min[1]) / 2.0);
150 pnt2d_t transformed_center = inv_transform(inv_cascade, cascade_len, center);
151 vec2d_t shift = center - transformed_center;
152 affine->setup_translation(shift[0], shift[1]);
155 typename TImage::PointType mosaic_min;
156 typename TImage::PointType mosaic_max;
157 mosaic_min[0] = std::numeric_limits<double>::max();
158 mosaic_min[1] = mosaic_min[0];
159 mosaic_max[0] = -mosaic_min[0];
160 mosaic_max[1] = -mosaic_min[0];
166 typename TImage::PointType tile_point;
167 #define UPDATE_BBOX( u, v ) \
168 tile_point[0] = tile_min[0] + u * (tile_max[0] - tile_min[0]); \
169 tile_point[1] = tile_min[1] + v * (tile_max[1] - tile_min[1]); \
170 update_bbox(mosaic_min, mosaic_max, \
171 inverse_transform<TImage, \
172 std::vector<base_transform_t::Pointer> > \
173 (inv_cascade, cascade_len, tile_point))
176 UPDATE_BBOX(0.0, 0.0);
177 UPDATE_BBOX(1.0, 0.0);
178 UPDATE_BBOX(0.0, 1.0);
179 UPDATE_BBOX(1.0, 1.0);
182 for (
unsigned int x = 0; x < samples; x++)
184 const double t =
static_cast<double>(x + 1) / static_cast<double>(samples + 1);
194 const double W = mosaic_max[0] - mosaic_min[0];
195 const double H = mosaic_max[1] - mosaic_min[1];
197 std::vector<pnt2d_t> uv(samples * samples);
198 std::vector<pnt2d_t> xy(samples * samples);
201 for (
unsigned int i = 0; i < samples; i++)
203 for (
unsigned int j = 0; j < samples; j++)
205 const unsigned int k = i * samples + j;
208 mosaic_min[0] + W * (
static_cast<double>(i) + drand()) / static_cast<double>(samples);
210 mosaic_min[1] + H * (
static_cast<double>(j) + drand()) / static_cast<double>(samples);
212 xy[k] = fwd_transform(fwd_cascade, cascade_len, uv[k]);
219 affine->solve_for_parameters(0, affine_transform_t::Degree + 1, uv, xy);
227 assemble_cascades(
const unsigned int num_images,
228 const unsigned int max_cascade_len,
229 array3d(translate_transform_t::Pointer) & path,
230 array3d(
double) & cost,
231 const bool & cumulative_cost =
false);
237 establish_mappings(
const unsigned int num_images,
238 const unsigned int max_cascade_len,
239 const array3d(translate_transform_t::Pointer) & path,
240 const array3d(
double) & cost,
241 array2d(translate_transform_t::Pointer) & mapping,
242 array2d(
double) & mapping_cost);
249 template <
typename TImage,
typename TMask>
251 refine_one_pair(
const TImage * i0,
255 translate_transform_t::Pointer & t01,
257 const unsigned int iterations,
258 const double & min_step,
259 const double & max_step,
261 const std::string & fn_prefix)
264 typedef itk::ImageRegistrationMethod<TImage, TImage> registration_t;
265 typename registration_t::Pointer registration = registration_t::New();
268 typedef itk::LinearInterpolateImageFunction<TImage, double> interpolator_t;
269 registration->SetInterpolator(interpolator_t::New());
271 if (! fn_prefix.empty())
273 save_rgb<TImage>(fn_prefix +
"a.tif", i0, i1, t01, m0, m1);
277 optimizer_t::Pointer optimizer = optimizer_t::New();
278 registration->SetOptimizer(optimizer);
279 optimizer_observer_t<optimizer_t>::Pointer observer =
281 optimizer->AddObserver(itk::IterationEvent(), observer);
282 optimizer->SetMinimize(
true);
283 optimizer->SetNumberOfIterations(iterations);
284 optimizer->SetMinimumStepLength(min_step);
285 optimizer->SetMaximumStepLength(max_step);
286 optimizer->SetGradientMagnitudeTolerance(1e-6);
287 optimizer->SetRelaxationFactor(5e-1);
288 optimizer->SetPickUpPaceSteps(5);
289 optimizer->SetBackTracking(
true);
292 typedef optimizer_t::ScalesType optimizer_scales_t;
293 optimizer_scales_t translation_scales(t01->GetNumberOfParameters());
294 translation_scales.Fill(1.0);
296 try { optimizer->SetScales(translation_scales); }
297 catch (itk::ExceptionObject & exception) {}
300 typedef itk::MeanSquaresImageToImageMetric<TImage, TImage> metric_t;
302 typename metric_t::Pointer metric = metric_t::New();
303 registration->SetMetric(metric);
305 registration->SetTransform(t01);
306 registration->SetInitialTransformParameters(t01->GetParameters());
309 typedef itk::ImageMaskSpatialObject<2> mask_so_t;
310 typename TMask::ConstPointer fi_mask = m0;
313 mask_so_t::Pointer fi_mask_so = mask_so_t::New();
314 fi_mask_so->SetImage(fi_mask);
315 metric->SetFixedImageMask(fi_mask_so);
318 typename TMask::ConstPointer mi_mask = m1;
321 mask_so_t::Pointer mi_mask_so = mask_so_t::New();
322 mi_mask_so->SetImage(mi_mask);
323 metric->SetMovingImageMask(mi_mask_so);
327 typename TImage::ConstPointer fi = i0;
328 typename TImage::ConstPointer mi = i1;
330 registration->SetFixedImageRegion(fi->GetLargestPossibleRegion());
331 registration->SetFixedImage(fi);
332 registration->SetMovingImage(mi);
335 double metric_before =
336 eval_metric<metric_t, interpolator_t>(t01, fi, mi, fi_mask, mi_mask);
337 assert(metric_before != std::numeric_limits<double>::max());
339 translate_transform_t::ParametersType params_before = t01->GetParameters();
341 std::ostringstream oss;
345 registration->StartRegistration();
347 catch (itk::ExceptionObject & exception)
349 oss << std::endl <<
"image registration threw an exception: "
350 << std::endl << exception.what() << std::endl;
352 t01->SetParameters(optimizer->GetBestParams());
355 double metric_after =
356 eval_metric<metric_t, interpolator_t>(t01, fi, mi, fi_mask, mi_mask);
358 translate_transform_t::ParametersType params_after =
359 t01->GetParameters();
361 oss << std::endl <<
"BEFORE: " << metric_before << std::endl
362 <<
"AFTER: " << metric_after << std::endl;
364 if (metric_before <= metric_after || metric_after != metric_after)
366 oss <<
"NOTE: minimization failed, ignoring registration results..."
368 t01->SetParameters(params_before);
369 metric_after = metric_before;
372 if (! fn_prefix.empty())
374 save_rgb<TImage>(fn_prefix +
"b.tif", i0, i1, t01, m0, m1);
377 CORE_LOG_MESSAGE(oss.str());
385 template <
class TImage,
class TMask>
387 refine_one_pair(
const array2d(
typename TImage::Pointer) & tile_pyramid,
388 const array2d(
typename TMask::ConstPointer) & mask_pyramid,
389 const unsigned int & ia,
390 const unsigned int & ib,
391 translate_transform_t::Pointer & t01,
393 const unsigned int iterations,
394 const double & min_step,
395 const double & max_step,
397 const bfs::path & prefix)
399 const unsigned int num_levels = tile_pyramid.size();
401 double metric = std::numeric_limits<double>::max();
402 for (
unsigned int i = 0; i < num_levels; i++)
404 std::ostringstream fn_prefix;
405 if (! prefix.empty())
407 fn_prefix << prefix <<
"level-" << the_text_t::number(i) <<
"-";
410 metric = refine_one_pair<TImage, TMask>(tile_pyramid[i][ia],
428 template <
class TImage,
class TMask>
430 refine_pairs(
const std::vector<typename TImage::Pointer> & image,
431 const std::vector<typename TMask::ConstPointer> & mask,
432 const double & overlap_min,
433 const double & overlap_max,
434 const unsigned int & iterations,
435 const double & min_step,
436 const double & max_step,
438 const array2d(translate_transform_t::Pointer) & mapping,
439 array2d(translate_transform_t::Pointer) & path,
440 array2d(
double) & cost,
442 const bfs::path & prefix)
444 static unsigned int pass = 0;
446 std::ostringstream oss;
447 const unsigned int num_images = image.size();
448 for (
unsigned int i = 0; i < num_images - 1; i++)
450 for (
unsigned int j = i + 1; j < num_images; j++)
453 double overlap = overlap_ratio<TImage>(image[i], image[j], mapping[i][j]);
454 if (overlap < overlap_min || overlap > overlap_max)
continue;
456 translate_transform_t::Pointer t_ij = translate_transform_t::New();
457 t_ij->SetParameters(mapping[i][j]->GetParameters());
459 oss <<
"refining the mapping: " << std::setw(2) << j <<
" -> "
460 << std::setw(2) << i << std::endl
461 <<
"overlap: " <<
static_cast<int>(overlap * 100.0) <<
" percent" << std::endl;
463 std::ostringstream fn_prefix;
464 if (! prefix.empty())
466 fn_prefix << prefix <<
"pair-" << the_text_t::number(i, 2,
'0') <<
"-" <<
467 the_text_t::number(j, 2,
'0') <<
"-pass-" << the_text_t::number(pass, 2,
'0') <<
"-";
470 cost[i][j] = refine_one_pair<TImage, TMask>(image[i],
480 overlap = overlap_ratio<TImage>(image[i], image[j], t_ij);
482 if (overlap < overlap_min || overlap > overlap_max)
485 cost[i][j] = std::numeric_limits<double>::max();
495 path[j][i] = inverse(t_ij);
496 cost[j][i] = cost[i][j];
503 for (
unsigned int i = 0; i < num_images; i++) oss << std::setw(9) << i;
506 for (
unsigned int i = 0; i < num_images; i++) oss <<
"---------";
508 for (
unsigned int i = 0; i < num_images; i++)
511 for (
unsigned int j = 0; j < num_images; j++)
513 if (path[i][j].GetPointer() != NULL)
515 oss <<
' ' << cost[i][j];
526 CORE_LOG_MESSAGE(oss.str());
532 template <
class TImage,
class TMask>
534 refine_pairs(
const array2d(
typename TImage::Pointer) & tile_pyramid,
535 const array2d(
typename TMask::ConstPointer) & mask_pyramid,
536 const double & overlap_min,
537 const double & overlap_max,
538 const unsigned int & iterations,
539 const double & min_step,
540 const double & max_step,
542 const array2d(translate_transform_t::Pointer) & mapping,
543 array2d(translate_transform_t::Pointer) & path,
544 array2d(
double) & cost,
546 const bfs::path & prefix)
548 static unsigned int pass = 0;
551 unsigned int pyramid_levels = tile_pyramid.size();
552 unsigned int high_res_level = pyramid_levels - 1;
554 const unsigned int num_images = tile_pyramid[high_res_level].size();
555 std::ostringstream oss;
556 for (
unsigned int i = 0; i < num_images - 1; i++)
558 for (
unsigned int j = i + 1; j < num_images; j++)
562 overlap_ratio<TImage>(tile_pyramid[high_res_level][i],
563 tile_pyramid[high_res_level][j],
565 if (overlap < overlap_min || overlap > overlap_max)
continue;
567 translate_transform_t::Pointer t_ij = translate_transform_t::New();
568 t_ij->SetParameters(mapping[i][j]->GetParameters());
570 oss <<
"refining the mapping: " << std::setw(2) << j <<
" -> "
571 << std::setw(2) << i << std::endl
572 <<
"overlap: " <<
static_cast<int>(overlap * 100.0) <<
" percent" << std::endl;
574 std::ostringstream fn_prefix;
575 if (! prefix.empty())
577 fn_prefix << prefix <<
"pair-" << the_text_t::number(i, 2,
'0') <<
"-" <<
578 the_text_t::number(j, 2,
'0') <<
"-pass-" << the_text_t::number(pass, 2,
'0') <<
"-";
581 cost[i][j] = refine_one_pair<TImage, TMask>(tile_pyramid,
591 overlap = overlap_ratio<TImage>(tile_pyramid[high_res_level][i],
592 tile_pyramid[high_res_level][j],
595 if (overlap < overlap_min || overlap > overlap_max)
598 cost[i][j] = std::numeric_limits<double>::max();
608 path[j][i] = inverse(t_ij);
609 cost[j][i] = cost[i][j];
616 for (
unsigned int i = 0; i < num_images; i++) oss << std::setw(9) << i;
619 for (
unsigned int i = 0; i < num_images; i++) oss <<
"---------";
621 for (
unsigned int i = 0; i < num_images; i++)
624 for (
unsigned int j = 0; j < num_images; j++)
626 if (path[i][j].GetPointer() != NULL)
628 oss <<
' ' << cost[i][j];
639 CORE_LOG_MESSAGE(oss.str());
646 template <
typename TImage,
typename TMask>
648 brute_force_one_pair(
const TImage * i0,
652 translate_transform_t::Pointer & t01,
657 const std::string & fn_prefix)
659 translate_transform_t::ParametersType init_params = t01->GetParameters();
660 translate_transform_t::ParametersType best_params = init_params;
661 translate_transform_t::ParametersType curr_params = init_params;
663 translate_transform_t::Pointer t = translate_transform_t::New();
664 double best_metric = std::numeric_limits<double>::max();
665 typename TImage::SpacingType sp = i0->GetSpacing();
667 typedef typename itk::NearestNeighborInterpolateImageFunction
668 <TImage,
double> nn_interpolator_t;
669 typename nn_interpolator_t::Pointer nn = nn_interpolator_t::New();
670 nn->SetInputImage(i1);
672 if (! fn_prefix.empty())
674 save_rgb<TImage>(fn_prefix +
"a.tif", i0, i1, t01, m0, m1);
677 for (
int x = -dx; x <= dx; x++)
679 for (
int y = -dy; y <= dy; y++)
682 curr_params[0] = init_params[0] + sp[0] *
static_cast<double>(x);
683 curr_params[1] = init_params[1] + sp[1] *
static_cast<double>(y);
685 t->SetParameters(curr_params);
687 double overlap_area = 0;
689 my_metric<TImage, nn_interpolator_t>
690 (overlap_area, i0, i1, t, m0, m1, nn);
692 if (metric < best_metric)
694 best_metric = metric;
695 best_params = curr_params;
700 t01->SetParameters(best_params);
702 if (! fn_prefix.empty())
704 save_rgb<TImage>(fn_prefix +
"b.tif", i0, i1, t01, m0, m1);
714 template <
typename TImage,
typename TMask>
716 brute_force_one_pair(
const array2d(
typename TImage::Pointer) & tile_pyramid,
717 const array2d(
typename TMask::ConstPointer) & mask_pyramid,
718 const unsigned int & ia,
719 const unsigned int & ib,
720 translate_transform_t::Pointer & t01,
724 const unsigned int & coarse_to_fine_levels,
726 const bfs::path & prefix)
728 unsigned int num_levels = std::min(coarse_to_fine_levels,
729 static_cast<unsigned int>(tile_pyramid.size()));
730 unsigned int high_res_level = num_levels - 1;
732 for (
unsigned int i = 0; i < num_levels; i++)
734 std::ostringstream fn_prefix;
735 if (! prefix.empty())
737 fn_prefix << prefix <<
"level-" << the_text_t::number(i) <<
"-";
740 brute_force_one_pair<TImage, TMask>(tile_pyramid[i][ia].GetPointer(),
741 tile_pyramid[i][ib].GetPointer(),
742 mask_pyramid[i][ia].GetPointer(),
743 mask_pyramid[i][ib].GetPointer(),
751 typedef itk::MeanSquaresImageToImageMetric<TImage, TImage> metric_t;
753 typedef itk::LinearInterpolateImageFunction<TImage, double> interpolator_t;
755 eval_metric<metric_t, interpolator_t>(t01,
756 tile_pyramid[high_res_level][ia],
757 tile_pyramid[high_res_level][ib],
758 mask_pyramid[high_res_level][ia],
759 mask_pyramid[high_res_level][ib]);
767 template <
typename TImage,
typename TMask>
769 brute_force_pairs(
const std::vector<typename TImage::Pointer> & image,
770 const std::vector<typename TMask::ConstPointer> & mask,
771 const double & overlap_min,
772 const double & overlap_max,
776 const array2d(translate_transform_t::Pointer) & mapping,
777 array2d(translate_transform_t::Pointer) & path,
778 array2d(
double) & cost,
780 const bfs::path & prefix)
782 static unsigned int pass = 0;
785 const unsigned int num_images = image.size();
786 std::ostringstream oss;
787 for (
unsigned int i = 0; i < num_images - 1; i++)
789 for (
unsigned int j = i + 1; j < num_images; j++)
793 overlap_ratio<TImage>(image[i], image[j], mapping[i][j]);
794 if (overlap < overlap_min || overlap > overlap_max)
continue;
796 translate_transform_t::Pointer t_ij = translate_transform_t::New();
797 t_ij->SetParameters(mapping[i][j]->GetParameters());
799 oss <<
"refining the mapping: " << std::setw(2) << j <<
" -> "
800 << std::setw(2) << i << std::endl
801 <<
"overlap: " <<
static_cast<int>(overlap * 100.0) <<
" percent" << std::endl;
803 std::ostringstream fn_prefix;
804 if (! prefix.empty())
806 fn_prefix << prefix <<
"pair-" << the_text_t::number(i, 2,
'0') <<
"-" <<
807 the_text_t::number(j, 2,
'0') <<
"-pass-" << the_text_t::number(pass, 2,
'0') <<
"-";
810 cost[i][j] = brute_force_one_pair<TImage, TMask>(image[i],
819 overlap = overlap_ratio<TImage>(image[i], image[j], t_ij);
821 if (overlap < overlap_min || overlap > overlap_max)
824 cost[i][j] = std::numeric_limits<double>::max();
834 path[j][i] = inverse(t_ij);
835 cost[j][i] = cost[i][j];
842 for (
unsigned int i = 0; i < num_images; i++) oss << std::setw(9) << i;
845 for (
unsigned int i = 0; i < num_images; i++) oss <<
"---------";
847 for (
unsigned int i = 0; i < num_images; i++)
850 for (
unsigned int j = 0; j < num_images; j++)
852 if (path[i][j].GetPointer() != NULL)
854 oss <<
' ' << cost[i][j];
865 CORE_LOG_MESSAGE(oss.str());
871 template <
typename TImage,
typename TMask>
873 brute_force_pairs(
const array2d(
typename TImage::Pointer) & tile_pyramid,
874 const array2d(
typename TMask::ConstPointer) & mask_pyramid,
875 const double & overlap_min,
876 const double & overlap_max,
881 const unsigned int & coarse_to_fine_levels,
883 const array2d(translate_transform_t::Pointer) & mapping,
884 array2d(translate_transform_t::Pointer) & path,
885 array2d(
double) & cost,
887 const bfs::path & prefix)
889 static unsigned int pass = 0;
892 unsigned int pyramid_levels = tile_pyramid.size();
893 unsigned int high_res_level = pyramid_levels - 1;
895 const unsigned int num_images = tile_pyramid[high_res_level].size();
896 std::ostringstream oss;
897 for (
unsigned int i = 0; i < num_images - 1; i++)
899 for (
unsigned int j = i + 1; j < num_images; j++)
903 overlap_ratio<TImage>(tile_pyramid[high_res_level][i],
904 tile_pyramid[high_res_level][j],
906 if (overlap < overlap_min || overlap > overlap_max)
continue;
908 translate_transform_t::Pointer t_ij = translate_transform_t::New();
909 t_ij->SetParameters(mapping[i][j]->GetParameters());
911 oss <<
"refining the mapping: " << std::setw(2) << j <<
" -> "
912 << std::setw(2) << i << std::endl
913 <<
"overlap: " <<
static_cast<int>(overlap * 100.0) <<
" percent" << std::endl;
915 std::ostringstream fn_prefix;
916 if (! prefix.empty())
918 fn_prefix << prefix <<
"pair-" << the_text_t::number(i, 2,
'0') <<
"-" <<
919 the_text_t::number(j, 2,
'0') <<
"-pass-" << the_text_t::number(pass, 2,
'0') <<
"-";
922 cost[i][j] = brute_force_one_pair<TImage, TMask>(tile_pyramid,
929 coarse_to_fine_levels,
932 overlap = overlap_ratio<TImage>(tile_pyramid[high_res_level][i],
933 tile_pyramid[high_res_level][j],
936 if (overlap < overlap_min || overlap > overlap_max)
939 cost[i][j] = std::numeric_limits<double>::max();
949 path[j][i] = inverse(t_ij);
950 cost[j][i] = cost[i][j];
957 for (
unsigned int i = 0; i < num_images; i++) oss << std::setw(9) << i;
960 for (
unsigned int i = 0; i < num_images; i++) oss <<
"---------";
962 for (
unsigned int i = 0; i < num_images; i++)
965 for (
unsigned int j = 0; j < num_images; j++)
967 if (path[i][j].GetPointer() != NULL)
969 oss <<
' ' << cost[i][j];
980 CORE_LOG_MESSAGE(oss.str());
987 template <
typename TImage,
typename TMask>
989 dump_neighbors(
const std::vector<typename TImage::Pointer> & image,
990 const std::vector<typename TMask::ConstPointer> & mask,
991 const array2d(translate_transform_t::Pointer) & path,
992 const bfs::path & prefix)
994 static unsigned int pass = 0;
997 const unsigned int num_images = image.size();
998 std::ostringstream oss;
999 for (
unsigned int i = 0; i < num_images; i++)
1001 std::list<typename TImage::Pointer> image_list;
1002 std::list<typename TMask::ConstPointer> mask_list;
1003 std::list<base_transform_t::ConstPointer> transform_list;
1005 image_list.push_back(image[i]);
1006 mask_list.push_back(mask[i]);
1007 transform_list.push_back((identity_transform_t::New()).GetPointer());
1010 for (
unsigned int j = 0; j < num_images; j++)
1012 if (i == j)
continue;
1013 if (path[i][j].GetPointer() == NULL)
continue;
1015 oss <<
"found a mapping: " << std::setw(2) << j <<
" -> "
1016 << std::setw(2) << i << std::endl;
1018 image_list.push_back(image[j]);
1019 mask_list.push_back(mask[j]);
1020 transform_list.push_back(path[i][j].GetPointer());
1023 CORE_LOG_MESSAGE(oss.str());
1025 std::vector<typename TImage::Pointer>
1026 images(image_list.begin(), image_list.end());
1028 std::vector<typename TMask::ConstPointer>
1029 masks(mask_list.begin(), mask_list.end());
1031 std::vector<base_transform_t::ConstPointer>
1032 transforms(transform_list.begin(), transform_list.end());
1034 typename TImage::Pointer mosaic[3];
1035 make_mosaic_rgb(mosaic,
1036 std::vector<bool>(images.size(),
false),
1044 std::ostringstream fn_save;
1045 fn_save << prefix <<
"neighbors-" << the_text_t::number(i, 2,
'0') <<
1046 "-pass-" << the_text_t::number(pass) <<
".png";
1047 save_rgb<typename TImage::Pointer>(mosaic, fn_save.str(),
true);
1055 template <
class TImage,
class transform_ptr_t>
1057 calc_area_and_dist(
const std::vector<typename TImage::Pointer> & image,
1058 const array2d(transform_ptr_t) & mapping,
1059 array2d(
double) & overlap,
1060 array2d(
double) & distance)
1062 const unsigned int num_images = image.size();
1063 resize(overlap, num_images, num_images);
1064 resize(distance, num_images, num_images);
1066 for (
unsigned int i = 0; i < num_images; i++)
1068 if (image[i].GetPointer() == NULL)
continue;
1073 typename TImage::SizeType sz =
1074 image[i]->GetLargestPossibleRegion().GetSize();
1076 typename TImage::SpacingType sp =
1077 image[i]->GetSpacing();
1079 pnt2d_t tile_min = image[i]->GetOrigin();
1081 tile_max[0] = tile_min[0] + sp[0] *
static_cast<double>(sz[0]);
1082 tile_max[1] = tile_min[1] + sp[1] *
static_cast<double>(sz[1]);
1084 ci = pnt2d(tile_min[0] + (tile_max[0] - tile_min[0]) / 2.0,
1085 tile_min[1] + (tile_max[1] - tile_min[1]) / 2.0);
1088 overlap[i][i] = 1.0;
1089 distance[i][i] = 0.0;
1091 for (
unsigned int j = i + 1; j < num_images; j++)
1093 if (mapping[i][j].GetPointer() == NULL)
continue;
1094 overlap[i][j] = overlap_ratio<TImage>(image[i], image[j],
1096 overlap[j][i] = overlap[i][j];
1100 typename TImage::SizeType sz =
1101 image[j]->GetLargestPossibleRegion().GetSize();
1103 typename TImage::SpacingType sp =
1104 image[j]->GetSpacing();
1106 pnt2d_t tile_min = image[j]->GetOrigin();
1108 tile_max[0] = tile_min[0] + sp[0] *
static_cast<double>(sz[0]);
1109 tile_max[1] = tile_min[1] + sp[1] *
static_cast<double>(sz[1]);
1112 pnt2d(tile_min[0] + (tile_max[0] - tile_min[0]) / 2.0,
1113 tile_min[1] + (tile_max[1] - tile_min[1]) / 2.0);
1115 cj = mapping[j][i]->TransformPoint(center);
1118 distance[i][j] = (ci - cj).GetNorm();
1119 distance[j][i] = distance[i][j];
1132 const unsigned int & to = UINT_MAX,
1133 const double & dist = std::numeric_limits<double>::max()):
1141 if (to_ == m.to_)
return distance_ < m.distance_;
1146 {
return to_ == m.to_; }
1162 metric_(std::numeric_limits<double>::max())
1166 const double & metric,
1167 translate_transform_t::Pointer & t):
1173 inline bool operator == (
const neighbor_t & d)
const
1174 {
return id_ == d.id_; }
1176 inline bool operator < (
const neighbor_t & d)
const
1177 {
return metric_ < d.metric_; }
1181 translate_transform_t::Pointer t_;
1187 template <
class TImage,
class TMask>
1189 match_pairs(std::vector<typename TImage::Pointer> & image,
1190 std::vector<typename TMask::ConstPointer> & mask,
1191 const bool & images_were_resampled,
1192 const bool & use_std_mask,
1194 std::vector<bool> & matched,
1195 array2d(translate_transform_t::Pointer) & path,
1196 array2d(
double) & cost,
1198 const double & overlap_min,
1199 const double & overlap_max,
1201 image_t::PointType offset_min,
1202 image_t::PointType offset_max,
1204 double min_peak = 0.1,
1205 double peak_threshold = 0.3)
1208 const unsigned int num_images = image.size();
1209 if (num_images < 2)
return false;
1212 for (
unsigned int i = 0; i < num_images; i++)
1215 for (
unsigned int j = 0; j < num_images; j++)
1218 cost[i][j] = std::numeric_limits<double>::max();
1226 std::vector<std::pair<unsigned int, std::list<local_max_t> > >
1229 std::list<unsigned int> perimeter;
1230 bool perimeter_seeded =
false;
1231 std::ostringstream oss;
1232 for (
unsigned int seed = 0;
1233 seed < num_images && (!perimeter_seeded || !perimeter.empty());
1237 if (!perimeter_seeded) push_back_unique(perimeter, seed);
1241 while (!perimeter.empty())
1243 oss <<
"- - - - - - - - - - - - - - - - - - - - - - - - - - -" << std::endl;
1246 unsigned int node_id = remove_head(perimeter);
1247 matched[node_id] =
true;
1250 std::list<neighbor_t> md;
1251 unsigned int md_size = 0;
1252 for (
unsigned int i = 0; i < matched.size(); i++)
1254 if (matched[i])
continue;
1256 oss << std::endl <<
"matching image " << node_id <<
" to " << i <<
", "
1259 const TImage * fi = image[node_id];
1260 const TImage * mi = image[i];
1261 const TMask * fi_mask = mask[node_id];
1262 const TMask * mi_mask = mask[i];
1266 best_match.metric_ =
1267 match_one_pair<TImage, TMask>(images_were_resampled,
1281 if (best_match.metric_ != std::numeric_limits<double>::max())
1283 md.push_back(best_match);
1291 std::list<unsigned int> next_perimeter;
1295 oss << std::endl <<
"METRICS: " << std::flush;
1299 oss << std::endl <<
"WARNING: " << node_id
1300 <<
" did not match any other tile" << std::flush;
1303 bool separator =
false;
1304 for (std::list<neighbor_t>::iterator i = md.begin();
1310 double decay = (1.0 + data.metric_) / (1.0 + best.metric_);
1321 next_perimeter.push_front(data.id_);
1323 path[node_id][data.id_] = data.t_;
1324 cost[node_id][data.id_] = data.metric_;
1325 path[data.id_][node_id] = inverse(data.t_);
1326 cost[data.id_][node_id] = data.metric_;
1329 oss << data.metric_ <<
' ' << std::flush;
1334 expand(perimeter, next_perimeter,
true);
1337 perimeter_seeded = perimeter_seeded || (next_perimeter.size() != 0);
1342 if (!perimeter_seeded)
continue;
1345 for (
unsigned int i = 0; i < num_images; i++)
1347 if (matched[i])
continue;
1349 unsigned int node_id = peaks[i].first;
1350 const TImage * fi = image[node_id];
1351 const TImage * mi = image[i];
1352 const TMask * fi_mask = mask[node_id];
1353 const TMask * mi_mask = mask[i];
1357 for (std::list<local_max_t>::iterator j = peaks[i].second.begin();
1358 j != peaks[i].second.end(); ++j)
1360 oss <<
"evaluating permutations..." << std::endl;
1363 image_t::PointType offset_min;
1364 offset_min[0] = -std::numeric_limits<double>::max();
1365 offset_min[1] = -std::numeric_limits<double>::max();
1366 image_t::PointType offset_max;
1367 offset_max[0] = std::numeric_limits<double>::max();
1368 offset_max[1] = std::numeric_limits<double>::max();
1370 translate_transform_t::Pointer ti = translate_transform_t::New();
1371 double metric = estimate_displacement<TImage>(fi,
1381 if (metric > best_match.metric_)
continue;
1386 if (best_match.id_ == UINT_MAX)
continue;
1387 oss <<
"METRIC: " << best_match.metric_ << std::endl;
1389 perimeter.push_front(best_match.id_);
1391 path[node_id][best_match.id_] = best_match.t_;
1392 cost[node_id][best_match.id_] = best_match.metric_;
1393 path[best_match.id_][node_id] = inverse(best_match.t_);
1394 cost[best_match.id_][node_id] = best_match.metric_;
1398 CORE_LOG_MESSAGE(oss.str());
1399 return perimeter_seeded;
1405 template <
class TImage,
class TMask>
1407 match_pairs_strategy(std::vector<typename TImage::Pointer> & image,
1408 std::vector<typename TMask::ConstPointer> & mask,
1409 const bool & images_were_resampled,
1410 const bool & use_std_mask,
1412 std::vector<bool> & matched,
1413 array2d(translate_transform_t::Pointer) & path,
1414 array2d(
double) & cost,
1416 const double & overlap_min,
1417 const double & overlap_max,
1419 image_t::PointType offset_min,
1420 image_t::PointType offset_max,
1424 const StrategyType strategy,
1426 double min_peak = 0.1,
1427 double peak_threshold = 0.3)
1430 if (strategy == DEFAULT)
1431 CORE_LOG_DEBUG(
"match_pairs_strategy: DEFAULT");
1432 else if (strategy == TOP_LEFT_BOOK)
1433 CORE_LOG_DEBUG(
"match_pairs_strategy: TOP_LEFT_BOOK");
1434 else if (strategy == TOP_LEFT_SNAKE)
1435 CORE_LOG_DEBUG(
"match_pairs_strategy: TOP_LEFT_SNAKE");
1437 CORE_LOG_DEBUG(
"match_pairs_strategy: UNKNOWN");
1441 const unsigned int num_images = image.size();
1442 if (num_images < 2)
return false;
1444 typename TImage::IndexValueType numRows =
1445 (
typename TImage::IndexValueType)
1446 (image[0]->GetLargestPossibleRegion().GetSize()[0]);
1447 typename TImage::IndexValueType numCols =
1448 (
typename TImage::IndexValueType)
1449 (image[0]->GetLargestPossibleRegion().GetSize()[1]);
1450 std::vector<std::pair<unsigned int, std::list<local_max_t> > >
1453 std::ostringstream oss;
1454 for (
int row = 0; row < edgeLen; row++) {
1455 for (
int column = 0; column < edgeLen; column++) {
1456 oss <<
"- - - - - - - - - - - - - - - - - - - - - -" << std::endl;
1458 int seed = column + row * edgeLen;
1459 if (seed > num_images)
break;
1460 matched[seed] =
true;
1462 for (
int i = -1; i <= 1; i++) {
1463 for (
int j = -1; j <= 1; j++) {
1465 if ((i == 0 && j == 0) ||
1466 i + row < 0 || i + row >= edgeLen ||
1467 j + column < 0 || j + column >= edgeLen)
1471 if (i && j)
continue;
1474 int neighbor = seed + edgeLen * i + j;
1475 if (strategy == TOP_LEFT_SNAKE)
1477 int col = neighbor % edgeLen;
1478 int rw = ((double)neighbor) / ((double)edgeLen);
1480 col = edgeLen - col - 1;
1481 neighbor = col + rw * edgeLen;
1484 if (neighbor > num_images || neighbor < 0)
1487 std::cout <<
"matching image " <<
1488 seed <<
" to " << neighbor <<
". " << std::endl;
1492 const TImage * fi = image[seed];
1493 const TImage * mi = image[neighbor];
1494 const TMask * fi_mask = mask[seed];
1495 const TMask * mi_mask = mask[neighbor];
1498 best_match.id_ = neighbor;
1499 best_match.metric_ = match_one_pair<TImage, TMask>(
1500 images_were_resampled,
1516 static_cast<size_t>((i+1) + ((j+1)*4)));
1518 if (best_match.metric_ ==
1519 std::numeric_limits<double>::max()) {
1521 translate_transform_t::Pointer t = translate_transform_t::New();
1522 typedef itk::TranslationTransform<double,2>::OutputVectorType VectorType;
1524 double rowLen =
static_cast<double>(numRows) * shrink;
1525 double colLen =
static_cast<double>(numCols) * shrink;
1526 v[0] = rowLen * overlap_min * ((column<edgeLen/2)?-1.:1.);
1527 v[1] = colLen * overlap_min * ((row <edgeLen/2)?-1.:1.);
1529 v[0] = rowLen * (1. - overlap_min);
1531 v[0] = rowLen * (overlap_min - 1.);
1533 v[1] = colLen * (1. - overlap_min);
1535 v[1] = colLen * (overlap_min - 1.);
1539 if (column + j > 0) {
1540 v[1] = path[seed - 1][neighbor - 1].
1541 GetPointer()->GetOffset()[1];
1544 }
else if (seed - 1 != neighbor) {
1545 v[1] = path[seed - 1][neighbor].
1546 GetPointer()->GetOffset()[1];
1557 v[0] = path[seed - edgeLen][neighbor - edgeLen].
1558 GetPointer()->GetOffset()[0];
1561 }
else if (seed - edgeLen != neighbor) {
1562 v[0] = path[seed - edgeLen][neighbor].
1563 GetPointer()->GetOffset()[0];
1572 t.GetPointer()->SetOffset(v);
1574 best_match.metric_ = estimate_displacement<TImage>(
1589 path[seed][neighbor] = best_match.t_;
1590 cost[seed][neighbor] = best_match.metric_;
1594 if (cost[neighbor][seed] > best_match.metric_) {
1595 path[neighbor][seed] = inverse(best_match.t_);
1596 cost[neighbor][seed] = best_match.metric_;
1602 CORE_LOG_MESSAGE(oss.str());
1610 template <
class TImage,
class TMask>
1614 const unsigned int & num_tiles,
1615 array2d(
typename TImage::Pointer) & tile_pyramid,
1616 array2d(
typename TMask::ConstPointer) & mask_pyramid,
1619 const unsigned int & max_cascade_len,
1622 std::vector<affine_transform_t::Pointer> & affine,
1625 std::vector<bool> & matched,
1628 std::list<unsigned int> & tile_order,
1632 array3d(
double) & cost,
1637 array3d(translate_transform_t::Pointer) & path,
1640 bool images_were_resampled,
1647 image_t::PointType offset_min,
1648 image_t::PointType offset_max,
1651 double peak_threshold,
1655 const StrategyType strategy = DEFAULT,
1657 bool try_refining =
false,
1658 const bfs::path & prefix =
"")
1660 typedef itk::LinearInterpolateImageFunction<TImage, double> interpolator_t;
1662 static const unsigned int iterations_per_level = 8;
1663 unsigned int pyramid_levels = tile_pyramid.size();
1664 unsigned int high_res_level = pyramid_levels - 1;
1666 bool perimeter_seeded =
false;
1668 if (strategy == DEFAULT)
1670 match_pairs<TImage, TMask>(tile_pyramid[high_res_level],
1671 mask_pyramid[high_res_level],
1672 images_were_resampled,
1685 match_pairs_strategy<TImage, TMask>(tile_pyramid[high_res_level],
1686 mask_pyramid[high_res_level],
1687 images_were_resampled,
1702 std::ostringstream oss;
1703 if (! perimeter_seeded)
1705 oss <<
"WARNING: none of the tiles matched..." << std::endl;
1706 std::cout <<
"WARNING: none of the tiles matched..." << std::endl;
1707 CORE_LOG_MESSAGE(oss.str());
1714 if (strategy == DEFAULT)
1715 assemble_cascades(num_tiles, max_cascade_len, path, cost,
false);
1718 array2d(translate_transform_t::Pointer) mapping;
1719 resize(mapping, num_tiles, num_tiles);
1721 array2d(
double) mapping_cost;
1722 resize(mapping_cost, num_tiles, num_tiles);
1724 establish_mappings(num_tiles,
1731 if (strategy == DEFAULT)
1733 reset(num_tiles, max_cascade_len, path, cost);
1735 brute_force_pairs<TImage, TMask>(tile_pyramid,
1748 if (! prefix.empty())
1750 dump_neighbors<TImage, TMask>(tile_pyramid[high_res_level],
1751 mask_pyramid[high_res_level],
1757 array2d(
double) local_overlap;
1758 array2d(
double) local_distance;
1759 calc_area_and_dist<TImage, translate_transform_t::Pointer>
1760 (tile_pyramid[high_res_level], path[0], local_overlap, local_distance);
1762 unsigned int best_target = ~0;
1765 double best_overlap = 0.0;
1766 for (
unsigned int i = 0; i < num_tiles; i++)
1768 double overlap_sum = 0.0;
1769 for (
unsigned int j = 0; j < num_tiles; j++)
1771 if (j == i)
continue;
1773 overlap_sum += local_overlap[i][j];
1776 if (overlap_sum > best_overlap)
1779 best_overlap = overlap_sum;
1783 std::list<local_mapping_t> perimeter;
1787 std::list<unsigned int> tiles;
1788 for (
unsigned int i = 0; i < num_tiles; i++)
1790 if (i == best_target)
continue;
1791 if (tile_pyramid[high_res_level][i].GetPointer() == NULL)
continue;
1797 affine.resize(num_tiles);
1799 identity_transform_t::Pointer identity = identity_transform_t::New();
1800 affine[best_target] =
1801 setup_transform<TImage>(tile_pyramid[high_res_level][best_target],
1806 std::vector<bool> param_shared;
1807 std::vector<bool> param_active;
1808 affine_transform_t::setup_shared_params_mask(
false, param_shared);
1809 param_active.assign(param_shared.size(),
true);
1815 std::list<local_mapping_t> candidates;
1818 while (!perimeter.empty())
1820 const unsigned int i = remove_head(perimeter).to_;
1821 tile_order.push_back(i);
1824 for (std::list<unsigned int>::const_iterator jter = tiles.begin();
1825 jter != tiles.end(); ++jter)
1827 const unsigned int j = *jter;
1828 if (path[0][i][j].GetPointer() == NULL)
continue;
1831 candidates.push_back(m);
1835 if (candidates.empty())
1843 perimeter.push_back(remove_head(candidates));
1844 tiles.remove(perimeter.back().to_);
1846 while (!candidates.empty())
1850 if (prev.to_ == next.to_)
continue;
1852 perimeter.push_back(next);
1853 tiles.remove(next.to_);
1857 const unsigned int num_optimize = tile_order.size() + perimeter.size();
1863 typename mosaic_metric_t::Pointer mosaic_metric = mosaic_metric_t::New();
1864 mosaic_metric->image_.resize(num_optimize);
1865 mosaic_metric->mask_.resize(num_optimize);
1866 mosaic_metric->transform_.resize(num_optimize);
1868 std::vector<unsigned int> tile_id(num_optimize);
1871 for (std::list<unsigned int>::const_iterator iter = tile_order.begin();
1872 iter != tile_order.end(); ++iter, i++)
1875 mosaic_metric->image_[i] = tile_pyramid[high_res_level][tile_id[i]];
1876 mosaic_metric->mask_[i] = mask_pyramid[high_res_level][tile_id[i]];
1877 mosaic_metric->transform_[i] = affine[tile_id[i]].GetPointer();
1880 for (std::list<local_mapping_t>::const_iterator iter = perimeter.begin();
1881 iter != perimeter.end(); ++iter, i++)
1885 affine[tile_id[i]] =
1886 setup_transform<TImage>(tile_pyramid[high_res_level][tile_id[i]],
1888 path[0][m.from_][tile_id[i]]);
1890 mosaic_metric->image_[i] = tile_pyramid[high_res_level][tile_id[i]];
1891 mosaic_metric->mask_[i] = mask_pyramid[high_res_level][tile_id[i]];
1892 mosaic_metric->transform_[i] = affine[tile_id[i]].GetPointer();
1900 mosaic_metric->setup_param_map(param_shared, param_active);
1901 mosaic_metric->Initialize();
1904 typename mosaic_metric_t::params_t parameter_scales =
1905 mosaic_metric->GetTransformParameters();
1906 parameter_scales.Fill(1.0);
1909 for (
unsigned int k = 0; k < num_optimize; k++)
1911 const unsigned int * addr = &(mosaic_metric->address_[k][0]);
1913 parameter_scales[addr[affine_transform_t::index_a(0, 1)]] = 1e+6;
1914 parameter_scales[addr[affine_transform_t::index_b(1, 0)]] = 1e+6;
1917 for (
unsigned int i = 0; i < num_optimize; i++)
1919 mosaic_metric->image_[i] = tile_pyramid[high_res_level][tile_id[i]];
1922 typename mosaic_metric_t::measure_t metric_before =
1923 mosaic_metric->GetValue(mosaic_metric->GetTransformParameters());
1926 for (
unsigned int k = 0; k < 1; k++)
1928 typename mosaic_metric_t::params_t params_before =
1929 mosaic_metric->GetTransformParameters();
1931 typename mosaic_metric_t::measure_t metric_after =
1932 std::numeric_limits<typename mosaic_metric_t::measure_t>::max();
1935 optimizer_t::Pointer optimizer = optimizer_t::New();
1936 optimizer_observer_t<optimizer_t>::Pointer observer =
1938 optimizer->AddObserver(itk::IterationEvent(), observer);
1939 optimizer->SetMinimize(
true);
1940 optimizer->SetNumberOfIterations(iterations_per_level);
1941 optimizer->SetMinimumStepLength(1e-12);
1942 optimizer->SetMaximumStepLength(1e-5);
1943 optimizer->SetGradientMagnitudeTolerance(1e-6);
1944 optimizer->SetRelaxationFactor(5e-1);
1945 optimizer->SetCostFunction(mosaic_metric);
1946 optimizer->SetInitialPosition(params_before);
1947 optimizer->SetScales(parameter_scales);
1948 optimizer->SetPickUpPaceSteps(5);
1949 optimizer->SetBackTracking(
true);
1954 oss <<
"\n" << k <<
": refining affine transforms" << std::endl;
1955 optimizer->StartOptimization();
1957 catch (itk::ExceptionObject & exception)
1960 oss <<
"optimizer threw an exception:" << std::endl << exception.what() << std::endl;
1962 mosaic_metric->SetTransformParameters(optimizer->GetBestParams());
1963 metric_after = optimizer->GetBestValue();
1965 typename mosaic_metric_t::params_t params_after =
1966 mosaic_metric->GetTransformParameters();
1968 oss <<
"before: METRIC = " << metric_before
1969 <<
", PARAMS = " << params_before << std::endl
1970 <<
"after: METRIC = " << metric_after
1971 <<
", PARAMS = " << params_after << std::endl;
1974 double improvement = 1.0 - metric_after / metric_before;
1975 bool failed_to_improve = (metric_after - metric_before) >= 0.0;
1976 bool negligible_improvement = !failed_to_improve && (improvement < 1e-3);
1978 if (!failed_to_improve)
1980 oss <<
"IMPROVEMENT: " << std::setw(3) <<
static_cast<int>(100.0 * improvement) <<
"%" << std::endl;
1983 if (failed_to_improve)
1985 oss <<
"NOTE: minimization failed, ignoring registration results..." << std::endl;
1988 mosaic_metric->SetTransformParameters(params_before);
1991 else if (negligible_improvement)
1993 oss <<
"NOTE: improvement is negligible..." << std::endl;
1998 metric_before = metric_after;
2001 CORE_LOG_MESSAGE(oss.str());
2005 #endif // MOSAIC_LAYOUT_COMMON_HXX_
Computes mean pixel variance within the overlapping regions of a mosaic.
Definition: itkImageMosaicVarianceMetric.h:69
Definition: mosaic_layout_common.hxx:1128
Definition: optimizer_observer.hxx:44
Definition: fft_common.hxx:77
Definition: mosaic_layout_common.hxx:1157