1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
/* Copyright (c) 2008-2025 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/
#include "command.h"
#include "header.h"
#include "image.h"
#include "image_helpers.h"
#include "progressbar.h"
#include "algo/loop.h"
#include "dwi/tractography/ACT/act.h"
#include "dwi/tractography/ACT/tissues.h"
using namespace MR;
using namespace App;
void usage ()
{
AUTHOR = "Robert E. Smith (robert.smith@florey.edu.au)";
SYNOPSIS = "Generate a mask image appropriate for seeding streamlines on the grey matter-white matter interface";
REFERENCES
+ "Smith, R. E.; Tournier, J.-D.; Calamante, F. & Connelly, A. " // Internal
"Anatomically-constrained tractography:"
"Improved diffusion MRI streamlines tractography through effective use of anatomical information. "
"NeuroImage, 2012, 62, 1924-1938";
ARGUMENTS
+ Argument ("5tt_in", "the input 5TT segmented anatomical image").type_image_in()
+ Argument ("mask_out", "the output mask image") .type_image_out();
OPTIONS
+ Option("mask_in", "Filter an input mask image according to those voxels that lie upon the grey matter - white matter boundary. "
"If no input mask is provided, the output will be a whole-brain mask image calculated using the anatomical image only.")
+ Argument ("image", "the input mask image").type_image_in();
}
class Processor
{ MEMALIGN(Processor)
public:
Processor (const Image<bool>& mask) : mask (mask) { }
Processor (const Processor&) = default;
bool operator() (Image<float>& input, Image<float>& output)
{
// If a mask is defined, but is false in this voxel, do not continue processing
bool process_voxel = true;
if (mask.valid()) {
assign_pos_of (input, 0, 3).to (mask);
process_voxel = mask.value();
}
if (process_voxel) {
// Generate a non-binary seeding mask.
// Image intensity should be proportional to the tissue gradient present across the voxel
// Remember: This seeding mask is generated in the same space as the 5TT image: exploit this
// (no interpolators should be necessary)
// Essentially looking for an absolute gradient in (GM - WM) - just do in three axes
// - well, not quite; needs to be the minimum of the two
default_type gradient = 0.0;
for (size_t axis = 0; axis != 3; ++axis) {
assign_pos_of (output, 0, 3).to (input);
default_type multiplier = 0.5;
if (!output.index(axis)) {
multiplier = 1.0;
} else {
input.move_index (axis, -1);
}
const DWI::Tractography::ACT::Tissues neg (input);
if (output.index(axis) == output.size(axis)-1) {
multiplier = 1.0;
input.index(axis) = output.index(axis);
} else {
input.index(axis) = output.index(axis) + 1;
}
const DWI::Tractography::ACT::Tissues pos (input);
gradient += Math::pow2 (multiplier * std::min (abs (pos.get_gm() - neg.get_gm()), abs (pos.get_wm() - neg.get_wm())));
}
output.value() = std::max (0.0, std::sqrt (gradient));
assign_pos_of (output, 0, 3).to (input);
} else {
output.value() = 0.0f;
}
return true;
}
private:
Image<bool> mask;
};
void run ()
{
auto input = Image<float>::open (argument[0]);
DWI::Tractography::ACT::verify_5TT_image (input);
check_3D_nonunity (input);
// TODO It would be nice to have the capability to define this mask based on another image
// This will however require the use of interpolators
Image<bool> mask;
auto opt = get_options ("mask_in");
if (opt.size()) {
mask = Image<bool>::open (opt[0][0]);
if (!dimensions_match (input, mask, 0, 3))
throw Exception ("Mask image provided using the -mask option must match the input 5TT image");
}
Header H;
if (mask.valid()) {
H = mask;
H.datatype() = DataType::Float32;
H.datatype().set_byte_order_native();
} else {
H = input;
H.ndim() = 3;
}
auto output = Image<float>::create (argument[1], H);
ThreadedLoop ("Generating GMWMI seed mask", input, 0, 3)
.run (Processor (mask), input, output);
}
|