47 QList<QSet<int>> adj(nVerts);
49 for (
int t = 0; t < tris.rows(); ++t) {
54 if (v0 >= 0 && v0 < nVerts && v1 >= 0 && v1 < nVerts && v2 >= 0 && v2 < nVerts) {
76 const int nVerts =
static_cast<int>(surface.
rr().rows());
81 for (
int i = 0; i < label.
vertices.size(); ++i)
84 QSet<int> allVerts = current;
87 for (
int step = 0; step < nSteps; ++step) {
89 for (
int v : current) {
90 if (v >= 0 && v < nVerts) {
91 for (
int neighbor : adj[v]) {
92 if (!allVerts.contains(neighbor))
93 frontier.insert(neighbor);
97 allVerts.unite(frontier);
100 if (frontier.isEmpty())
105 QList<int> sortedVerts(allVerts.begin(), allVerts.end());
106 std::sort(sortedVerts.begin(), sortedVerts.end());
110 result.
name = label.
name +
"_grown";
111 result.
vertices.resize(sortedVerts.size());
112 result.
pos.resize(sortedVerts.size(), 3);
113 result.
values = VectorXd::Ones(sortedVerts.size());
115 for (
int i = 0; i < sortedVerts.size(); ++i) {
116 result.
vertices[i] = sortedVerts[i];
117 if (sortedVerts[i] < nVerts)
118 result.
pos.row(i) = surface.
rr().row(sortedVerts[i]);
127 const QVector<int>& seeds,
133 const MatrixX3f& rr = surface.
rr();
134 const int nVerts =
static_cast<int>(rr.rows());
138 QVector<QVector<QPair<int, double>>> graph(nVerts);
139 for (
int v = 0; v < nVerts; ++v) {
140 QList<int> neighbours(adjacency[v].begin(), adjacency[v].end());
141 std::sort(neighbours.begin(), neighbours.end());
142 for (
int n : neighbours)
143 graph[v].append({n, 1000.0 * (rr.row(n) - rr.row(v)).cast<
double>().norm()});
146 QHash<int, double> distMap;
147 QVector<int> addedLast;
148 for (
int seed : seeds) {
149 if (seed < 0 || seed >= nVerts)
151 distMap.insert(seed, 0.0);
152 addedLast.append(seed);
154 while (!addedLast.isEmpty()) {
156 for (
int i : std::as_const(addedLast)) {
157 const double vDist = distMap.value(i);
158 for (
const auto& [j, d] : std::as_const(graph[i])) {
159 const double nDist = vDist + d;
160 const auto it = distMap.find(j);
161 if (it != distMap.end()) {
162 if (nDist < it.value())
164 }
else if (nDist <= extentMm) {
165 distMap.insert(j, nDist);
173 QList<int> verts = distMap.keys();
174 std::sort(verts.begin(), verts.end());
177 const QString suffix = hemi == 0 ? QStringLiteral(
"lh") : QStringLiteral(
"rh");
178 label.
name = name.endsWith(suffix) ? name : name +
"-" + suffix;
179 label.
vertices.resize(verts.size());
180 label.
pos.resize(verts.size(), 3);
181 label.
values.resize(verts.size());
182 for (
int k = 0; k < verts.size(); ++k) {
184 label.
pos.row(k) = rr.row(verts[k]);
185 label.
values[k] = std::trunc(distMap.value(verts[k]));
195 QList<FsLabel> components;
201 QHash<int, int> rowOf;
202 for (
int i = 0; i < label.
vertices.size(); ++i)
206 const int nVerts = surface.
isEmpty() ? 0 :
static_cast<int>(surface.
rr().rows());
209 QList<QList<int>> parts;
210 for (
int i = 0; i < label.
vertices.size(); ++i) {
212 if (visited.contains(seed))
216 visited.insert(seed);
218 while (!queue.isEmpty()) {
219 const int v = queue.dequeue();
221 if (v >= 0 && v < nVerts) {
222 for (
int neighbor : adj[v]) {
223 if (rowOf.contains(neighbor) && !visited.contains(neighbor)) {
224 visited.insert(neighbor);
225 queue.enqueue(neighbor);
230 std::sort(part.begin(), part.end());
235 std::stable_sort(parts.begin(), parts.end(), [](
const QList<int>& a,
const QList<int>& b) { return a.size() > b.size(); });
236 const bool hemiSuffix = label.
name.endsWith(QStringLiteral(
"lh")) || label.
name.endsWith(QStringLiteral(
"rh"));
237 const QString base = hemiSuffix ? label.
name.left(label.
name.size() - 3) : label.
name;
238 const QString ext = hemiSuffix ? label.
name.right(3) : QString();
240 for (
int p = 0; p < parts.size(); ++p) {
241 const QList<int>& part = parts[p];
244 comp.
name = QStringLiteral(
"%1_div%2%3").arg(base).arg(p + 1).arg(ext);
246 comp.
pos.resize(part.size(), 3);
247 comp.
values.resize(part.size());
248 for (
int j = 0; j < part.size(); ++j) {
249 const int row = rowOf.value(part[j]);
251 comp.
pos.row(j) = label.
pos.row(row);
254 components.append(comp);
263 const VectorXi& vertices,
268 QList<FsLabel> labels;
270 if (stcData.size() == 0 || vertices.size() == 0 || surface.
isEmpty())
274 VectorXd maxAbs = stcData.cwiseAbs().rowwise().maxCoeff();
276 QSet<int> aboveThresh;
277 for (
int i = 0; i < vertices.size(); ++i) {
278 if (maxAbs[i] > dThreshold)
279 aboveThresh.insert(vertices[i]);
282 if (aboveThresh.isEmpty())
287 fullLabel.
hemi = iHemi;
288 fullLabel.
name =
"stc_label";
290 QList<int> sortedVerts(aboveThresh.begin(), aboveThresh.end());
291 std::sort(sortedVerts.begin(), sortedVerts.end());
293 const int nSurfVerts =
static_cast<int>(surface.
rr().rows());
294 fullLabel.
vertices.resize(sortedVerts.size());
295 fullLabel.
pos.resize(sortedVerts.size(), 3);
296 fullLabel.
values.resize(sortedVerts.size());
298 for (
int i = 0; i < sortedVerts.size(); ++i) {
299 fullLabel.
vertices[i] = sortedVerts[i];
300 if (sortedVerts[i] < nSurfVerts)
301 fullLabel.
pos.row(i) = surface.
rr().row(sortedVerts[i]);
304 for (
int j = 0; j < vertices.size(); ++j) {
305 if (vertices[j] == sortedVerts[i]) {
306 fullLabel.
values[i] = maxAbs[j];
321 const VectorXi& stcVertices,
324 const int nVerts =
static_cast<int>(stcVertices.size());
325 MatrixXd mask = MatrixXd::Zero(nVerts, nTimes);
328 QMap<int, int> vertToRow;
329 for (
int i = 0; i < nVerts; ++i)
330 vertToRow.insert(stcVertices[i], i);
332 for (
const auto& label : labels) {
333 for (
int i = 0; i < label.vertices.size(); ++i) {
334 auto it = vertToRow.find(label.vertices[i]);
335 if (it != vertToRow.end()) {
336 mask.row(it.value()).setOnes();
Surface-mesh label manipulation: grow, split into connected components, STC ↔ label conversion.
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
A FreeSurfer/MNE surface label: per-vertex indices, Tk-RAS positions and scalar values for one hemisp...
static QList< QSet< int > > buildAdjacency(const Eigen::MatrixX3i &tris, int nVerts)
Build adjacency list from surface triangle mesh.
static FsLabel growCircularLabel(const FsSurface &surface, const QVector< int > &seeds, double extentMm, int hemi, const QString &name)
Grow a circular label around seed vertices, as mne.grow_labels does for one seed set.
static QList< FsLabel > splitLabel(const FsLabel &label, const FsSurface &surface)
Split a label into connected components.
static FsLabel growLabel(const FsLabel &label, const FsSurface &surface, int nSteps)
Grow a label by expanding along the surface mesh.
static Eigen::MatrixXd labelsToStc(const QList< FsLabel > &labels, const Eigen::VectorXi &stcVertices, int nTimes)
Convert labels to a binary source estimate mask.
static QList< FsLabel > stcToLabel(const Eigen::MatrixXd &stcData, const Eigen::VectorXi &vertices, const FsSurface &surface, double dThreshold=0.0, int iHemi=0)
Convert a source estimate to labels by thresholding.
In-memory FreeSurfer triangular cortical surface for one hemisphere.
const Eigen::MatrixX3i & tris() const
const Eigen::MatrixX3f & rr() const