highres-cortex 6.0.4
cortex_column_region_quality.tcc
Go to the documentation of this file.
1/*
2Copyright CEA (2014).
3Copyright Université Paris XI (2014).
4
5Contributor: Yann Leprince <yann.leprince@ylep.fr>.
6
7This file is part of highres-cortex, a collection of software designed
8to process high-resolution magnetic resonance images of the cerebral
9cortex.
10
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/>.
16
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
21liability.
22
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.
33
34The fact that you are presently reading this means that you have had
35knowledge of the CeCILL licence and that you accept its terms.
36*/
37
38#include "cortex_column_region_quality.hh"
39
40#include <cmath>
41#include <limits>
42#include <algorithm>
43
44#include "cortex.hh"
45
46namespace
47{
48
49template <typename T>
50inline T square(T x)
51{
52 return x * x;
53}
54
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.
63//
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.
68//
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
78// is read-only.
79// ----------------------------------------------------------------------------
80// Parameters:
81// A: The symmetric input matrix
82// w: Storage buffer for eigenvalues
83// ----------------------------------------------------------------------------
84{
85 static const double SQRT3 = 1.73205080756887729352744634151;
86 double m, c1, c0;
87
88 // Determine coefficients of characteristic poynomial. We write
89 // | a d f |
90 // A = | d* b e |
91 // | f* e* c |
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)
101
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));
106
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);
109
110 c = sqrt_p * std::cos(phi);
111 s = (1.0 / SQRT3) * sqrt_p * std::sin(phi);
112
113 w[1] = (1.0 / 3.0) * (m - c);
114 w[2] = w[1] + s;
115 w[0] = w[1] + c;
116 w[1] -= s;
117}
118
119inline bool proj_is_valid(float xproj, float /*unused*/, float /*unused*/)
120{
121 // Invalid values are (-1, -1, -1), and no valid coordinate is supposed to be
122 // negative
123 return xproj >= 0;
124}
125
126float large_eigenvalue(const yl::MomentAccumulator& mom)
127{
128 if(mom.m_000 == 0)
129 return 0.f;
130
131 double centred_moment_matrix[3][3];
132
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;
146
147 double eigenvalues[3];
148 // Compute the eigenvalues of the centred moment matrix
149 dsyevc3(centred_moment_matrix, eigenvalues);
150
151 return static_cast<float>(
152 std::max(0., *std::max_element(eigenvalues, eigenvalues + 3)));
153}
154
155template <class BaseIterator>
156class ChainedIterator
157 : public boost::iterator_adaptor<
158 ChainedIterator<BaseIterator>,
159 BaseIterator,
160 boost::use_default,
161 boost::forward_traversal_tag>
162{
163public:
164 ChainedIterator()
165 : ChainedIterator::iterator_adaptor_(0) {}
166
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) {}
173
174 explicit ChainedIterator(const BaseIterator& begin)
175 : ChainedIterator::iterator_adaptor_(begin),
176 m_end_first(),
177 m_begin_second() {}
178
179
180private:
181 const BaseIterator m_end_first, m_begin_second;
182
183 friend class boost::iterator_core_access;
184 void increment()
185 {
186 BaseIterator& base = this->base_reference();
187 ++base;
188 if(base == m_end_first && m_end_first != BaseIterator())
189 base = m_begin_second;
190 }
191};
192
193} // end of anonymous namespace
194
195
196namespace yl
197{
198
199float CortexColumnRegionQuality::fusion_ordering(const Cache& cache) const
200{
201 const std::size_t region_size = cache.region_size();
202
203 return region_size;
204}
205
206bool CortexColumnRegionQuality::want_fusion(const Cache& cache) const
207{
208 return pseudo_area(cache) < m_pseudo_area_cutoff;
209}
210
211float CortexColumnRegionQuality::
212pseudo_area(const Cache& cache) const
213{
214 const yl::MomentAccumulator& CSF_moment = cache.CSF_moments();
215 const yl::MomentAccumulator& white_moment = cache.white_moments();
216
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?
219
220 const float CSF_large_eigenval = large_eigenvalue(CSF_moment);
221 const float white_large_eigenval = large_eigenvalue(white_moment);
222
223 return CSF_large_eigenval + white_large_eigenval;
224}
225
226template <class PointIterator>
227CortexColumnRegionQuality::Cache CortexColumnRegionQuality::
228cache(const PointIterator& point_it_begin,
229 const PointIterator& point_it_end) const
230{
231 Cache cache;
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();
237
238 for(PointIterator point_it = point_it_begin;
239 point_it != point_it_end;
240 ++point_it)
241 {
242 const Point3d& point = *point_it;
243 const int x = point[0];
244 const int y = point[1];
245 const int z = point[2];
246
247 ++region_size;
248
249 {
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);
255 }
256 }
257
258 {
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);
264 }
265 }
266
267 const int16_t voxel_classif = m_classif.at(x, y, z);
268 if(voxel_classif == CSF_LABEL)
269 touches_CSF = true;
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;
275 }
276
277 return cache;
278}
279
280template <typename Tlabel>
281CortexColumnRegionQuality::Cache CortexColumnRegionQuality::
282cache(const LabelVolume<Tlabel>& label_vol, const Tlabel label) const
283{
284 return cache(label_vol.region_begin(label),
285 label_vol.region_end(label));
286}
287
288template <class PointIterator>
289float CortexColumnRegionQuality::
290fusion_ordering(const PointIterator& point_it_begin,
291 const PointIterator& point_it_end) const
292{
293 return fusion_ordering(cache(point_it_begin, point_it_end));
294}
295
296template <typename Tlabel>
297float CortexColumnRegionQuality::
298fusion_ordering(const LabelVolume<Tlabel>& label_vol, const Tlabel label) const
299{
300 return fusion_ordering(label_vol.region_begin(label),
301 label_vol.region_end(label));
302}
303
304template <typename Tlabel>
305float CortexColumnRegionQuality::
306fusion_ordering(const LabelVolume<Tlabel>& label_vol,
307 const Tlabel label1,
308 const Tlabel label2) const
309{
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)));
317}
318
319}; // namespace yl