void run () { Image::Buffer<float> dir_buf (argument[0]); Image::Buffer<float>::voxel_type dir_vox (dir_buf); Image::Header header (argument[0]); header.dim(3) = header.dim(3)/3; Image::Buffer<float> amp_buf (argument[1], header); Image::Buffer<float>::voxel_type amp_vox (amp_buf); Image::LoopInOrder loop (dir_vox, "converting directions to amplitudes...", 0, 3); for (loop.start (dir_vox, amp_vox); loop.ok(); loop.next (dir_vox, amp_vox)) { Math::Vector<float> dir (3); dir_vox[3] = 0; amp_vox[3] = 0; while (dir_vox[3] < dir_vox.dim(3)) { dir[0] = dir_vox.value(); ++dir_vox[3]; dir[1] = dir_vox.value(); ++dir_vox[3]; dir[2] = dir_vox.value(); ++dir_vox[3]; float amplitude = 0.0; if (std::isfinite (dir[0]) && std::isfinite (dir[1]) && std::isfinite (dir[2])) amplitude = Math::norm (dir); amp_vox.value() = amplitude; ++amp_vox[3]; } } }
void run () { Image::Buffer<node_t> nodes_data (argument[0]); Image::Buffer<node_t>::voxel_type nodes (nodes_data); Node_map node_map; load_lookup_table (node_map); Options opt = get_options ("config"); if (opt.size()) { if (node_map.empty()) throw Exception ("Cannot properly interpret connectome config file if no lookup table is provided"); ConfigInvLookup config; load_config (opt[0][0], config); // Now that we know the configuration, can convert the lookup table to reflect the new indices // If no corresponding entry exists in the config file, then the node doesn't get coloured Node_map new_node_map; for (Node_map::iterator i = node_map.begin(); i != node_map.end(); ++i) { ConfigInvLookup::const_iterator existing = config.find (i->second.get_name()); if (existing != config.end()) new_node_map.insert (std::make_pair (existing->second, i->second)); } if (new_node_map.empty()) throw Exception ("Config file and parcellation lookup table do not appear to belong to one another"); new_node_map.insert (std::make_pair (0, Node_info ("Unknown", Point<uint8_t> (0, 0, 0), 0))); node_map = new_node_map; } if (node_map.empty()) { INFO ("No lookup table provided; colouring nodes randomly"); node_t max_index = 0; Image::LoopInOrder loop (nodes); for (loop.start (nodes); loop.ok(); loop.next (nodes)) { const node_t index = nodes.value(); if (index > max_index) max_index = index; } node_map.insert (std::make_pair (0, Node_info ("None", 0, 0, 0, 0))); Math::RNG rng; for (node_t i = 1; i <= max_index; ++i) { Point<uint8_t> colour; do { colour[0] = rng.uniform_int (255); colour[1] = rng.uniform_int (255); colour[2] = rng.uniform_int (255); } while (int(colour[0]) + int(colour[1]) + int(colour[2]) < 100); node_map.insert (std::make_pair (i, Node_info (str(i), colour))); } } Image::Header H; H.info() = nodes_data.info(); H.set_ndim (4); H.dim(3) = 3; H.datatype() = DataType::UInt8; H.comments().push_back ("Coloured parcellation image generated by label2colour"); Image::Buffer<uint8_t> out_data (argument[1], H); Image::Buffer<uint8_t>::voxel_type out (out_data); Image::LoopInOrder loop (nodes, "Colourizing parcellated node image... "); for (loop.start (nodes, out); loop.ok(); loop.next (nodes, out)) { const node_t index = nodes.value(); Node_map::const_iterator i = node_map.find (index); if (i == node_map.end()) { out[3] = 0; out.value() = 0; out[3] = 1; out.value() = 0; out[3] = 2; out.value() = 0; } else { const Point<uint8_t>& colour (i->second.get_colour()); out[3] = 0; out.value() = colour[0]; out[3] = 1; out.value() = colour[1]; out[3] = 2; out.value() = colour[2]; } } }
void check_and_update (Image::Header& H, const conv_t conversion) { const size_t lmax = Math::SH::LforN (H.dim(3)); // Flag which volumes are m==0 and which are not const ssize_t N = H.dim(3); BitSet mzero_terms (N, false); for (size_t l = 2; l <= lmax; l += 2) mzero_terms[Math::SH::index (l, 0)] = true; // Open in read-write mode if there's a chance of modification typename Image::Buffer<value_type> buffer (H, (conversion != NONE)); typename Image::Buffer<value_type>::voxel_type v (buffer); // Need to mask out voxels where the DC term is zero Image::Info info_mask (H); info_mask.set_ndim (3); info_mask.datatype() = DataType::Bit; Image::BufferScratch<bool> mask (info_mask); Image::BufferScratch<bool>::voxel_type v_mask (mask); size_t voxel_count = 0; { Image::LoopInOrder loop (v, "Masking image based on DC term...", 0, 3); for (loop.start (v, v_mask); loop.ok(); loop.next (v, v_mask)) { const value_type value = v.value(); if (value && std::isfinite (value)) { v_mask.value() = true; ++voxel_count; } else { v_mask.value() = false; } } } // Get sums independently for each l // Each order has a different power, and a different number of m!=0 volumes. // Therefore, calculate the mean-square intensity for the m==0 and m!=0 // volumes independently, and report ratio for each harmonic order Ptr<ProgressBar> progress; if (App::log_level > 0 && App::log_level < 2) progress = new ProgressBar ("Evaluating SH basis of image \"" + H.name() + "\"...", N-1); std::vector<float> ratios; for (size_t l = 2; l <= lmax; l += 2) { double mzero_sum = 0.0, mnonzero_sum = 0.0; Image::LoopInOrder loop (v, 0, 3); for (v[3] = ssize_t (Math::SH::NforL(l-2)); v[3] != ssize_t (Math::SH::NforL(l)); ++v[3]) { double sum = 0.0; for (loop.start (v, v_mask); loop.ok(); loop.next (v, v_mask)) { if (v_mask.value()) sum += Math::pow2 (value_type(v.value())); } if (mzero_terms[v[3]]) { mzero_sum += sum; DEBUG ("Volume " + str(v[3]) + ", m==0, sum " + str(sum)); } else { mnonzero_sum += sum; DEBUG ("Volume " + str(v[3]) + ", m!=0, sum " + str(sum)); } if (progress) ++*progress; } const double mnonzero_MSoS = mnonzero_sum / (2.0 * l); const float power_ratio = mnonzero_MSoS/mzero_sum; ratios.push_back (power_ratio); INFO ("SH order " + str(l) + ", ratio of m!=0 to m==0 power: " + str(power_ratio) + ", m==0 power: " + str (mzero_sum)); } if (progress) progress = NULL; // First is ratio to be used for SH basis decision, second is gradient of regression std::pair<float, float> regression = std::make_pair (0.0f, 0.0f); size_t l_for_decision; float power_ratio; // The gradient will change depending on the current basis, so the threshold needs to also // The gradient is as a function of l, not of even orders float grad_threshold = 0.02; switch (lmax) { // Lmax == 2: only one order to use case 2: power_ratio = ratios.front(); l_for_decision = 2; break; // Lmax = 4: Use l=4 order to determine SH basis, can't check gradient since l=2 is untrustworthy case 4: power_ratio = ratios.back(); l_for_decision = 4; break; // Lmax = 6: Use l=4 order to determine SH basis, but checking the gradient is not reliable: // artificially double the threshold so the power ratio at l=6 needs to be substantially // different to l=4 to throw a warning case 6: regression = std::make_pair (ratios[1] - 2*(ratios[2]-ratios[1]), 0.5*(ratios[2]-ratios[1])); power_ratio = ratios[1]; l_for_decision = 4; grad_threshold *= 2.0; break; // Lmax >= 8: Do a linear regression from l=4 to l=lmax, project back to l=0 // (this is a more reliable quantification on poor data than l=4 alone) default: regression = get_regression (ratios); power_ratio = regression.first; l_for_decision = 0; break; } // If the gradient is in fact positive (i.e. power ration increases for larger l), use the // regression to pull the power ratio from l=lmax if (regression.second > 0.0) { l_for_decision = lmax; power_ratio = regression.first + (lmax * regression.second); } DEBUG ("Power ratio for assessing SH basis is " + str(power_ratio) + " as " + (lmax < 8 ? "derived from" : "regressed to") + " l=" + str(l_for_decision)); // Threshold to make decision on what basis the data are currently stored in value_type multiplier = 1.0; if ((power_ratio > (5.0/3.0)) && (power_ratio < (7.0/3.0))) { CONSOLE ("Image \"" + str(H.name()) + "\" appears to be in the old non-orthonormal basis"); switch (conversion) { case NONE: break; case OLD: break; case NEW: multiplier = 1.0 / M_SQRT2; break; case FORCE_OLDTONEW: multiplier = 1.0 / M_SQRT2; break; case FORCE_NEWTOOLD: WARN ("Refusing to convert image \"" + H.name() + "\" from new to old basis, as data appear to already be in the old non-orthonormal basis"); return; } grad_threshold *= 2.0; } else if ((power_ratio > (2.0/3.0)) && (power_ratio < (4.0/3.0))) { CONSOLE ("Image \"" + str(H.name()) + "\" appears to be in the new orthonormal basis"); switch (conversion) { case NONE: break; case OLD: multiplier = M_SQRT2; break; case NEW: break; case FORCE_OLDTONEW: WARN ("Refusing to convert image \"" + H.name() + "\" from old to new basis, as data appear to already be in the new orthonormal basis"); return; case FORCE_NEWTOOLD: multiplier = M_SQRT2; break; } } else { multiplier = 0.0; WARN ("Cannot make unambiguous decision on SH basis of image \"" + H.name() + "\" (power ratio " + (lmax < 8 ? "in" : "regressed to") + " " + str(l_for_decision) + " is " + str(power_ratio) + ")"); if (conversion == FORCE_OLDTONEW) { WARN ("Forcing conversion of image \"" + H.name() + "\" from old to new SH basis on user request; however NO GUARANTEE IS PROVIDED on appropriateness of this conversion!"); multiplier = 1.0 / M_SQRT2; } else if (conversion == FORCE_NEWTOOLD) { WARN ("Forcing conversion of image \"" + H.name() + "\" from new to old SH basis on user request; however NO GUARANTEE IS PROVIDED on appropriateness of this conversion!"); multiplier = M_SQRT2; } } // Decide whether the user needs to be warned about a poor diffusion encoding scheme if (regression.second) DEBUG ("Gradient of regression is " + str(regression.second) + "; threshold is " + str(grad_threshold)); if (Math::abs(regression.second) > grad_threshold) { WARN ("Image \"" + H.name() + "\" may have been derived from poor directional encoding, or have some other underlying data problem"); WARN ("(m!=0 to m==0 power ratio changing by " + str(2.0*regression.second) + " per even order)"); } // Adjust the image data in-place if necessary if (multiplier && (multiplier != 1.0)) { Image::LoopInOrder loop (v, 0, 3); ProgressBar progress ("Modifying SH basis of image \"" + H.name() + "\"...", N-1); for (v[3] = 1; v[3] != N; ++v[3]) { if (!mzero_terms[v[3]]) { for (loop.start (v); loop.ok(); loop.next (v)) v.value() *= multiplier; } ++progress; } } else if (multiplier && (conversion != NONE)) { INFO ("Image \"" + H.name() + "\" already in desired basis; nothing to do"); } }
void run () { Image::Header input_header (argument[0]); const size_t filter_index = argument[1]; // Separate code path for FFT filter if (!filter_index) { // Gets preloaded by the FFT filter anyways, so just use a buffer // FIXME Had to use cdouble throughout; seems to fail at compile time even trying to // convert between cfloat and cdouble... Image::Buffer<cdouble> input_data (input_header); Image::Buffer<cdouble>::voxel_type input_voxel (input_data); Image::Filter::FFT* filter = (dynamic_cast<Image::Filter::FFT*> (create_fft_filter (input_voxel))); Image::Header header; header.info() = filter->info(); set_strides (header); filter->set_message (std::string("applying FFT filter to image " + std::string(argument[0]) + "...")); if (get_options ("magnitude").size()) { Image::BufferScratch<cdouble> temp_data (header, "complex FFT result"); Image::BufferScratch<cdouble>::voxel_type temp_voxel (temp_data); (*filter) (input_voxel, temp_voxel); header.datatype() = DataType::Float32; Image::Buffer<float> output_data (argument[2], header); Image::Buffer<float>::voxel_type output_voxel (output_data); Image::LoopInOrder loop (output_voxel); for (loop.start (temp_voxel, output_voxel); loop.ok(); loop.next (temp_voxel, output_voxel)) output_voxel.value() = std::abs (cdouble(temp_voxel.value())); } else { Image::Buffer<cdouble> output_data (argument[2], header); Image::Buffer<cdouble>::voxel_type output_voxel (output_data); (*filter) (input_voxel, output_voxel); } delete filter; return; } Image::BufferPreload<float> input_data (input_header); Image::BufferPreload<float>::voxel_type input_voxel (input_data); Image::Filter::Base* filter = NULL; switch (filter_index) { case 0: assert (0); break; case 1: filter = create_gradient_filter (input_voxel); break; case 2: filter = create_median_filter (input_voxel); break; case 3: filter = create_smooth_filter (input_voxel); break; default: assert (0); } Image::Header header; header.info() = filter->info(); set_strides (header); Image::Buffer<float> output_data (argument[2], header); Image::Buffer<float>::voxel_type output_voxel (output_data); filter->set_message (std::string("applying ") + std::string(argument[1]) + " filter to image " + std::string(argument[0]) + "..."); switch (filter_index) { case 0: assert (0); break; case 1: (*dynamic_cast<Image::Filter::Gradient*> (filter)) (input_voxel, output_voxel); break; case 2: (*dynamic_cast<Image::Filter::Median*> (filter)) (input_voxel, output_voxel); break; case 3: (*dynamic_cast<Image::Filter::Smooth*> (filter)) (input_voxel, output_voxel); break; } delete filter; }
void run () { Point<> p[3]; for (size_t arg = 0; arg < 3; ++arg) { std::vector<float> V = argument[arg]; if (V.size() != 3) throw Exception ("coordinates must contain 3 elements"); p[arg][0] = V[0]; p[arg][1] = V[1]; p[arg][2] = V[2]; } Image::Header header (argument[3]); float vox = Math::pow (header.vox(0)*header.vox(1)*header.vox(2), 1.0f/3.0f); Options opt = get_options ("vox"); if (opt.size()) vox = opt[0][0]; Point<> d1 = p[1] - p[0]; Point<> d2 = p[2] - p[0]; d1.normalise(); d2.normalise(); d2 = d2 - d1 * d1.dot(d2); d2.normalise(); Point<> d3 = d1.cross(d2); float min1 = std::numeric_limits<float>::infinity(); float min2 = std::numeric_limits<float>::infinity(); float max1 = -std::numeric_limits<float>::infinity(); float max2 = -std::numeric_limits<float>::infinity(); update_bounds (min1, min2, max1, max2, header, Point<int>(0,0,0), 0, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(0,0,0), 1, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(0,0,0), 2, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(1,0,0), 1, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(1,0,0), 2, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(0,1,0), 0, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(0,1,0), 2, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(0,0,1), 0, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(0,0,1), 1, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(1,1,0), 2, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(1,0,1), 1, p[0], d1, d2); update_bounds (min1, min2, max1, max2, header, Point<int>(0,1,1), 0, p[0], d1, d2); Point<> translation (p[0] + min1*d1 + min2*d2); header.transform()(0,0) = d1[0]; header.transform()(1,0) = d1[1]; header.transform()(2,0) = d1[2]; header.transform()(0,1) = d2[0]; header.transform()(1,1) = d2[1]; header.transform()(2,1) = d2[2]; header.transform()(0,2) = d3[0]; header.transform()(1,2) = d3[1]; header.transform()(2,2) = d3[2]; header.transform()(0,3) = translation[0]; header.transform()(1,3) = translation[1]; header.transform()(2,3) = translation[2]; header.set_ndim (3); header.dim (0) = (max1-min1) / vox; header.dim (1) = (max2-min2) / vox; header.dim (2) = 1; header.vox(0) = header.vox(1) = header.vox(2) = vox; header.datatype() = DataType::Bit; header.DW_scheme().clear(); Image::Buffer<bool> roi_buffer (argument[4], header); Image::Buffer<bool>::voxel_type roi (roi_buffer); Image::LoopInOrder loop (roi); for (loop.start (roi); loop.ok(); loop.next (roi)) roi.value() = true; }