v2.0.0
Loading...
Searching...
No Matches
rtsourcedataworker.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "rtsourcedataworker.h"
18#include "core/rendertypes.h"
19
21#include <QMutexLocker>
22#include <QDebug>
23#include <cmath>
24
25using namespace DISP3DLIB;
26
27//=============================================================================================================
28// DEFINE MEMBER METHODS
29//=============================================================================================================
30
32 : QObject(parent)
33{
34}
35
36//=============================================================================================================
37
38void RtSourceDataWorker::addData(const Eigen::VectorXd &data)
39{
40 QMutexLocker locker(&m_mutex);
41
42 // Cap queue size at sampling frequency (1 second of data)
43 if (m_lDataQ.size() < static_cast<int>(m_dSFreq)) {
44 m_lDataQ.append(data);
45 }
46
47 // Also store for looping
48 if (m_bIsLooping) {
49 m_lDataLoopQ.append(data);
50 // Cap loop queue too
51 if (m_lDataLoopQ.size() > static_cast<int>(m_dSFreq) * 10) {
52 m_lDataLoopQ.removeFirst();
53 }
54 }
55}
56
57//=============================================================================================================
58
60{
61 QMutexLocker locker(&m_mutex);
62 m_lDataQ.clear();
63 m_lDataLoopQ.clear();
64 m_vecAverage = Eigen::VectorXd();
65 m_iSampleCtr = 0;
66 m_iCurrentSample = 0;
67}
68
69//=============================================================================================================
70
71void RtSourceDataWorker::setInterpolationMatrixLeft(QSharedPointer<Eigen::SparseMatrix<float>> mat)
72{
73 QMutexLocker locker(&m_mutex);
74 m_interpMatLh = mat;
75}
76
77//=============================================================================================================
78
79void RtSourceDataWorker::setInterpolationMatrixRight(QSharedPointer<Eigen::SparseMatrix<float>> mat)
80{
81 QMutexLocker locker(&m_mutex);
82 m_interpMatRh = mat;
83}
84
85//=============================================================================================================
86
88{
89 QMutexLocker locker(&m_mutex);
90 m_iNumAverages = qMax(1, numAvr);
91}
92
93//=============================================================================================================
94
95void RtSourceDataWorker::setColormapType(const QString &name)
96{
97 QMutexLocker locker(&m_mutex);
98 m_sColormapType = name;
99}
100
101//=============================================================================================================
102
103void RtSourceDataWorker::setThresholds(double min, double mid, double max)
104{
105 QMutexLocker locker(&m_mutex);
106 m_dThreshMin = min;
107 m_dThreshMid = mid;
108 m_dThreshMax = max;
109}
110
111//=============================================================================================================
112
114{
115 QMutexLocker locker(&m_mutex);
116 m_bIsLooping = enabled;
117}
118
119//=============================================================================================================
120
122{
123 QMutexLocker locker(&m_mutex);
124 m_dSFreq = qMax(1.0, sFreq);
125}
126
127//=============================================================================================================
128
129void RtSourceDataWorker::setSurfaceColor(const QVector<uint32_t> &baseColorsLh,
130 const QVector<uint32_t> &baseColorsRh)
131{
132 QMutexLocker locker(&m_mutex);
133 m_baseColorsLh = baseColorsLh;
134 m_baseColorsRh = baseColorsRh;
135}
136
137//=============================================================================================================
138
139void RtSourceDataWorker::setStreamSmoothedData(bool bStreamSmoothedData)
140{
141 QMutexLocker locker(&m_mutex);
142 m_bStreamSmoothedData = bStreamSmoothedData;
143}
144
145//=============================================================================================================
146
148{
149 QMutexLocker locker(&m_mutex);
150
151 // Try to get data from queue
152 Eigen::VectorXd vecCurrentData;
153
154 if (!m_lDataQ.isEmpty()) {
155 vecCurrentData = m_lDataQ.takeFirst();
156 } else if (m_bIsLooping && !m_lDataLoopQ.isEmpty()) {
157 // Loop: replay from stored data
158 vecCurrentData = m_lDataLoopQ[m_iCurrentSample % m_lDataLoopQ.size()];
159 m_iCurrentSample++;
160 } else {
161 // No data available
162 return;
163 }
164
165 // Averaging
166 if (m_iNumAverages > 1) {
167 if (m_vecAverage.size() != vecCurrentData.size()) {
168 m_vecAverage = vecCurrentData;
169 m_iSampleCtr = 1;
170 } else {
171 m_vecAverage += vecCurrentData;
172 m_iSampleCtr++;
173 }
174
175 if (m_iSampleCtr < m_iNumAverages) {
176 return; // Accumulate more samples
177 }
178
179 vecCurrentData = m_vecAverage / static_cast<double>(m_iSampleCtr);
180 m_vecAverage = Eigen::VectorXd();
181 m_iSampleCtr = 0;
182 }
183
184 // Take absolute values (source power is always positive)
185 Eigen::VectorXf vecAbsData = vecCurrentData.cwiseAbs().cast<float>();
186
187 // Determine split point between LH and RH based on interpolation matrix column counts
188 int nSourcesLh = 0;
189 int nSourcesRh = 0;
190
191 if (m_interpMatLh) {
192 nSourcesLh = m_interpMatLh->cols();
193 }
194 if (m_interpMatRh) {
195 nSourcesRh = m_interpMatRh->cols();
196 }
197
198 int nTotalExpected = nSourcesLh + nSourcesRh;
199
200 // Handle case where data doesn't match expected source count
201 if (vecAbsData.size() != nTotalExpected && nTotalExpected > 0) {
202 // Try to use whatever data we have
203 if (vecAbsData.size() < nTotalExpected) {
204 qWarning() << "RtSourceDataWorker: Data size" << vecAbsData.size()
205 << "< expected" << nTotalExpected;
206 return;
207 }
208 }
209
210 // Split data into LH and RH
211 Eigen::VectorXf dataLh, dataRh;
212 if (nSourcesLh > 0 && vecAbsData.size() >= nSourcesLh) {
213 dataLh = vecAbsData.head(nSourcesLh);
214 }
215 if (nSourcesRh > 0 && vecAbsData.size() >= nSourcesLh + nSourcesRh) {
216 dataRh = vecAbsData.segment(nSourcesLh, nSourcesRh);
217 }
218
219 // Check streaming mode
220 if (!m_bStreamSmoothedData) {
221 // Raw mode: emit source values without interpolation
222 Eigen::VectorXd rawLh = dataLh.cast<double>();
223 Eigen::VectorXd rawRh = dataRh.cast<double>();
224
225 locker.unlock();
226 emit newRtRawData(rawLh, rawRh);
227 return;
228 }
229
230 // Compute per-vertex colors for each hemisphere
231 QVector<uint32_t> colorsLh = computeHemiColors(dataLh, m_interpMatLh, m_baseColorsLh);
232 QVector<uint32_t> colorsRh = computeHemiColors(dataRh, m_interpMatRh, m_baseColorsRh);
233
234 // Unlock before emitting (avoid deadlock if slot is direct connection)
235 locker.unlock();
236
237 emit newRtSmoothedData(colorsLh, colorsRh);
238}
239
240//=============================================================================================================
241
242QVector<uint32_t> RtSourceDataWorker::computeHemiColors(
243 const Eigen::VectorXf &sourceData,
244 const QSharedPointer<Eigen::SparseMatrix<float>> &interpMat,
245 const QVector<uint32_t> &baseColors) const
246{
247 if (sourceData.size() == 0 || !interpMat || interpMat->rows() == 0) {
248 return QVector<uint32_t>();
249 }
250
251 // Interpolate source data to all surface vertices
252 Eigen::VectorXf interpolated = (*interpMat) * sourceData;
253
254 int nVertices = interpolated.size();
255 QVector<uint32_t> colors(nVertices);
256
257 bool hasBaseColors = (baseColors.size() == nVertices);
258
259 double range = m_dThreshMax - m_dThreshMin;
260 if (range <= 0.0) range = 1.0;
261
262 for (int i = 0; i < nVertices; ++i) {
263 float value = interpolated(i);
264
265 // Normalize based on thresholds
266 double normalized = (static_cast<double>(value) - m_dThreshMin) / range;
267 normalized = qBound(0.0, normalized, 1.0);
268
269 // Calculate alpha based on threshold
270 uint8_t alpha = 255;
271 if (value < m_dThreshMin) {
272 // Below threshold: use base surface color or transparent
273 if (hasBaseColors) {
274 colors[i] = baseColors[i];
275 } else {
276 colors[i] = 0; // Fully transparent
277 }
278 continue;
279 } else if (value < m_dThreshMid) {
280 // Fade in from min to mid
281 double fadeRange = m_dThreshMid - m_dThreshMin;
282 if (fadeRange > 0.0) {
283 alpha = static_cast<uint8_t>(255.0 * (value - m_dThreshMin) / fadeRange);
284 }
285
286 // Blend activation color with base color
287 if (hasBaseColors && alpha < 255) {
288 uint32_t actColor = valueToColor(normalized, 255);
289 uint32_t baseColor = baseColors[i];
290
291 // Simple alpha blend: result = act * alpha/255 + base * (255-alpha)/255
292 uint32_t aR = actColor & 0xFF;
293 uint32_t aG = (actColor >> 8) & 0xFF;
294 uint32_t aB = (actColor >> 16) & 0xFF;
295 uint32_t bR = baseColor & 0xFF;
296 uint32_t bG = (baseColor >> 8) & 0xFF;
297 uint32_t bB = (baseColor >> 16) & 0xFF;
298
299 uint32_t rR = (aR * alpha + bR * (255 - alpha)) / 255;
300 uint32_t rG = (aG * alpha + bG * (255 - alpha)) / 255;
301 uint32_t rB = (aB * alpha + bB * (255 - alpha)) / 255;
302
303 colors[i] = packABGR(rR, rG, rB);
304 continue;
305 }
306 }
307
308 colors[i] = valueToColor(normalized, alpha);
309 }
310
311 return colors;
312}
313
314//=============================================================================================================
315
316uint32_t RtSourceDataWorker::valueToColor(double value, uint8_t alpha) const
317{
318 QRgb rgb = DISPLIB::ColorMap::valueToColor(value, m_sColormapType);
319
320 uint32_t r = qRed(rgb);
321 uint32_t g = qGreen(rgb);
322 uint32_t b = qBlue(rgb);
323
324 // Pack as ABGR (same format as BrainSurface uses)
325 return packABGR(r, g, b, static_cast<uint32_t>(alpha));
326}
Background worker that converts streaming source vectors into smoothed per-vertex ABGR colour buffers...
Lightweight render-related enums (ShaderMode, VisualizationMode) shared across disp3D.
uint32_t packABGR(uint32_t r, uint32_t g, uint32_t b, uint32_t a=0xFF)
Definition rendertypes.h:48
Static scalar-to-colour lookup helpers (Jet, Hot, Bone, Viridis, Cool, RedBlue, MNE) used by every pl...
3-D brain visualisation using the Qt RHI rendering backend.
static QRgb valueToColor(double v, const QString &sMap)
Definition colormap.h:681
void setInterpolationMatrixRight(QSharedPointer< Eigen::SparseMatrix< float > > mat)
void setColormapType(const QString &name)
void setInterpolationMatrixLeft(QSharedPointer< Eigen::SparseMatrix< float > > mat)
void newRtRawData(const Eigen::VectorXd &dataLh, const Eigen::VectorXd &dataRh)
RtSourceDataWorker(QObject *parent=nullptr)
void setStreamSmoothedData(bool bStreamSmoothedData)
void setThresholds(double min, double mid, double max)
void addData(const Eigen::VectorXd &data)
void newRtSmoothedData(const QVector< uint32_t > &colorsLh, const QVector< uint32_t > &colorsRh)
void setSurfaceColor(const QVector< uint32_t > &baseColorsLh, const QVector< uint32_t > &baseColorsRh)