3Copyright Université Paris XI (2014).
5Contributor: Yann Leprince <yann.leprince@ylep.fr>.
7This file is part of highres-cortex, a collection of software designed
8to process high-resolution magnetic resonance images of the cerebral
11This software is governed by the CeCILL licence under French law and
12abiding by the rules of distribution of free software. You can use,
13modify and/or redistribute the software under the terms of the CeCILL
14licence as circulated by CEA, CNRS and INRIA at the following URL:
15<http://www.cecill.info/>.
17As a counterpart to the access to the source code and rights to copy,
18modify and redistribute granted by the licence, users are provided only
19with a limited warranty and the software's author, the holder of the
20economic rights, and the successive licensors have only limited
23In this respect, the user's attention is drawn to the risks associated
24with loading, using, modifying and/or developing or reproducing the
25software by the user in light of its specific status of scientific
26software, that may mean that it is complicated to manipulate, and that
27also therefore means that it is reserved for developers and experienced
28professionals having in-depth computer knowledge. Users are therefore
29encouraged to load and test the software's suitability as regards their
30requirements in conditions enabling the security of their systems and/or
31data to be ensured and, more generally, to use and operate it in the
32same conditions as regards security.
34The fact that you are presently reading this means that you have had
35knowledge of the CeCILL licence and that you accept its terms.
38#include "cortex_column_region_quality.hh"
55// ----------------------------------------------------------------------------
56// Numerical diagonalization of 3x3 matrcies
57// Copyright (C) 2006 Joachim Kopp
58// ----------------------------------------------------------------------------
59// This library is free software; you can redistribute it and/or
60// modify it under the terms of the GNU Lesser General Public
61// License as published by the Free Software Foundation; either
62// version 2.1 of the License, or (at your option) any later version.
64// This library is distributed in the hope that it will be useful,
65// but WITHOUT ANY WARRANTY; without even the implied warranty of
66// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
67// Lesser General Public License for more details.
69// You should have received a copy of the GNU Lesser General Public
70// License along with this library; if not, write to the Free Software
71// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
72// ----------------------------------------------------------------------------
73void dsyevc3(const double A[3][3], double w[3])
74// ----------------------------------------------------------------------------
75// Calculates the eigenvalues of a symmetric 3x3 matrix A using Cardano's
76// analytical algorithm.
77// Only the diagonal and upper triangular parts of A are accessed. The access
79// ----------------------------------------------------------------------------
81// A: The symmetric input matrix
82// w: Storage buffer for eigenvalues
83// ----------------------------------------------------------------------------
85 static const double SQRT3 = 1.73205080756887729352744634151;
88 // Determine coefficients of characteristic poynomial. We write
92 double de = A[0][1] * A[1][2]; // d * e
93 double dd = square(A[0][1]); // d^2
94 double ee = square(A[1][2]); // e^2
95 double ff = square(A[0][2]); // f^2
96 m = A[0][0] + A[1][1] + A[2][2];
97 c1 = (A[0][0] * A[1][1] + A[0][0] * A[2][2] + A[1][1] * A[2][2])
98 - (dd + ee + ff); // a*b + a*c + b*c - d^2 - e^2 - f^2
99 c0 = A[2][2] * dd + A[0][0] * ee + A[1][1] * ff - A[0][0] * A[1][1] * A[2][2]
100 - 2.0 * A[0][2]*de; // c*d^2 + a*e^2 + b*f^2 - a*b*c - 2*f*d*e)
102 double p, sqrt_p, q, c, s, phi;
103 p = square(m) - 3.0 * c1;
104 q = m * (p - (3.0 / 2.0) * c1) - (27.0 / 2.0) * c0;
105 sqrt_p = std::sqrt(std::fabs(p));
107 phi = 27.0 * (0.25 * square(c1) * (p - c1) + c0 * (q + 27.0 / 4.0 * c0));
108 phi = (1.0 / 3.0) * std::atan2(std::sqrt(std::fabs(phi)), q);
110 c = sqrt_p * std::cos(phi);
111 s = (1.0 / SQRT3) * sqrt_p * std::sin(phi);
113 w[1] = (1.0 / 3.0) * (m - c);
119inline bool proj_is_valid(float xproj, float /*unused*/, float /*unused*/)
121 // Invalid values are (-1, -1, -1), and no valid coordinate is supposed to be
126float large_eigenvalue(const yl::MomentAccumulator& mom)
131 double centred_moment_matrix[3][3];
133 // Only the lower triangular part is referenced
134 centred_moment_matrix[0][0]
135 = (mom.m_200 - mom.m_100 * mom.m_100 / mom.m_000) / mom.m_000;
136 centred_moment_matrix[0][1]
137 = (mom.m_110 - mom.m_100 * mom.m_010 / mom.m_000) / mom.m_000;
138 centred_moment_matrix[1][1]
139 = (mom.m_020 - mom.m_010 * mom.m_010 / mom.m_000) / mom.m_000;
140 centred_moment_matrix[0][2]
141 = (mom.m_101 - mom.m_100 * mom.m_001 / mom.m_000) / mom.m_000;
142 centred_moment_matrix[1][2]
143 = (mom.m_011 - mom.m_010 * mom.m_001 / mom.m_000) / mom.m_000;
144 centred_moment_matrix[2][2]
145 = (mom.m_002 - mom.m_001 * mom.m_001 / mom.m_000) / mom.m_000;
147 double eigenvalues[3];
148 // Compute the eigenvalues of the centred moment matrix
149 dsyevc3(centred_moment_matrix, eigenvalues);
151 return static_cast<float>(
152 std::max(0., *std::max_element(eigenvalues, eigenvalues + 3)));
155template <class BaseIterator>
157 : public boost::iterator_adaptor<
158 ChainedIterator<BaseIterator>,
161 boost::forward_traversal_tag>
165 : ChainedIterator::iterator_adaptor_(0) {}
167 ChainedIterator(const BaseIterator& begin_first,
168 const BaseIterator& end_first,
169 const BaseIterator& begin_second)
170 : ChainedIterator::iterator_adaptor_(begin_first),
171 m_end_first(end_first),
172 m_begin_second(begin_second) {}
174 explicit ChainedIterator(const BaseIterator& begin)
175 : ChainedIterator::iterator_adaptor_(begin),
181 const BaseIterator m_end_first, m_begin_second;
183 friend class boost::iterator_core_access;
186 BaseIterator& base = this->base_reference();
188 if(base == m_end_first && m_end_first != BaseIterator())
189 base = m_begin_second;
193} // end of anonymous namespace
199float CortexColumnRegionQuality::fusion_ordering(const Cache& cache) const
201 const std::size_t region_size = cache.region_size();
206bool CortexColumnRegionQuality::want_fusion(const Cache& cache) const
208 return pseudo_area(cache) < m_pseudo_area_cutoff;
211float CortexColumnRegionQuality::
212pseudo_area(const Cache& cache) const
214 const yl::MomentAccumulator& CSF_moment = cache.CSF_moments();
215 const yl::MomentAccumulator& white_moment = cache.white_moments();
217 // The moments are calculated based on the projected point cloud, thus the
218 // thicker regions lead to a denser point cloud and weigh more. Is this good?
220 const float CSF_large_eigenval = large_eigenvalue(CSF_moment);
221 const float white_large_eigenval = large_eigenvalue(white_moment);
223 return CSF_large_eigenval + white_large_eigenval;
226template <class PointIterator>
227CortexColumnRegionQuality::Cache CortexColumnRegionQuality::
228cache(const PointIterator& point_it_begin,
229 const PointIterator& point_it_end) const
232 yl::MomentAccumulator& CSF_moment = cache.CSF_moments();
233 yl::MomentAccumulator& white_moment = cache.white_moments();
234 std::size_t& region_size = cache.region_size();
235 bool& touches_CSF = cache.touches_CSF();
236 bool& touches_white = cache.touches_white();
238 for(PointIterator point_it = point_it_begin;
239 point_it != point_it_end;
242 const Point3d& point = *point_it;
243 const int x = point[0];
244 const int y = point[1];
245 const int z = point[2];
250 const float xproj = m_CSF_projections.at(x, y, z, 0);
251 const float yproj = m_CSF_projections.at(x, y, z, 1);
252 const float zproj = m_CSF_projections.at(x, y, z, 2);
253 if(proj_is_valid(xproj, yproj, zproj)) {
254 CSF_moment.update(xproj, yproj, zproj);
259 const float xproj = m_white_projections.at(x, y, z, 0);
260 const float yproj = m_white_projections.at(x, y, z, 1);
261 const float zproj = m_white_projections.at(x, y, z, 2);
262 if(proj_is_valid(xproj, yproj, zproj)) {
263 white_moment.update(xproj, yproj, zproj);
267 const int16_t voxel_classif = m_classif.at(x, y, z);
268 if(voxel_classif == CSF_LABEL)
270 else if(voxel_classif == WHITE_LABEL)
271 touches_white = true;
272 else if(voxel_classif != CORTEX_LABEL)
273 std::clog << "WARNING: CortexColumnRegionQuality: unknown classif label "
274 << voxel_classif << std::endl;
280template <typename Tlabel>
281CortexColumnRegionQuality::Cache CortexColumnRegionQuality::
282cache(const LabelVolume<Tlabel>& label_vol, const Tlabel label) const
284 return cache(label_vol.region_begin(label),
285 label_vol.region_end(label));
288template <class PointIterator>
289float CortexColumnRegionQuality::
290fusion_ordering(const PointIterator& point_it_begin,
291 const PointIterator& point_it_end) const
293 return fusion_ordering(cache(point_it_begin, point_it_end));
296template <typename Tlabel>
297float CortexColumnRegionQuality::
298fusion_ordering(const LabelVolume<Tlabel>& label_vol, const Tlabel label) const
300 return fusion_ordering(label_vol.region_begin(label),
301 label_vol.region_end(label));
304template <typename Tlabel>
305float CortexColumnRegionQuality::
306fusion_ordering(const LabelVolume<Tlabel>& label_vol,
308 const Tlabel label2) const
310 typedef ChainedIterator<typename LabelVolume<Tlabel>::const_point_iterator>
311 ChainedPointIterator;
312 return fusion_ordering(
313 ChainedPointIterator(label_vol.region_begin(label1),
314 label_vol.region_end(label1),
315 label_vol.region_begin(label2)),
316 ChainedPointIterator(label_vol.region_end(label2)));