-
Notifications
You must be signed in to change notification settings - Fork 0
/
manuallyLabelData.m
247 lines (197 loc) · 7.51 KB
/
manuallyLabelData.m
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
function [labels, timeSpent] = manuallyLabelData(basePath, date, scanName, dataFilename, structName)
% manuallyLabelData Manually label insect lidar images
%
% Inputs:
% basePath - path to where the date folders are
% date - the date folder where the images are located
% scanName - name of the scan folder to label
% dataFilename (optional) - filename of the preprocessed mat file
% structName (optional) - name of the data structure in the mat file
%
% Outputs:
% labels - table of label information
% timeSpent - how many seconds you spent labeling the data
%
% Example usage:
% manuallyLabelData('../data/msu-bee-hives', '2022-06-23', 'MSU-horticulture-farm-bees-122126')
arguments
basePath (1,1) string;
date (1,1) string;
scanName (1,1) string;
dataFilename (1,1) string = "adjusted_data_junecal_volts";
structName (1,1) string = "adjusted_data_junecal"
end
tic;
% table to store label information
labels = table();
dataStruct = load(basePath + filesep + date + filesep + scanName + ...
filesep + dataFilename);
nImages = numel(dataStruct.(structName));
% rename the data structure for convenience
data = dataStruct.(structName);
% For each image in the scan
for imageNum = 1:nImages
% plot the image for manual inspection;
% the voltage from the PMT is between -1.5 and 0.5 volts
imagesc(data(imageNum).data);
colormap(flipud(colormap('parula')));
colorbar;
title("image " + num2str(imageNum))
% pause so the image actually shows
% up...
% for some reason, the figure window was not showing up
% before the user prompt was reached
pause(0.00001)
% while there are still potential insects in the image
while true
% prompt the user for inect presence
insectPresent = promptForInsectPresence(imageNum, nImages);
if insectPresent
% get insect details (row, start column, end column) from the user
[startRow, endRow, startCol, endCol] = promptForInsectDetails();
% look at the fourier transform of the insect to further verify the presence/non-presence of the potential inesct.
spectrumFigHandle = plotInsectSpectrum(data(imageNum), startRow, endRow, startCol, endCol);
pause(0.00001);
% prompt the user for their confidence in the potential insect
confidence = promptForInsectConfidence();
try
close(spectrumFigHandle);
catch ME
end
comments = string(input("Comments on the insect or your label decisions? ", "s"));
labels = addInsectToLabels(labels, date, scanName, imageNum, startRow, endRow, startCol, endCol, confidence, comments);
else
% no more insects are present, so stop the loop
break;
end
end
% save the labels after every image just in case something goes wrong...
% don't want to lose all that time staring at images...
writetable(labels, basePath + filesep + date + filesep + scanName + ...
filesep + "labels.csv");
end
timeSpent = toc
end
function [startRow, endRow, startCol, endCol] = promptForInsectDetails()
restart = true;
while restart
restart = false;
while true
startRow = input("First row of inesct (0 to restart): ");
if startRow == 0
restart = true;
break;
elseif startRow < 1
warning("invalid input; must be a positive number");
else
break;
end
end
if restart
continue;
end
while true
endRow = input("Last row of inesct (0 to restart): ");
if endRow == 0
restart = true;
break;
elseif endRow < startRow
warning("invalid input; must be >= first insect row");
else
break;
end
end
if restart
continue;
end
while true
startCol = input("Starting column for insect (0 to restart): ");
if startCol == 0
restart = true;
break;
elseif startCol < 1
warning("invalid input; must be a positive number");
else
break;
end
end
if restart
continue;
end
while true
endCol = input("Ending column for insect (0 to restart): ");
if endCol == 0
restart = true;
break;
elseif endCol < startCol
warning("invalid input; must be >= first insect column");
else
break;
end
end
end
end
function insectPresent = promptForInsectPresence(imageNum, nImages)
inputIsInvalid = true;
while inputIsInvalid
userInput = input("Are there any (more) potential insects in this image? (" + num2str(imageNum) + " out of " + num2str(nImages) +") ", "s");
if strcmpi(userInput, "y")
inputIsInvalid = false;
insectPresent = true;
elseif strcmpi(userInput, "n")
inputIsInvalid = false;
insectPresent = false;
else
warning("invalid input: " + userInput + ". Should be 'y' or 'n'");
end
end
end
function confidence = promptForInsectConfidence()
inputIsInvalid = true;
while inputIsInvalid
confidence = input("How likely is this an insect (0--4: 0 -> not an insect, 4 --> definitely an insect)? ");
if (0 <= confidence) && (confidence <= 4)
inputIsInvalid = false;
else
inputIsInvalid = true;
warning("invalid input: needs to be between 0 and 4");
end
end
end
function figHandle = plotInsectSpectrum(data, startRow, endRow, startCol, endCol)
insectRows = data.data(startRow:endRow, startCol:endCol);
% sometimes the insects span multiple rows because there's a range bin
% uncertainty due to the lidar instrument.
% Add all insect rows together to get better SNR
insect = sum(insectRows);
FFT_SIZE = 1024;
% compute the pulse reptition frequency, i.e. PRF, i.e. sampling frequency
prf = 1/mean(diff(data.time));
insectSpectrum = abs(fft(insect, FFT_SIZE)).^2;
% we only need to look at the one-sided spectrum
insectSpectrum = insectSpectrum(1:end/2);
f = linspace(0, prf/2, numel(insectSpectrum));
figHandle = figure;
subplot(2,1,1)
plot(data.time(startCol:endCol), insectRows, 'LineWidth', 1.2);
xlabel('seconds');
subplot(2,1,2)
plot(f, insectSpectrum, 'LineWidth', 1.2);
xlabel('hertz');
end
function labels = addInsectToLabels(labels, date, scanName, imageNum, startRow, endRow, startCol, endCol, confidence, comments)
labelData.date = date;
labelData.scanName = scanName;
labelData.imageNum = imageNum;
labelData.startRow = startRow;
labelData.endRow = endRow;
labelData.startCol = startCol;
labelData.endCol = endCol;
labelData.comments = comments;
if confidence > 0
labelData.confidence = confidence;
labels = [labels; struct2table(labelData)];
else
% no insect, so don't add a label to the table
end
end