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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
/* 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 "image.h"
#include "image_helpers.h"
#include "mrtrix.h"
#include "transform.h"
#include "algo/loop.h"
#include "file/path.h"
#include "file/utils.h"
#include "interp/nearest.h"
#include "connectome/connectome.h"
#include "connectome/lut.h"
#include <string>
#define SPINE_NODE_NAME std::string("Spinal_column")
using namespace MR;
using namespace App;
using namespace MR::Connectome;
using MR::Connectome::node_t;
void usage ()
{
AUTHOR = "Robert E. Smith (robert.smith@florey.edu.au)";
SYNOPSIS = "Convert a connectome node image from one lookup table to another";
DESCRIPTION
+ "Typical usage is to convert a parcellation image provided by some other software, based on "
"the lookup table provided by that software, to conform to a new lookup table, particularly "
"one where the node indices increment from 1, in preparation for connectome construction; "
"examples of such target lookup table files are provided in share//mrtrix3//labelconvert//, "
"but can be created by the user to provide the desired node set // ordering // colours.";
EXAMPLES
+ Example ("Convert a Desikan-Killiany parcellation image as provided by FreeSurfer to have nodes incrementing from 1",
"labelconvert aparc+aseg.mgz FreeSurferColorLUT.txt mrtrix3//share//mrtrix3//labelconvert//fs_default.txt nodes.mif",
"Paths to the files in the example above would need to be revised according to their "
"locations on the user's system.");
ARGUMENTS
+ Argument ("path_in", "the input image").type_image_in()
+ Argument ("lut_in", "the connectome lookup table corresponding to the input image").type_file_in()
+ Argument ("lut_out", "the target connectome lookup table for the output image").type_file_in()
+ Argument ("image_out", "the output image").type_image_out();
OPTIONS
+ Option ("spine", "provide a manually-defined segmentation of the base of the spine where the streamlines terminate, so that "
"this can become a node in the connection matrix.")
+ Argument ("image").type_image_in();
}
void run ()
{
// Open the input file
auto H = Header::open (argument[0]);
Connectome::check (H);
auto in = H.get_image<node_t>();
// Load the lookup tables
LUT lut_in (argument[1]), lut_out (argument[2]);
// Build the mapping from input to output indices
const auto mapping = get_lut_mapping (lut_in, lut_out);
// Modify the header for the output file
H.datatype() = DataType::from<node_t>();
add_line (H.keyval()["comments"], "LUT: " + Path::basename (argument[2]));
// Create the output file
auto out = Image<node_t>::create (argument[3], H);
// Fill the output image with data
bool user_warn = false;
for (auto l = Loop (in) (in, out); l; ++l) {
const node_t orig = in.value();
if (orig < mapping.size())
out.value() = mapping[orig];
else
user_warn = true;
}
if (user_warn)
WARN ("Unexpected values detected in input image; suggest checking input image thoroughly");
// Need to manually search through the output LUT to see if the
// 'Spinal_column' node is in there, and appears only once
node_t spine_index = 0;
bool duplicates = false;
for (const auto& i : lut_out) {
if (i.second.get_name() == SPINE_NODE_NAME) {
if (!spine_index)
spine_index = i.first;
else
duplicates = true;
}
}
// If the spine segment option has been provided, add this retrospectively
auto opt = get_options ("spine");
if (opt.size()) {
if (duplicates)
throw Exception ("Cannot add spine node: \"" + SPINE_NODE_NAME + "\" appears multiple times in output LUT");
if (!spine_index)
throw Exception ("Cannot add spine node: \"" + SPINE_NODE_NAME + "\" not present in output LUT");
auto in_spine = Image<bool>::open (opt[0][0]);
if (dimensions_match (in_spine, out)) {
for (auto l = Loop (in_spine) (in_spine, out); l; ++l) {
if (in_spine.value())
out.value() = spine_index;
}
} else {
WARN ("Spine node is being created from the mask image provided using -spine option using nearest-neighbour interpolation;");
WARN ("recommend using the parcellation image as the basis for this mask so that interpolation is not required");
Transform transform (out);
Interp::Nearest<decltype(in_spine)> nearest (in_spine);
for (auto l = Loop (out) (out); l; ++l) {
Eigen::Vector3d p (out.index (0), out.index (1), out.index (2));
p = transform.voxel2scanner * p;
if (nearest.scanner (p) && nearest.value())
out.value() = spine_index;
}
}
} else if (spine_index) {
WARN ("Config file includes \"" + SPINE_NODE_NAME + "\" node, but user has not provided the segmentation using -spine option");
}
}
|