Repository navigation
Expand file tree
/
Copy pathcaptureGroundTruth.m
More file actions
116 lines (99 loc) · 4.66 KB
/
Copy pathcaptureGroundTruth.m
File metadata and controls
116 lines (99 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
function groundTruth = captureGroundTruth(timepointFolder, fractionToCheck)
%% ONE-TIME MANUAL LABELLING OF A CELL SUBSET (REUSABLE GROUND TRUTH)
% Review a random subset of cells for ONE timepoint (e.g. your noisy 24plus)
% and record, per cell, how many REAL transients you see (0 = noise only).
% Because this is a human judgement of the true signal, it is independent of
% the detection parameters -- so you label once and then score any number of
% parameter combinations against it with tuneCalciumParameters, no re-review.
%
% From the count we derive BOTH:
% - active/inactive truth (active = count > 0) -> classification accuracy
% - per-cell transient count -> count accuracy
% so a single pass scores both metrics.
%
% USAGE:
% captureGroundTruth % pick folder, default 15% of cells
% captureGroundTruth(folder, 0.20) % label 20% of cells
%
% Saves groundTruth_<tag>.mat inside the timepoint folder.
if nargin < 1 || isempty(timepointFolder)
timepointFolder = uigetdir(pwd, 'Select the timepoint folder to label (e.g. the 24plus folder)');
if isequal(timepointFolder, 0), error('Cancelled.'); end
end
if nargin < 2 || isempty(fractionToCheck), fractionToCheck = 0.15; end
thisDir = fileparts(mfilename('fullpath'));
settingsFile = fullfile(thisDir, 'preprocessing_settings.mat');
if ~isfile(settingsFile)
error('preprocessing_settings.mat not found. Run PreProcessingSettingsApp first.');
end
settings = load(settingsFile);
duration = settings.analysisDurationSeconds;
nSmooth = settings.n;
fp = fullfile(timepointFolder, 'fdata.mat');
if ~isfile(fp), error('No fdata.mat in %s', timepointFolder); end
S = load(fp); fn = fieldnames(S); fdata_original = S.(fn{1});
si = ProcessNeuroCa(fdata_original, duration);
ss = smoothCalciumSignals(si, nSmooth);
numCells = size(ss, 1);
if numCells == 0, error('No cells to label after processing.'); end
numCheck = max(1, round(fractionToCheck * numCells));
rng('shuffle');
sel = sort(randperm(numCells, numCheck));
counts = NaN(numCheck, 1);
frames = size(ss, 2);
labeled = 0;
stoppedEarly = false;
for k = 1:numCheck
c = sel(k);
fig = figure('Name', sprintf('Ground truth %d/%d', k, numCheck), 'NumberTitle', 'off');
plot(1:frames, si.fdata(c, 1:frames), '-', 'Color', [0.7 0.7 0.7]); hold on;
plot(1:frames, ss(c, :), 'b', 'LineWidth', 2); hold off;
xlabel('Frame #'); ylabel('dF/F');
legend('Raw (original)', 'Smoothed', 'Location', 'best');
title(sprintf('Cell %d (%d/%d) - count the REAL transients (0 if noise only)', c, k, numCheck));
drawnow;
val = [];
while isempty(val)
answer = inputdlg(sprintf('Cell %d: number of real transients (0 = noise only):', c), ...
'Ground Truth', 1, {'0'});
if isempty(answer) % Cancel -> stop and keep what we have
stoppedEarly = true;
break;
end
v = str2double(answer{1});
if ~isnan(v) && v >= 0 && v == round(v)
val = v;
else
uiwait(msgbox('Please enter a non-negative whole number.', 'Invalid', 'warn'));
end
end
if stoppedEarly
if ishghandle(fig), close(fig); end
break;
end
counts(k) = val;
labeled = labeled + 1;
if ishghandle(fig), close(fig); end
end
if labeled == 0, error('No cells were labelled.'); end
if stoppedEarly
fprintf('Labelling stopped early. Keeping %d labelled cell(s).\n', labeled);
end
sel = sel(1:labeled);
counts = counts(1:labeled);
[~, tag] = fileparts(timepointFolder);
groundTruth = struct();
groundTruth.tag = tag;
groundTruth.selectedCells = sel(:); % indices into processed signal
groundTruth.transientCounts = counts(:);
groundTruth.isActive = counts(:) > 0;
groundTruth.fractionChecked = fractionToCheck;
groundTruth.n_atLabeling = nSmooth; % smoothing shown while labelling
groundTruth.analysisDurationSeconds = duration; % must match when scoring
groundTruth.numCellsTotalProcessed = numCells;
groundTruth.createdOn = char(datetime('now'));
outFile = fullfile(timepointFolder, ['groundTruth_' tag '.mat']);
save(outFile, 'groundTruth');
fprintf('Saved %d labels to %s\n', labeled, outFile);
fprintf(' Active: %d Inactive: %d\n', sum(groundTruth.isActive), sum(~groundTruth.isActive));
end