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
|
#include "labeller.h"
#include "settings.h"
#include <fstream>
namespace backend {
struct labeller_impl {
std::vector<std::string> labels;
std::vector<std::string> rectangleLabels;
std::vector<label> annotations;
// Bool in undo/redo buffers is whether was applied or deleted
std::vector<std::pair<bool, label>> undoBuffer;
std::vector<std::pair<bool, label>> redoBuffer;
std::filesystem::path savepath;
};
labeller::labeller(const std::filesystem::path& savepath) {
data = std::shared_ptr<labeller_impl>(new labeller_impl);
data->labels = settings::getLabels();
data->rectangleLabels = settings::getRectangleLabels();
data->savepath = savepath;
}
std::pair<label, label> labeller::getSurrounding(double time) const {
std::pair<label, label> labs;
if(!data->annotations.empty()) {
auto above = data->annotations.rbegin();
auto below = above;
auto rit = above;
while(++rit != data->annotations.rend()) {
if(rit->time > time && (above->time <= time || rit->time < above->time)) above = rit;
if(rit->time <= time && (below->time > time || rit->time > below->time)) below = rit;
}
if(below->time <= time) labs.first = *below;
if(above->time > time) labs.second = *above;
}
return labs;
}
std::vector<std::string> labeller::getLabels() const {
return data->labels;
}
std::vector<std::string> labeller::getRectangleLabels() const {
return data->rectangleLabels;
}
void appLab(std::vector<label>& annotations, label lab) {
annotations.push_back(lab);
}
void labeller::applyLabel(std::string name, double time) {
label lab(name, time);
appLab(data->annotations, lab);
data->undoBuffer.push_back({true, lab});
data->redoBuffer.clear();
}
double abs(double x) {
if(x < 0) x *= -1;
return x;
}
label delLab(std::vector<label>& annotations, double time) {
if(annotations.empty()) {
return label();
}
auto closest = annotations.rbegin();
auto rit = closest;
while(++rit != annotations.rend()) {
if(abs(rit->time - time) < abs(closest->time - time)) {
closest = rit;
}
}
label c(*closest);
annotations.erase(closest.base());
return c;
}
void labeller::deleteLabel(double time) {
label deleted = delLab(data->annotations, time);
data->undoBuffer.push_back({false, deleted});
data->redoBuffer.clear();
}
// Pops action from 'from', applies, and appends to 'to'.
void handleUndoRedo(std::vector<std::pair<bool, label>>& from, std::vector<std::pair<bool, label>>& to, std::vector<label>& annotations) {
if(! from.empty()) {
auto elem = from.back();
from.pop_back();
if(elem.first) { // It was applied, so we must unapply
delLab(annotations, elem.second.time);
} else {
appLab(annotations, elem.second);
}
to.push_back({! elem.first, elem.second});
}
}
void labeller::undo() {
handleUndoRedo(data->undoBuffer, data->redoBuffer, data->annotations);
}
void labeller::redo() {
handleUndoRedo(data->redoBuffer, data->undoBuffer, data->annotations);
}
bool compareLabels(label l1, label l2) {
return (l1.time < l2.time);
}
void labeller::save() const {
std::ofstream out;
out.open(data->savepath);
std::vector<label> a(data->annotations);
std::sort(a.begin(), a.end(), compareLabels);
for(label l : a) {
out << l.name << "," << l.time << std::endl;
}
out.close();
}
}
|