46 QList<QSet<int>> adj(nVerts);
48 for (
int t = 0; t < tris.rows(); ++t) {
53 if (v0 >= 0 && v0 < nVerts && v1 >= 0 && v1 < nVerts && v2 >= 0 && v2 < nVerts) {
75 const int nVerts =
static_cast<int>(surface.
rr().rows());
80 for (
int i = 0; i < label.
vertices.size(); ++i)
83 QSet<int> allVerts = current;
86 for (
int step = 0; step < nSteps; ++step) {
88 for (
int v : current) {
89 if (v >= 0 && v < nVerts) {
90 for (
int neighbor : adj[v]) {
91 if (!allVerts.contains(neighbor))
92 frontier.insert(neighbor);
96 allVerts.unite(frontier);
99 if (frontier.isEmpty())
104 QList<int> sortedVerts(allVerts.begin(), allVerts.end());
105 std::sort(sortedVerts.begin(), sortedVerts.end());
109 result.
name = label.
name +
"_grown";
110 result.
vertices.resize(sortedVerts.size());
111 result.
pos.resize(sortedVerts.size(), 3);
112 result.
values = VectorXd::Ones(sortedVerts.size());
114 for (
int i = 0; i < sortedVerts.size(); ++i) {
115 result.
vertices[i] = sortedVerts[i];
116 if (sortedVerts[i] < nVerts)
117 result.
pos.row(i) = surface.
rr().row(sortedVerts[i]);
128 QList<FsLabel> components;
134 QHash<int, int> rowOf;
135 for (
int i = 0; i < label.
vertices.size(); ++i)
139 const int nVerts = surface.
isEmpty() ? 0 :
static_cast<int>(surface.
rr().rows());
142 QList<QList<int>> parts;
143 for (
int i = 0; i < label.
vertices.size(); ++i) {
145 if (visited.contains(seed))
149 visited.insert(seed);
151 while (!queue.isEmpty()) {
152 const int v = queue.dequeue();
154 if (v >= 0 && v < nVerts) {
155 for (
int neighbor : adj[v]) {
156 if (rowOf.contains(neighbor) && !visited.contains(neighbor)) {
157 visited.insert(neighbor);
158 queue.enqueue(neighbor);
163 std::sort(part.begin(), part.end());
168 std::stable_sort(parts.begin(), parts.end(), [](
const QList<int>& a,
const QList<int>& b) { return a.size() > b.size(); });
169 const bool hemiSuffix = label.
name.endsWith(QStringLiteral(
"lh")) || label.
name.endsWith(QStringLiteral(
"rh"));
170 const QString base = hemiSuffix ? label.
name.left(label.
name.size() - 3) : label.
name;
171 const QString ext = hemiSuffix ? label.
name.right(3) : QString();
173 for (
int p = 0; p < parts.size(); ++p) {
174 const QList<int>& part = parts[p];
177 comp.
name = QStringLiteral(
"%1_div%2%3").arg(base).arg(p + 1).arg(ext);
179 comp.
pos.resize(part.size(), 3);
180 comp.
values.resize(part.size());
181 for (
int j = 0; j < part.size(); ++j) {
182 const int row = rowOf.value(part[j]);
184 comp.
pos.row(j) = label.
pos.row(row);
187 components.append(comp);
196 const VectorXi& vertices,
201 QList<FsLabel> labels;
203 if (stcData.size() == 0 || vertices.size() == 0 || surface.
isEmpty())
207 VectorXd maxAbs = stcData.cwiseAbs().rowwise().maxCoeff();
209 QSet<int> aboveThresh;
210 for (
int i = 0; i < vertices.size(); ++i) {
211 if (maxAbs[i] > dThreshold)
212 aboveThresh.insert(vertices[i]);
215 if (aboveThresh.isEmpty())
220 fullLabel.
hemi = iHemi;
221 fullLabel.
name =
"stc_label";
223 QList<int> sortedVerts(aboveThresh.begin(), aboveThresh.end());
224 std::sort(sortedVerts.begin(), sortedVerts.end());
226 const int nSurfVerts =
static_cast<int>(surface.
rr().rows());
227 fullLabel.
vertices.resize(sortedVerts.size());
228 fullLabel.
pos.resize(sortedVerts.size(), 3);
229 fullLabel.
values.resize(sortedVerts.size());
231 for (
int i = 0; i < sortedVerts.size(); ++i) {
232 fullLabel.
vertices[i] = sortedVerts[i];
233 if (sortedVerts[i] < nSurfVerts)
234 fullLabel.
pos.row(i) = surface.
rr().row(sortedVerts[i]);
237 for (
int j = 0; j < vertices.size(); ++j) {
238 if (vertices[j] == sortedVerts[i]) {
239 fullLabel.
values[i] = maxAbs[j];
254 const VectorXi& stcVertices,
257 const int nVerts =
static_cast<int>(stcVertices.size());
258 MatrixXd mask = MatrixXd::Zero(nVerts, nTimes);
261 QMap<int, int> vertToRow;
262 for (
int i = 0; i < nVerts; ++i)
263 vertToRow.insert(stcVertices[i], i);
265 for (
const auto& label : labels) {
266 for (
int i = 0; i < label.vertices.size(); ++i) {
267 auto it = vertToRow.find(label.vertices[i]);
268 if (it != vertToRow.end()) {
269 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 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