45 QList<QSet<int>> adj(nVerts);
47 for (
int t = 0; t < tris.rows(); ++t) {
52 if (v0 >= 0 && v0 < nVerts && v1 >= 0 && v1 < nVerts && v2 >= 0 && v2 < nVerts) {
74 const int nVerts =
static_cast<int>(surface.
rr().rows());
79 for (
int i = 0; i < label.
vertices.size(); ++i)
82 QSet<int> allVerts = current;
85 for (
int step = 0; step < nSteps; ++step) {
87 for (
int v : current) {
88 if (v >= 0 && v < nVerts) {
89 for (
int neighbor : adj[v]) {
90 if (!allVerts.contains(neighbor))
91 frontier.insert(neighbor);
95 allVerts.unite(frontier);
98 if (frontier.isEmpty())
103 QList<int> sortedVerts(allVerts.begin(), allVerts.end());
104 std::sort(sortedVerts.begin(), sortedVerts.end());
108 result.
name = label.
name +
"_grown";
109 result.
vertices.resize(sortedVerts.size());
110 result.
pos.resize(sortedVerts.size(), 3);
111 result.
values = VectorXd::Ones(sortedVerts.size());
113 for (
int i = 0; i < sortedVerts.size(); ++i) {
114 result.
vertices[i] = sortedVerts[i];
115 if (sortedVerts[i] < nVerts)
116 result.
pos.row(i) = surface.
rr().row(sortedVerts[i]);
127 QList<FsLabel> components;
134 for (
int i = 0; i < label.
vertices.size(); ++i) {
137 comp.
name = QString(
"%1_part%2").arg(label.
name).arg(i);
140 comp.
pos = label.
pos.row(i);
141 comp.
values = VectorXd::Ones(1);
142 if (i < label.
values.size())
144 components.append(comp);
149 const int nVerts =
static_cast<int>(surface.
rr().rows());
154 for (
int i = 0; i < label.
vertices.size(); ++i)
160 for (
int i = 0; i < label.
vertices.size(); ++i) {
162 if (visited.contains(seed))
168 visited.insert(seed);
169 QList<int> component;
171 while (!queue.isEmpty()) {
172 int v = queue.dequeue();
175 if (v >= 0 && v < nVerts) {
176 for (
int neighbor : adj[v]) {
177 if (labelSet.contains(neighbor) && !visited.contains(neighbor)) {
178 visited.insert(neighbor);
179 queue.enqueue(neighbor);
185 std::sort(component.begin(), component.end());
189 comp.
name = QString(
"%1_part%2").arg(label.
name).arg(compIdx++);
190 comp.
vertices.resize(component.size());
191 comp.
pos.resize(component.size(), 3);
192 comp.
values = VectorXd::Ones(component.size());
194 for (
int j = 0; j < component.size(); ++j) {
196 if (component[j] < nVerts)
197 comp.
pos.row(j) = surface.
rr().row(component[j]);
200 components.append(comp);
209 const VectorXi& vertices,
214 QList<FsLabel> labels;
216 if (stcData.size() == 0 || vertices.size() == 0 || surface.
isEmpty())
220 VectorXd maxAbs = stcData.cwiseAbs().rowwise().maxCoeff();
222 QSet<int> aboveThresh;
223 for (
int i = 0; i < vertices.size(); ++i) {
224 if (maxAbs[i] > dThreshold)
225 aboveThresh.insert(vertices[i]);
228 if (aboveThresh.isEmpty())
233 fullLabel.
hemi = iHemi;
234 fullLabel.
name =
"stc_label";
236 QList<int> sortedVerts(aboveThresh.begin(), aboveThresh.end());
237 std::sort(sortedVerts.begin(), sortedVerts.end());
239 const int nSurfVerts =
static_cast<int>(surface.
rr().rows());
240 fullLabel.
vertices.resize(sortedVerts.size());
241 fullLabel.
pos.resize(sortedVerts.size(), 3);
242 fullLabel.
values.resize(sortedVerts.size());
244 for (
int i = 0; i < sortedVerts.size(); ++i) {
245 fullLabel.
vertices[i] = sortedVerts[i];
246 if (sortedVerts[i] < nSurfVerts)
247 fullLabel.
pos.row(i) = surface.
rr().row(sortedVerts[i]);
250 for (
int j = 0; j < vertices.size(); ++j) {
251 if (vertices[j] == sortedVerts[i]) {
252 fullLabel.
values[i] = maxAbs[j];
267 const VectorXi& stcVertices,
270 const int nVerts =
static_cast<int>(stcVertices.size());
271 MatrixXd mask = MatrixXd::Zero(nVerts, nTimes);
274 QMap<int, int> vertToRow;
275 for (
int i = 0; i < nVerts; ++i)
276 vertToRow.insert(stcVertices[i], i);
278 for (
const auto& label : labels) {
279 for (
int i = 0; i < label.vertices.size(); ++i) {
280 auto it = vertToRow.find(label.vertices[i]);
281 if (it != vertToRow.end()) {
282 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