OSCR

DeepFaceMouse enables scalable prediction of large-scale brain activity from facial dynamics in mice.

Code ↔ Paper

5 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 5 matches
  1. [1] § STAR★Methods › Method details › Brain activity prediction using custom Deep Learning Model ↔ FaceBrainPredictionGUI_DeepFace_6s_6f.m, lines 509–580 · score 0.68 · auto regressive, processed brain, brain features, vector, linearly, filter
  2. [2] § STAR★Methods › Method details › Brain activity prediction using custom Deep Learning Model ↔ FaceBrainPredictionGUI_Facemap_8m_100Hz.m, lines 269–314 · score 0.63 · velocity features, raw feature, window aggregation, frame, Facemap, prediction
  3. [3] § STAR★Methods › Method details › Brain activity prediction using custom Deep Learning Model ↔ FaceBrainPredictionGUI_Facemap_6s_6f.m, lines 379–421 · score 0.62 · velocity features, raw feature, window aggregation, frame, Facemap, prediction
  4. [4] § STAR★Methods › Method details › Brain activity prediction using custom Deep Learning Model ↔ FaceBrainPredictionGUI_Facemap_6s_6f.m, lines 634–721 · score 0.60 · eye features, mouth features, centroid, nose, dynamic, prediction
  5. [5] § STAR★Methods › Method details › Brain activity prediction using custom Deep Learning Model ↔ FaceBrainPredictionGUI_Facemap_6s_6f.m, lines 1–44 · score 0.57 · corresponding brain, face brain, hemodynamic, FaceMap, Facial, tracking

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

MATLAB · 736 lines · 29 KB · CC-BY-4.0 · 3 matches

  1. function FaceBrainPredictionGUI_Facemap_NoEnsemble_winsorizedR2_ko
  2. % FaceBrainPredictionGUI_Facemap_NoEnsemble_winsorizedR2.m
  3. %
  4. % Overview:
  5. %
  6. % The GUI includes the following functionalities:
  7. % 1. Set Data Label: Specify a label for the dataset.
  8. % 2. Change Save Folder: Select a folder to save outputs, models, graphs, and test data.
  9. % 3. Add Training Pairs: Select face CSV and corresponding brain MAT files for training.
  10. % 4. Clear Training Pairs: Remove current training data from memory.
  11. % 5. Add Testing Pairs: Select face CSV and corresponding brain MAT files for testing.
  12. % 6. Clear Testing Pairs: Remove current testing data from memory.
  13. % 7. Clear In-Memory Model: Reset the current model.
  14. % 8. Load Pre-Trained Model: Load a previously saved model (Neural Network or Ensemble).
  15. % 9. Train New Model: Train a new Neural Network model using the training pairs.
  16. % 10. Test Model: Run the model on testing pairs. For each test pair:
  17. % - Computes performance metrics: RMSE, standard R², Winsorized R² (Filtered R²), and MAE.
  18. % - Generates a figure comparing actual and predicted signals.
  19. % - Saves the figure as both .fig and .png files.
  20. % - Saves test data and metrics in a .mat file.
  21. % - Writes overall test metrics to an Excel file.
  22. % 11. Save Pairs to Disk: Save current training/testing pairs to a .mat backup file.
  23. % 12. Load Pairs from Disk: Load training/testing pairs from a backup file.
  24. % 13. Exit: Exit the application.
  25. %
  26. % Instructions for New Users:
  27. % - Prepare your face data as CSV files that include columns for tracking facial features.
  28. % - Prepare your brain data as MAT files containing a variable with hemodynamic signals.
  29. % The script automatically detects the relevant field (e.g., 'hemodynamic').
  30. % - Follow the menu options to add training/testing pairs.
  31. % - Train your model and then test it; the script will generate and save graphs and metrics.
  32. %
  33. % Outputs:
  34. % - Trained model saved as a .mat file.
  35. % - Test graphs saved as .fig and .png files.
  36. % - Test data and metrics saved as individual .mat files.
  37. % - An Excel file summarizing test metrics.
  38. %
  39. % Note:
  40. % - The Winsorized R² (Filtered R²) calculation ignores the top 5% of errors to
  41. % reduce the impact of outliers.
  42. clc; clear;
  43. %% Default Parameters
  44. dataLabel = 'withaddbrain';
  45. savePath = pwd; % Default: current folder
  46. fsBrain = 20; % Brain sampling rate (Hz)
  47. fsFace = 120; % Face sampling rate (Hz)
  48. bandpassLowCut = 0.1; % For brain band-pass filtering
  49. bandpassHighCut = 9.99;
  50. butterOrder = 2;
  51. movAvgBrainSec = 1; % 1 s moving average for brain smoothing
  52. ignoreInitialSec = 5; % Ignore first 5 s of both signals
  53. faceWindowSize = 5; % Number of face frames per time-window aggregator
  54. nnHiddenNeurons = 5;
  55. nnEpochs = 100;
  56. % ensLearningCycles = 50; % Not used
  57. % Filenames for saving test metrics and pairs backup
  58. testMetricsFile = 'TestMetrics.xlsx';
  59. pairsBackupFile = [dataLabel '_pairsBackup.mat'];
  60. %% Data in Memory
  61. trainingPairs = [];
  62. testingPairs = [];
  63. %% Model
  64. currentModel = [];
  65. currentModelType = ''; % 'Neural Network' or 'Ensemble'
  66. %% Main Menu Loop
  67. while true
  68. choice = menu('Select an option', ...
  69. '1. Set Data Label', ...
  70. '2. Change Save Folder', ...
  71. '3. Add Training Pairs (Face CSV + Brain MAT)', ...
  72. '4. Clear Training Pairs', ...
  73. '5. Add Testing Pairs (Face CSV + Brain MAT)', ...
  74. '6. Clear Testing Pairs', ...
  75. '7. Clear In-Memory Model', ...
  76. '8. Load Pre-Trained Model', ...
  77. '9. Train New Model', ...
  78. '10. Test Model', ...
  79. '11. Save Pairs to Disk', ...
  80. '12. Load Pairs from Disk', ...
  81. '13. Exit');
  82. switch choice
  83. case 1
  84. prompt = {'Enter data label (e.g. withaddbrain):'};
  85. dlg_title = 'Set Data Label';
  86. answer = inputdlg(prompt, dlg_title, 1, {dataLabel});
  87. if ~isempty(answer)
  88. dataLabel = answer{1};
  89. fprintf('Data label set to: %s\n', dataLabel);
  90. end
  91. case 2
  92. newFolder = uigetdir([], 'Select folder for saving outputs/models');
  93. if isequal(newFolder, 0)
  94. disp('No folder selected. Save folder unchanged.');
  95. else
  96. savePath = newFolder;
  97. fprintf('Save folder changed to: %s\n', savePath);
  98. end
  99. case 3
  100. addMultipleBrainPairs('training', fsFace, fsBrain, faceWindowSize, ...
  101. bandpassLowCut, bandpassHighCut, butterOrder, movAvgBrainSec, ignoreInitialSec);
  102. case 4
  103. trainingPairs = [];
  104. disp('Training pairs cleared.');
  105. case 5
  106. addMultipleBrainPairs('testing', fsFace, fsBrain, faceWindowSize, ...
  107. bandpassLowCut, bandpassHighCut, butterOrder, movAvgBrainSec, ignoreInitialSec);
  108. case 6
  109. testingPairs = [];
  110. disp('Testing pairs cleared.');
  111. case 7
  112. currentModel = [];
  113. currentModelType = '';
  114. disp('Cleared in-memory model.');
  115. case 8
  116. [modelFile, modelPath] = uigetfile('*.mat','Select a pre-trained model file');
  117. if isequal(modelFile, 0)
  118. disp('No model file selected.');
  119. else
  120. loadedData = load(fullfile(modelPath, modelFile));
  121. modelTypeChoice = menu('Select model type','Neural Network','Ensemble');
  122. if modelTypeChoice == 1
  123. if isfield(loadedData, 'net')
  124. currentModel = loadedData.net;
  125. currentModelType = 'Neural Network';
  126. fprintf('Loaded Neural Network model from: %s\n', modelFile);
  127. else
  128. error('The file does not contain variable "net".');
  129. end
  130. else
  131. if isfield(loadedData, 'ensModel')
  132. currentModel = loadedData.ensModel;
  133. currentModelType = 'Ensemble';
  134. fprintf('Loaded Ensemble model from: %s\n', modelFile);
  135. else
  136. error('The file does not contain variable "ensModel".');
  137. end
  138. end
  139. end
  140. case 9
  141. % Train new model
  142. if isempty(trainingPairs)
  143. disp('No training pairs available.');
  144. continue;
  145. end
  146. [Xall, Yall] = concatPairs(trainingPairs);
  147. if isempty(Xall)
  148. disp('No valid data in training pairs.');
  149. continue;
  150. end
  151. % Train only the Neural Network model
  152. net = fitnet(nnHiddenNeurons);
  153. net.trainParam.epochs = nnEpochs;
  154. net.trainParam.goal = 1e-4;
  155. [net, ~] = train(net, Xall', Yall');
  156. YpredNN = net(Xall');
  157. rmseNN = sqrt(mean((YpredNN' - Yall).^2));
  158. r2NN = 1 - sum((Yall - YpredNN').^2) / sum((Yall - mean(Yall)).^2);
  159. fprintf('Neural Net: RMSE=%.4f, R^2=%.4f\n', rmseNN, r2NN);
  160. % Set neural network as the current model
  161. currentModel = net;
  162. currentModelType = 'Neural Network';
  163. % Save neural network model
  164. save(fullfile(savePath, [dataLabel '_trainedNeuralNetwork.mat']), 'net');
  165. disp('Neural Network model saved to disk.');
  166. case 10
  167. % Test Model
  168. if isempty(testingPairs)
  169. disp('No testing pairs available.');
  170. continue;
  171. end
  172. if isempty(currentModel)
  173. disp('No model loaded/trained. Please train or load a model first.');
  174. continue;
  175. end
  176. % Debug messages
  177. disp('Entering Test Model mode...');
  178. fprintf('Number of testing pairs: %d\n', length(testingPairs));
  179. testRMSEs = [];
  180. testR2s = [];
  181. testFilteredR2s = [];
  182. testMAEs = [];
  183. metricsCell = {}; % {BrainFile, Region, Hemisphere, RMSE, R2, Filtered R^2, MAE}
  184. for iPair = 1:length(testingPairs)
  185. fprintf('Processing test pair %d of %d...\n', iPair, length(testingPairs));
  186. Xtest = testingPairs(iPair).X;
  187. Ytest = testingPairs(iPair).Y;
  188. if strcmpi(currentModelType, 'Neural Network')
  189. Ypred = currentModel(Xtest')';
  190. else
  191. Ypred = predict(currentModel, Xtest);
  192. end
  193. if length(Ypred) == length(Ytest)
  194. rmseVal = sqrt(mean((Ypred - Ytest).^2));
  195. r2Val = 1 - sum((Ytest - Ypred).^2) / sum((Ytest - mean(Ytest)).^2);
  196. else
  197. minLen = min(length(Ypred), length(Ytest));
  198. rmseVal = sqrt(mean((Ypred(1:minLen) - Ytest(1:minLen)).^2));
  199. r2Val = NaN;
  200. end
  201. % Compute Winsorized R² (Filtered R²)
  202. errorValues = abs(Ytest - Ypred);
  203. threshold = prctile(errorValues, 99); % Ignore top 1% of errors
  204. validIdx = errorValues < threshold;
  205. r2Filtered = 1 - sum((Ytest(validIdx) - Ypred(validIdx)).^2) / sum((Ytest(validIdx) - mean(Ytest(validIdx))).^2);
  206. % Compute MAE (Mean Absolute Error)
  207. maeVal = mean(abs(Ytest - Ypred));
  208. testRMSEs(end+1) = rmseVal;
  209. testR2s(end+1) = r2Val;
  210. testFilteredR2s(end+1) = r2Filtered;
  211. testMAEs(end+1) = maeVal;
  212. regionStr = testingPairs(iPair).region;
  213. hemisphereStr = testingPairs(iPair).hemisphere;
  214. brainFile = testingPairs(iPair).brainMAT;
  215. fprintf('Test Pair %d\n Face: %s\n Brain: %s\n Region: %s, Hemi: %s\n', ...
  216. iPair, testingPairs(iPair).faceCSV, brainFile, regionStr, hemisphereStr);
  217. fprintf(' RMSE=%.4f, R^2=%.4f, Filtered R^2=%.4f, MAE=%.4f\n', rmseVal, r2Val, r2Filtered, maeVal);
  218. % Save metrics for this test pair
  219. metricsCell(iPair, :) = {brainFile, regionStr, hemisphereStr, rmseVal, r2Val, r2Filtered, maeVal};
  220. % Generate the prediction figure and save as .fig and .png
  221. fig = figure('Visible','off','Position',[100 100 1000 600]);
  222. plot(Ytest, 'g', 'LineWidth', 1.2); hold on;
  223. plot(Ypred, 'r', 'LineWidth', 1.2);
  224. legend('Actual Brain', 'Predicted', 'Location', 'best');
  225. title(sprintf('Brain main(t) Prediction (Auto-Regressive Input)\nRMSE=%.4f, R^2=%.4f', rmseVal, r2Val));
  226. xlabel('Samples'); ylabel('Amplitude');
  227. [~, bName, ~] = fileparts(brainFile);
  228. pngName = sprintf('TestPair%d_%s.png', iPair, bName);
  229. figName = sprintf('TestPair%d_%s.fig', iPair, bName);
  230. savefig(fig, fullfile(savePath, figName));
  231. saveas(fig, fullfile(savePath, pngName));
  232. % Save test data and figure info in .mat file
  233. matFileName = fullfile(savePath, sprintf('TestPair%d_%s.mat', iPair, bName));
  234. save(matFileName, 'Ytest', 'Ypred', 'brainFile', 'regionStr', 'hemisphereStr', 'rmseVal', 'r2Val');
  235. close(fig);
  236. end
  237. if ~isempty(testRMSEs)
  238. avgRMSE = mean(testRMSEs);
  239. avgR2 = mean(testR2s);
  240. fprintf('---\nOverall Test RMSE=%.4f, R^2=%.4f\n', avgRMSE, avgR2);
  241. end
  242. header = {'BrainFile', 'Region', 'Hemisphere', 'RMSE', 'R2', 'Filtered R^2', 'MAE'};
  243. if ~isempty(metricsCell)
  244. outCell = [header; metricsCell];
  245. else
  246. outCell = header;
  247. end
  248. outFile = fullfile(savePath, testMetricsFile);
  249. writecell(outCell, outFile, 'Sheet', 'TestMetrics', 'Range', 'A1');
  250. fprintf('Wrote test metrics to %s\n', outFile);
  251. case 11
  252. % Save pairs to disk
  253. pairsFile = fullfile(savePath, pairsBackupFile);
  254. save(pairsFile, 'trainingPairs', 'testingPairs');
  255. fprintf('Saved pairs to %s\n', pairsFile);
  256. case 12
  257. % Load pairs from disk
  258. pairsFile = fullfile(savePath, pairsBackupFile);
  259. if exist(pairsFile, 'file')
  260. load(pairsFile, 'trainingPairs', 'testingPairs');
  261. fprintf('Loaded pairs from %s\n', pairsFile);
  262. fprintf('Training pairs: %d, Testing pairs: %d\n', numel(trainingPairs), numel(testingPairs));
  263. else
  264. fprintf('No backup file found at %s\n', pairsFile);
  265. end
  266. case 13
  267. disp('Exiting application.');
  268. break;
  269. end
  270. end
  271. %% Nested Functions
  272. function addMultipleBrainPairs(modeStr, fsFace, fsBrain, faceWindowSize, bandpassLowCut, bandpassHighCut, bOrder, movAvgBrainSec, ignoreSec)
  273. [faceFile, facePath] = uigetfile('*.csv', sprintf('Select Face CSV (%.0f Hz) for %s', fsFace, modeStr));
  274. if isequal(faceFile, 0)
  275. disp('No face CSV selected.');
  276. return;
  277. end
  278. faceFullPath = fullfile(facePath, faceFile);
  279. [brainFiles, brainPath] = uigetfile('*.mat', sprintf('Select Brain MAT(s) (%.0f Hz) for %s', fsBrain, modeStr), 'MultiSelect', 'on');
  280. if isequal(brainFiles, 0)
  281. disp('No brain MAT selected.');
  282. return;
  283. end
  284. if ischar(brainFiles)
  285. brainFiles = {brainFiles};
  286. end
  287. % Load and aggregate face data using a time-window approach and z-scoring
  288. [faceTime, faceDataWindowed] = loadFaceCSV_withAggregatesWindowed(faceFullPath, fsFace, faceWindowSize);
  289. if isempty(faceDataWindowed)
  290. warning('Face aggregator is empty for %s. Skipping pair addition.', faceFile);
  291. return;
  292. end
  293. for iB = 1:length(brainFiles)
  294. brainFullPath = fullfile(brainPath, brainFiles{iB});
  295. % Load brain data and compute additional derived features
  296. [brainTime, brainMain, optionalBrainFeats] = loadBrainMAT_andDerived(brainFullPath, fsBrain);
  297. % Align and construct the final input and target.
  298. [Xaligned, Yaligned] = alignAndConstructAutoReg(faceTime, faceDataWindowed, fsFace, ...
  299. brainTime, brainMain, optionalBrainFeats, fsBrain, ...
  300. bandpassLowCut, bandpassHighCut, bOrder, ...
  301. movAvgBrainSec, ignoreSec);
  302. % Skip pair if Xaligned is empty or has inconsistent columns.
  303. if isempty(Xaligned) || size(Xaligned,2) == 0 || isempty(Yaligned)
  304. fprintf('Skipping pair for Brain file: %s due to insufficient data.\n', brainFiles{iB});
  305. continue;
  306. end
  307. newPair.faceCSV = faceFullPath;
  308. newPair.brainMAT = brainFullPath;
  309. % Parse region and hemisphere from file name (simple heuristic)
  310. [~, baseName, ~] = fileparts(brainFullPath);
  311. tokens = regexp(baseName, '^(.*?)(bilateral|left|right)', 'tokens', 'once');
  312. if ~isempty(tokens)
  313. regionStr = regexprep(tokens{1}, '_$', '');
  314. hemisphereStr = tokens{2};
  315. else
  316. regionStr = baseName;
  317. hemisphereStr = 'unknown';
  318. end
  319. newPair.region = regionStr;
  320. newPair.hemisphere = hemisphereStr;
  321. newPair.X = Xaligned;
  322. newPair.Y = Yaligned;
  323. if strcmpi(modeStr, 'training')
  324. trainingPairs = [trainingPairs; newPair];
  325. fprintf('Added to TRAINING: Face=%s, Brain=%s\n', faceFile, brainFiles{iB});
  326. else
  327. testingPairs = [testingPairs; newPair];
  328. fprintf('Added to TESTING: Face=%s, Brain=%s\n', faceFile, brainFiles{iB});
  329. end
  330. end
  331. end
  332. function [faceTimeOut, faceDataWindowed] = loadFaceCSV_withAggregatesWindowed(csvFile, fsFace, windowSize)
  333. opts = detectImportOptions(csvFile, 'VariableNamingRule', 'preserve');
  334. T = readtable(csvFile, opts);
  335. % Remove columns with "score" and "track"
  336. scoreMask = contains(T.Properties.VariableNames, 'score', 'IgnoreCase', true);
  337. T(:, scoreMask) = [];
  338. if ismember('track', T.Properties.VariableNames)
  339. T = removevars(T, 'track');
  340. end
  341. % Find frame column among 'Frame','frame','frame_idx'
  342. possibleFrames = {'Frame', 'frame', 'frame_idx'};
  343. colFound = intersect(possibleFrames, T.Properties.VariableNames, 'stable');
  344. if ~isempty(colFound)
  345. frameVec = T.(colFound{1});
  346. T = removevars(T, colFound{1});
  347. else
  348. if width(T) < 2
  349. error('No recognized frame column.');
  350. end
  351. frameVec = T{:,2};
  352. T(:,2) = [];
  353. end
  354. faceTime = (frameVec - frameVec(1)) / fsFace;
  355. % Compute aggregator features and velocities
  356. rawFeatures = computeAggregateFeatures(T); % Nx12
  357. velFeatures = computeVelocities(rawFeatures, fsFace); % Nx12 => total Nx24
  358. allFeatures = [rawFeatures, velFeatures];
  359. [faceTimeU, idxU] = unique(faceTime, 'stable');
  360. allFeatures = allFeatures(idxU, :);
  361. faceTime = faceTimeU;
  362. [faceTimeClean, faceData] = cleanAndInterpolateFaceData(faceTime, allFeatures);
  363. % Z-score each column
  364. zFaceData = zscore(faceData, 0, 1);
  365. % Build time-window aggregator
  366. [faceTimeOut, faceDataWindowed] = buildTimeWindow(faceTimeClean, zFaceData, windowSize);
  367. end
  368. function [timeWin, dataWin] = buildTimeWindow(timeIn, dataIn, wSize)
  369. N = length(timeIn);
  370. c = size(dataIn, 2);
  371. outRows = N - wSize + 1;
  372. if outRows < 1
  373. warning('Not enough data for time-window aggregator. Returning empty.');
  374. timeWin = [];
  375. dataWin = [];
  376. return;
  377. end
  378. dataWin = zeros(outRows, c * wSize);
  379. timeWin = timeIn(wSize:end);
  380. for i = 1:outRows
  381. block = dataIn(i:i+wSize-1, :); % [wSize x c]
  382. dataWin(i, :) = block(:)'; % Flatten row-major
  383. end
  384. end
  385. function [brainTime, brainMain, optionalBrainFeats] = loadBrainMAT_andDerived(matFile, fsBrain)
  386. S = load(matFile);
  387. fieldNames = fieldnames(S);
  388. hemoField = '';
  389. for iF = 1:numel(fieldNames)
  390. if contains(fieldNames{iF}, 'hemodynamic', 'IgnoreCase', true) && ~contains(fieldNames{iF}, 'sub', 'IgnoreCase', true)
  391. hemoField = fieldNames{iF};
  392. break;
  393. end
  394. end
  395. if isempty(hemoField)
  396. % fallback: look for 'hemo'
  397. for iF = 1:numel(fieldNames)
  398. if contains(fieldNames{iF}, 'hemo', 'IgnoreCase', true)
  399. hemoField = fieldNames{iF};
  400. break;
  401. end
  402. end
  403. end
  404. if isempty(hemoField)
  405. error('No hemodynamic field found in %s.', matFile);
  406. end
  407. brainMain = S.(hemoField);
  408. if isfield(S, 'hemoTime')
  409. brainTime = S.hemoTime;
  410. elseif isfield(S, 'time')
  411. brainTime = S.time;
  412. else
  413. nSamples = length(brainMain);
  414. brainTime = (0:nSamples-1)' / fsBrain;
  415. end
  416. % If brainMain is multi-dimensional, use PCA to extract top 3 components.
  417. if size(brainMain, 2) > 1
  418. [~, score, ~] = pca(brainMain);
  419. optionalBrainFeats = score(:, 1:3);
  420. else
  421. % Otherwise, compute frequency-domain features using a 1-second window.
  422. optionalBrainFeats = computeBrainFreqFeatures(brainMain, fsBrain);
  423. % Interpolate to match length of brainMain if needed.
  424. if size(optionalBrainFeats, 1) < length(brainMain)
  425. tFreq = linspace(0, 1, size(optionalBrainFeats, 1))';
  426. tBrain = linspace(0, 1, length(brainMain))';
  427. optionalBrainFeats = interp1(tFreq, optionalBrainFeats, tBrain, 'linear', 'extrap');
  428. end
  429. end
  430. end
  431. function freqFeats = computeBrainFreqFeatures(brainSignal, fs)
  432. % Compute frequency-domain features using a 1-second window (fs samples)
  433. winSize = round(1 * fs); % 1-second window
  434. N = length(brainSignal);
  435. nOut = N - winSize + 1;
  436. freqFeats = zeros(nOut, 2); % 2 features: power in two bands
  437. for i = 1:nOut
  438. windowData = brainSignal(i:i+winSize-1);
  439. windowData = windowData .* hamming(winSize);
  440. X = fft(windowData);
  441. P2 = abs(X / winSize).^2;
  442. P1 = P2(1:floor(winSize/2)+1);
  443. f = (0:floor(winSize/2))' * (fs / winSize);
  444. % Define two bands: 0.1-0.5 Hz and 0.5-4 Hz (adjust if needed)
  445. idx1 = f >= 0.1 & f < 1;
  446. idx2 = f >= 1 & f <= 5;
  447. power1 = sum(P1(idx1));
  448. power2 = sum(P1(idx2));
  449. freqFeats(i, :) = [power1, power2];
  450. end
  451. end
  452. function [Xaligned, Yaligned] = alignAndConstructAutoReg(faceTime, faceDataWindowed, fsFace, ...
  453. brainTime, brainMainInput, optionalBrainFeats, fsBrain, lowCut, highCut, bOrder, movAvgBrainSec, ignoreSec)
  454. % 1) Band-pass filter, compute DF/F, smooth
  455. yFilt = bandpassFilter(brainMainInput, fsBrain, lowCut, highCut, bOrder);
  456. baseVal = mean(yFilt);
  457. yDFF = (yFilt / baseVal) - 1;
  458. windowBrain = round(movAvgBrainSec * fsBrain);
  459. yProc = smoothdata(yDFF, 'movmean', windowBrain);
  460. % 2) Remove initial ignoreSec from both face and brain
  461. faceSkip = round(ignoreSec * fsFace);
  462. if faceSkip < length(faceTime)
  463. faceTime(1:faceSkip) = [];
  464. faceDataWindowed(1:faceSkip, :) = [];
  465. else
  466. warning('Ignoring entire face signal? Check data length.');
  467. faceTime(faceSkip:end) = [];
  468. faceDataWindowed(faceSkip:end, :) = [];
  469. end
  470. brainSkip = round(ignoreSec * fsBrain);
  471. if brainSkip < length(brainTime)
  472. brainTime(1:brainSkip) = [];
  473. yProc(1:brainSkip) = [];
  474. if ~isempty(optionalBrainFeats)
  475. optionalBrainFeats(1:brainSkip, :) = [];
  476. end
  477. else
  478. warning('Ignoring entire brain signal? Check data length.');
  479. brainTime(brainSkip:end) = [];
  480. yProc(brainSkip:end) = [];
  481. if ~isempty(optionalBrainFeats)
  482. optionalBrainFeats(1:brainSkip) = [];
  483. end
  484. end
  485. % 3) Resample face aggregator onto brain time
  486. faceInterp = zeros(length(brainTime), size(faceDataWindowed, 2));
  487. for c = 1:size(faceDataWindowed, 2)
  488. faceInterp(:, c) = interp1(faceTime, faceDataWindowed(:, c), brainTime, 'linear', 'extrap');
  489. end
  490. % 4) Resample optional brain features (if any) onto same time
  491. if ~isempty(optionalBrainFeats)
  492. brainFeatsInterp = zeros(length(brainTime), size(optionalBrainFeats, 2));
  493. % We assume optionalBrainFeats covers 0..1 in normalized time
  494. tOptional = linspace(0, 1, size(optionalBrainFeats, 1))';
  495. tBrain = linspace(0, 1, length(brainTime))';
  496. for c = 1:size(optionalBrainFeats, 2)
  497. brainFeatsInterp(:, c) = interp1(tOptional, optionalBrainFeats(:, c), tBrain, 'linear', 'extrap');
  498. end
  499. else
  500. brainFeatsInterp = [];
  501. end
  502. % 5) Construct auto-reg input with BrainMain(t-1)
  503. yShift = [NaN; yProc(1:end-1)];
  504. idxValid = 2:length(yProc); % remove first sample
  505. Xface = faceInterp(idxValid, :);
  506. Yfinal = yProc(idxValid);
  507. yShiftValid = yShift(idxValid);
  508. if ~isempty(brainFeatsInterp)
  509. bFeats = brainFeatsInterp(idxValid, :);
  510. Xall = [Xface, bFeats, yShiftValid];
  511. else
  512. Xall = [Xface, yShiftValid];
  513. end
  514. Xaligned = Xall;
  515. Yaligned = Yfinal;
  516. end
  517. function y = bandpassFilter(x, fs, lowCut, highCut, order)
  518. nyq = fs / 2;
  519. [b, a] = butter(order, [lowCut, highCut] / nyq, 'bandpass');
  520. y = filtfilt(b, a, x);
  521. end
  522. function [Xall, Yall] = concatPairs(pairsStruct)
  523. Xall = [];
  524. Yall = [];
  525. for i = 1:length(pairsStruct)
  526. Xi = pairsStruct(i).X;
  527. Yi = pairsStruct(i).Y;
  528. if isempty(Xi) || isempty(Yi)
  529. fprintf('Pair %d is empty. Skipping.\n', i);
  530. continue;
  531. end
  532. if isempty(Xall)
  533. Xall = Xi;
  534. Yall = Yi;
  535. else
  536. if size(Xi, 2) ~= size(Xall, 2)
  537. fprintf('Pair %d has %d columns, expected %d. Skipping pair.\n', i, size(Xi,2), size(Xall,2));
  538. continue;
  539. end
  540. Xall = [Xall; Xi];
  541. Yall = [Yall; Yi];
  542. end
  543. end
  544. end
  545. function [faceTime, faceData] = cleanAndInterpolateFaceData(faceTimeIn, faceDataIn)
  546. cleanTime = faceTimeIn(:);
  547. cleanData = faceDataIn;
  548. badMask = ~isfinite(cleanTime);
  549. if any(badMask)
  550. cleanTime(badMask) = [];
  551. cleanData(badMask, :) = [];
  552. end
  553. [nr, nc] = size(cleanData);
  554. for c = 1:nc
  555. colVals = cleanData(:, c);
  556. invalidMask = (colVals == 0) | isnan(colVals) | isinf(colVals);
  557. colVals(invalidMask) = NaN;
  558. colVals = fillmissing(colVals, 'linear', 'EndValues', 'extrap');
  559. cleanData(:, c) = colVals;
  560. end
  561. faceTime = cleanTime;
  562. faceData = cleanData;
  563. end
  564. function aggFeatures = computeAggregateFeatures(T)
  565. % This function computes 12 aggregator features from face data table T.
  566. % Updated to handle the possibility that only 3 whisker points exist
  567. % instead of 4. It checks columns dynamically.
  568. % The 12 aggregator features are:
  569. % (1) eyeCentroid_x
  570. % (2) eyeCentroid_y
  571. % (3) eyeArea
  572. % (4) noseMid_x
  573. % (5) noseMid_y
  574. % (6) noseDist
  575. % (7) mouthMid_x
  576. % (8) mouthMid_y
  577. % (9) mouthDist
  578. % (10) whiskerCentroid_x
  579. % (11) whiskerCentroid_y
  580. % (12) whiskerSpread
  581. N = height(T);
  582. aggFeatures = zeros(N, 12);
  583. % Whisker columns that might exist:
  584. possibleWhiskers = {'Whisker_T_L', 'Whisker_T_R', 'Whisker_B_L', 'Whisker_B_R'};
  585. for i = 1:N
  586. %% Eye features (Top, Bottom, Left, Right)
  587. eX = [safeVal(T, i, 'Top.x'), safeVal(T, i, 'Bottom.x'), safeVal(T, i, 'Left.x'), safeVal(T, i, 'Right.x')];
  588. eY = [safeVal(T, i, 'Top.y'), safeVal(T, i, 'Bottom.y'), safeVal(T, i, 'Left.y'), safeVal(T, i, 'Right.y')];
  589. eyeCentroid_x = mean(eX);
  590. eyeCentroid_y = mean(eY);
  591. w = max(eX) - min(eX);
  592. h = max(eY) - min(eY);
  593. eyeArea = w * h;
  594. %% Nose features (Nose_Top, Nose_Bottom)
  595. nxTop = safeVal(T, i, 'Nose_Top.x');
  596. nyTop = safeVal(T, i, 'Nose_Top.y');
  597. nxBot = safeVal(T, i, 'Nose_Bottom.x');
  598. nyBot = safeVal(T, i, 'Nose_Bottom.y');
  599. noseMid_x = mean([nxTop, nxBot]);
  600. noseMid_y = mean([nyTop, nyBot]);
  601. noseDist = sqrt((nxBot - nxTop)^2 + (nyBot - nyTop)^2);
  602. %% Mouth features (Mouth_Top, Mouth_Bottom)
  603. mxTop = safeVal(T, i, 'Mouth_Top.x');
  604. myTop = safeVal(T, i, 'Mouth_Top.y');
  605. mxBot = safeVal(T, i, 'Mouth_Bottom.x');
  606. myBot = safeVal(T, i, 'Mouth_Bottom.y');
  607. mouthMid_x = mean([mxTop, mxBot]);
  608. mouthMid_y = mean([myTop, myBot]);
  609. mouthDist = sqrt((mxBot - mxTop)^2 + (myBot - myTop)^2);
  610. %% Whisker features
  611. whiskerXvals = [];
  612. whiskerYvals = [];
  613. for widx = 1:length(possibleWhiskers)
  614. baseLabel = possibleWhiskers{widx};
  615. xLabel = [baseLabel, '.x'];
  616. yLabel = [baseLabel, '.y'];
  617. if ismember(xLabel, T.Properties.VariableNames) && ismember(yLabel, T.Properties.VariableNames)
  618. wX = safeVal(T, i, xLabel);
  619. wY = safeVal(T, i, yLabel);
  620. if ~(wX == 0 && wY == 0)
  621. whiskerXvals(end+1) = wX;
  622. whiskerYvals(end+1) = wY;
  623. end
  624. end
  625. end
  626. if isempty(whiskerXvals)
  627. whiskerCentroid_x = 0;
  628. whiskerCentroid_y = 0;
  629. whiskerSpread = 0;
  630. else
  631. whiskerCentroid_x = mean(whiskerXvals);
  632. whiskerCentroid_y = mean(whiskerYvals);
  633. ww = max(whiskerXvals) - min(whiskerXvals);
  634. hh = max(whiskerYvals) - min(whiskerYvals);
  635. whiskerSpread = ww * hh;
  636. end
  637. aggFeatures(i, :) = [eyeCentroid_x, eyeCentroid_y, eyeArea, noseMid_x, noseMid_y, noseDist, mouthMid_x, mouthMid_y, mouthDist, whiskerCentroid_x, whiskerCentroid_y, whiskerSpread];
  638. end
  639. end
  640. function val = safeVal(T, row, colName)
  641. if ismember(colName, T.Properties.VariableNames)
  642. val = T.(colName)(row);
  643. else
  644. val = 0;
  645. end
  646. end
  647. function vel = computeVelocities(features, fs)
  648. vel = [zeros(1, size(features,2)); diff(features)] * fs;
  649. end
  650. end

FaceBrainPredictionGUI_Facemap_6s_6f.m, under CC-BY-4.0 · at the source

Overview

Authors: Kemal Ozdemirli1,2, Tenesha Connor1,3, Kaleb Kim1,4, Ersan Alp Unlu1,4, Macit Emre Lacin1, Miguel Maldonado1,4, Frederick Bell1,5, Thiago Peixoto Leal6, Caglar Oksuz4, Anthony Sloan7, Nicholas Sarn6, Anthony Chomyk1, Bruce Trapp1, Justin D Lathia7,8,9,10, Ignacio Mata6,11, Charis Eng6, Murat Yildirim1,8
  1. Department of Neurosciences, Cleveland Clinic Research, Cleveland, OH, USA
  2. Department of Mechanical and Aerospace Engineering, Case Western Reserve University, Cleveland, OH, USA
  3. Department of Biomedical and Chemical Engineering, Cleveland State University, Cleveland, OH, USA
  4. Department of Computer and Data Sciences, Case Western Reserve University, Cleveland, OH, USA
  5. Department of Neurosciences, Case Western Reserve University, Cleveland, OH, USA
  6. Genomic Sciences and Systems Biology, Cleveland Clinic Research, Cleveland, OH, USA
  7. Department of Cancer Sciences, Cleveland Clinic Research, Cleveland, OH, USA
  8. Comprehensive Cancer Center, Case Western Reserve University, Cleveland, OH, USA
  9. Rose Ella Burkhardt Brain Tumor & Neuro-Oncology Center, Cleveland Clinic, Cleveland, OH, USA
  10. Department of Clinical Medicine, Faculty of Health and Medical Sciences, University of Copenhagen, Copenhagen, Denmark
  11. Department of Molecular Medicine, Case Western Reserve University, Cleveland, OH, USA
Journal: Cell reports methods, volume 6, issue 8, article 101480
Dates: received 2 July 2025; accepted 8 May 2026; published online 8 June 2026; in print August 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1016/j.crmeth.2026.101480 · PMID 42259296 · PMCID PMC13494551 · OpenAlex W7163922001
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: optical imaging (calcium, voltage, 2-photon) (modality), mouse (organism), methods / tools (subfield)
Methods: Spectral & time-frequency, Statistics, Smoothing, state filtering, decompositions, Preprocessing, fMRI & imaging, Single-unit activity, calcium imaging, Connectivity
Keywords: orofacial behavior, facial dynamics, pose estimation, SLEAP, deep learning, brain-behavior coupling, neural activity prediction, widefield calcium imaging, mesoscale imaging, systems neuroscience
MeSH: Brain*, Deep Learning*, Face*, Animals, Behavior, Animal, Mice (* major topic)
Topic: Face Recognition and Perception (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: BLRD VA (I01 BX005978); NCI NIH HHS (P30 CA043703); American Brain Tumor Association; NIBIB NIH HHS (R00 EB027706); NINDS NIH HHS (R01 NS112499); National Institute of Biomedical Imaging and Bioengineering; Case Comprehensive Cancer Center
Citations: not cited yet (Europe PMC); 40 references in the paper
Research resources: Wildtype Animals Mouse Wildtype C57BL/6J RRID:IMSR_JAX:000664, B6.Cg-Tg(Camk2a-cre)T29-1Stl/J RRID:IMSR_JAX:005359, GCaMP6f Ai148(TIT2L-GC6f-ICL-tTA2)-D RRID:IMSR_JAX:030328, GCaMP6s Ai162(TIT2L-GC6s-ICL-tTA2)-D RRID:IMSR_JAX:031562, jGCaMP8mTIGRE2-jGCaMP8m-IRES-tTA2-WPRE RRID:IMSR_JAX:037718, MATLAB RRID:SCR_001622

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

Its files are read in the Code ↔ Paper reader above, with 5 matches between paragraphs and lines of code.

Zenodo 19644866

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data and code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
5 files

yyildirimlab/DeepFaceMouse

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 7ed40947a3ec1a06913a7dc3b33a5155cdf79d8f, 11 June 2026
Size: 2 files, 0 scripts
Software Heritage: not archived
Found in: “DeepFaceMouse pipeline architecture and availabi”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
2 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 5 scripts, each with its path and the digest of its content;
  • 5 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Code and data availability statement

The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1016/j.crmeth.2026.101480.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 2, 28 September 2026

  • Authors: added Murat Yildirim (0000-0003-0853-2557); removed Murat Yildirim

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 17 authors, 10 keywords, 6 MeSH terms, 7 funders, 40 references, 6 RRIDs.

Cite

This paper

Ozdemirli, K., Connor, T., Kim, K., Unlu, E. A., Lacin, M. E., Maldonado, M., Bell, F., Leal, T. P., Oksuz, C., Sloan, A., Sarn, N., Chomyk, A., Trapp, B., Lathia, J. D., Mata, I., Eng, C., & Yildirim, M. (2026). DeepFaceMouse enables scalable prediction of large-scale brain activity from facial dynamics in mice. Cell reports methods, 6(8), 101480. https://doi.org/10.1016/j.crmeth.2026.101480

BibTeX

@article{ozdemirli2026deepfacemouse,
author = {Ozdemirli, Kemal and Connor, Tenesha and Kim, Kaleb and Unlu, Ersan Alp and Lacin, Macit Emre and Maldonado, Miguel and Bell, Frederick and Leal, Thiago Peixoto and Oksuz, Caglar and Sloan, Anthony and Sarn, Nicholas and Chomyk, Anthony and Trapp, Bruce and Lathia, Justin D and Mata, Ignacio and Eng, Charis and Yildirim, Murat},
title = {{DeepFaceMouse enables scalable prediction of large-scale brain activity from facial dynamics in mice}},
journal = {Cell reports methods},
year = {2026},
month = jun,
volume = {6},
number = {8},
pages = {101480},
publisher = {Elsevier},
issn = {2667-2375},
doi = {10.1016/j.crmeth.2026.101480},
url = {https://doi.org/10.1016/j.crmeth.2026.101480},
pmid = {42259296},
pmcid = {PMC13494551}
}

RIS

TY - JOUR
AU - Ozdemirli, Kemal
AU - Connor, Tenesha
AU - Kim, Kaleb
AU - Unlu, Ersan Alp
AU - Lacin, Macit Emre
AU - Maldonado, Miguel
AU - Bell, Frederick
AU - Leal, Thiago Peixoto
AU - Oksuz, Caglar
AU - Sloan, Anthony
AU - Sarn, Nicholas
AU - Chomyk, Anthony
AU - Trapp, Bruce
AU - Lathia, Justin D
AU - Mata, Ignacio
AU - Eng, Charis
AU - Yildirim, Murat
TI - DeepFaceMouse enables scalable prediction of large-scale brain activity from facial dynamics in mice
T2 - Cell reports methods
J2 - Cell Rep Methods
PY - 2026
DA - 2026/06/08
VL - 6
IS - 8
SP - 101480
SN - 2667-2375
PB - Elsevier
DO - 10.1016/j.crmeth.2026.101480
UR - https://doi.org/10.1016/j.crmeth.2026.101480
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.crmeth.2026.101480",
"type": "article-journal",
"title": "DeepFaceMouse enables scalable prediction of large-scale brain activity from facial dynamics in mice",
"container-title": "Cell reports methods",
"author": [
{
"family": "Ozdemirli",
"given": "Kemal"
},
{
"family": "Connor",
"given": "Tenesha"
},
{
"family": "Kim",
"given": "Kaleb"
},
{
"family": "Unlu",
"given": "Ersan Alp"
},
{
"family": "Lacin",
"given": "Macit Emre"
},
{
"family": "Maldonado",
"given": "Miguel"
},
{
"family": "Bell",
"given": "Frederick"
},
{
"family": "Leal",
"given": "Thiago Peixoto"
},
{
"family": "Oksuz",
"given": "Caglar"
},
{
"family": "Sloan",
"given": "Anthony"
},
{
"family": "Sarn",
"given": "Nicholas"
},
{
"family": "Chomyk",
"given": "Anthony"
},
{
"family": "Trapp",
"given": "Bruce"
},
{
"family": "Lathia",
"given": "Justin D"
},
{
"family": "Mata",
"given": "Ignacio"
},
{
"family": "Eng",
"given": "Charis"
},
{
"family": "Yildirim",
"given": "Murat"
}
],
"container-title-short": "Cell Rep Methods",
"volume": "6",
"issue": "8",
"page": "101480",
"DOI": "10.1016/j.crmeth.2026.101480",
"PMID": "42259296",
"PMCID": "PMC13494551",
"ISSN": "2667-2375",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.crmeth.2026.101480",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
8
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41593-026-02262-8 [code]
Cheese3D enables sensitive detection and analysis of whole-face movement in mice.
Journal: Nature neuroscience
In common: mouse, 7 references
[2] doi:10.1038/s41467-026-75347-4 [code]
Sleep reveals dynamics integrating and segregating movement and stimulus representations in V1.
Journal: Nature communications
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, mouse, 3 references
[3] doi:10.1038/s41467-026-75492-w [code]
Place and behavioral modulation of hippocampal neurons during immobility.
Journal: Nature communications
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, mouse, 3 references
[4] doi:10.1038/s41467-026-76581-6 [code]
Thalamocortical bursts encode reward contingencies and drive associative learning.
Journal: Nature communications
In common: Deep Learning Toolbox, Signal Processing Toolbox, Statistics and Machine Learning Toolbox, mouse, 1 reference
[5] doi:10.3389/fnins.2026.1790603 [code]
A novel behavioral paradigm using mice to study predictive postural control.
Journal: Frontiers in neuroscience
In common: mouse, 4 references
[6] doi:10.7554/elife.109240 [code]
Neural activity profiles reveal overlapping, intermingled subpopulations spanning area borders in mouse sensorimotor cortex.
Journal: eLife
In common: Statistics and Machine Learning Toolbox, optical imaging (calcium, voltage, 2-photon), mouse, 3 references
[7] doi:10.1038/s41467-026-73622-y [code]
Contextual gating of whisker-evoked responses by frontal cortex supports flexible decision making.
Journal: Nature communications
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, mouse, 3 references
[8] doi:10.1038/s41467-026-71664-w [code]
Dorsal prefrontal cortex drives perseverative behavior in mice.
Journal: Nature communications
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, mouse, 2 references
[9] doi:10.1038/s41598-026-52322-z [code]
A transparent wheel-based platform for locomotion-on-demand and multi-view body and facial kinematics in head-fixed mice.
Journal: Scientific reports
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, methods / tools, mouse, 2 references
[10] doi:10.3389/fendo.2026.1828487 [code]
Castration-induced nigrostriatal deficits are linked to reduced TrkB and loss of mature spines in the dorsal striatum.
Journal: Frontiers in endocrinology
In common: Signal Processing Toolbox, Statistics and Machine Learning Toolbox, mouse, 2 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.