• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / Whisker-Analysis / 13950233794

19 Mar 2025 03:20PM UTC coverage: 79.893% (+1.6%) from 78.293%
13950233794

push

github

paulmthompson
add catch2 to exclusion

1339 of 1676 relevant lines covered (79.89%)

63493762.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

32.61
/src/WhiskerTracking/whiskertracker.cpp
1
#include "whiskertracker.hpp"
2

3
#include "JaneliaWhiskerTracker/io.hpp"
4
#include "Geometry/lines.hpp"
5

6
#include <omp.h>
7

8
#include <algorithm>
9
#include <chrono>
10
#include <cmath>
11
#include <iostream>
12
#include <numeric>
13

14
janelia::JaneliaTracker whisker::WhiskerTracker::_janelia;
144✔
15
bool whisker::WhiskerTracker::_janelia_init;
16

17
namespace whisker {
18

19
janelia::Image<uint8_t> bg = janelia::Image<uint8_t>(640, 480, std::vector<uint8_t>(640 * 480, 0));
20

21
WhiskerTracker::WhiskerTracker()
4✔
22
{
23
    _janelia = janelia::JaneliaTracker();
4✔
24
    _janelia_init = false;
4✔
25
}
4✔
26

27
std::vector<std::vector<Line2D>> WhiskerTracker::trace_multiple_images(const std::vector<std::vector<uint8_t>> & images, const int image_height, const int image_width) {
3✔
28

29
    std::vector<std::vector<Line2D>> whiskers(images.size());
3✔
30

31
    #ifndef _MSC_VER
32
    #pragma omp parallel
3✔
33
    {
34
        _reinitializeJanelia();
35
    }
36
    #endif
37

38
    #ifndef _MSC_VER
39
    #pragma omp parallel for
3✔
40
    #endif
41
    for (int i = 0; i < static_cast<int>(images.size()); i++) {
42
        whiskers[i] = trace(images[i], image_height, image_width);
43
    }
44

45
    return whiskers;
3✔
46
}
47

48
std::vector<Line2D> WhiskerTracker::trace(const std::vector<uint8_t> & image, const int image_height, const int image_width) {
112✔
49

50
    _reinitializeJanelia();
112✔
51

52
    std::vector<Line2D> whiskers{};
112✔
53

54
    auto t0 = std::chrono::high_resolution_clock::now();
112✔
55

56
    auto img = janelia::Image<uint8_t>(image_width, image_height, image);
112✔
57

58
    auto t1 = std::chrono::high_resolution_clock::now();
112✔
59

60
    auto j_segs = _janelia.find_segments(1, img, bg);
112✔
61

62
    auto t2 = std::chrono::high_resolution_clock::now();
112✔
63

64
    for (auto &w_seg: j_segs) {
3,038✔
65
        auto whisker = create_line(w_seg.x, w_seg.y);
2,926✔
66
        if (length(whisker) > _whisker_length_threshold) {
2,926✔
67
            whiskers.push_back(std::move(whisker));
2,926✔
68
        }
69
    }
2,926✔
70

71
    auto t3 = std::chrono::high_resolution_clock::now();
112✔
72

73
    remove_duplicates(whiskers);
112✔
74
    std::ranges::for_each(whiskers, [wp=_whisker_pad](Line2D & w)
224✔
75
    {align_whisker_to_follicle(w, wp);});
780✔
76

77
    auto t4 = std::chrono::high_resolution_clock::now();
112✔
78

79
    _connectToFaceMask(whiskers);
112✔
80

81
    auto t5 = std::chrono::high_resolution_clock::now();
112✔
82

83
    remove_whiskers_outside_radius(whiskers, _whisker_pad, _whisker_pad_radius);
112✔
84

85
    auto t6 = std::chrono::high_resolution_clock::now();
112✔
86

87
    order_whiskers(whiskers, _head_direction_vector);
112✔
88

89
    auto t7 = std::chrono::high_resolution_clock::now();
112✔
90

91
    if (_verbose) {
112✔
92
        std::cout << "Image conversion: " << std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count() << "ms" << std::endl;
×
93
        std::cout << "Janelia find segments: " << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << "ms" << std::endl;
×
94
        std::cout << "Create whiskers: " << std::chrono::duration_cast<std::chrono::milliseconds>(t3 - t2).count() << "ms" << std::endl;
×
95
        std::cout << "Remove duplicates: " << std::chrono::duration_cast<std::chrono::milliseconds>(t4 - t3).count() << "ms" << std::endl;
×
96
        std::cout << "Connect to face mask: " << std::chrono::duration_cast<std::chrono::milliseconds>(t5 - t4).count() << "ms" << std::endl;
×
97
        std::cout << "Remove whiskers by whisker pad radius: " << std::chrono::duration_cast<std::chrono::milliseconds>(t6 - t5).count() << "ms" << std::endl;
×
98
        std::cout << "Order whiskers: " << std::chrono::duration_cast<std::chrono::milliseconds>(t7 - t6).count() << "ms" << std::endl;
×
99
    }
100

101
    return whiskers;
224✔
102
}
112✔
103

104
std::map<int, std::vector<Line2D>> load_janelia_whiskers(std::string const & filename) {
×
105
    auto j_segs = janelia::load_binary_data(filename);
×
106

107
    auto output_whiskers = std::map<int, std::vector<Line2D>>();
×
108

109
    for (auto const & w_seg: j_segs) {
×
110

111
        if (!output_whiskers.contains(w_seg.time)) { // Key doesn't exist
×
112
            output_whiskers[w_seg.time] = std::vector<Line2D>();
×
113
        }
114

115
        output_whiskers[w_seg.time].push_back(create_line(w_seg.x, w_seg.y));
×
116

117
    }
118

119
    return output_whiskers;
×
120
}
×
121

122
void WhiskerTracker::setHeadDirection(float x, float y)
×
123
{
124
    _head_direction_vector = GeomVector{x,y};
×
125
    _head_direction_vector = normalize(_head_direction_vector);
×
126
}
×
127

128
void WhiskerTracker::changeJaneliaParameter(JaneliaParameter parameter, float value) {
×
129
    switch (parameter) {
×
130
        case SEED_ON_GRID_LATTICE_SPACING: {
×
131
            _janelia.config._lattice_spacing = static_cast<int>(value);
×
132
            break;
×
133
        }
134
        case SEED_SIZE_PX: {
×
135
            _janelia.config._maxr = static_cast<int>(value);
×
136
            break;
×
137
        }
138
        case SEED_ITERATIONS: {
×
139
            _janelia.config._maxiter = static_cast<int>(value);
×
140
            break;
×
141
        }
142
        case SEED_ITERATION_THRESH: {
×
143
            _janelia.config._iteration_thres = value;
×
144
            break;
×
145
        }
146
        case SEED_ACCUM_THRESH: {
×
147
            _janelia.config._accum_thres = value;
×
148
            break;
×
149
        }
150
        case SEED_THRESH: {
×
151
            _janelia.config._accum_thres = value;
×
152
            break;
×
153
        }
154
        case HAT_RADIUS: {
×
155

156
        }
157
        case MIN_LEVEL: {
×
158

159
        }
160
        case MIN_SIZE: {
×
161

162
        }
163
        case TLEN: {
×
164
            _janelia.config._tlen = value;
×
165
            _reinitializeJanelia();
×
166
            break;
×
167
        }
168
        case OFFSET_STEP: {
×
169
            _janelia.config._offset_step = value;
×
170
            _reinitializeJanelia();
×
171
            break;
×
172
        }
173
        case ANGLE_STEP: {
×
174
            _janelia.config._angle_step = value;
×
175
            _reinitializeJanelia();
×
176
            break;
×
177
        }
178
        case WIDTH_STEP: {
×
179
            _janelia.config._width_step = value;
×
180
            _reinitializeJanelia();
×
181
        }
182
        case WIDTH_MIN: {
×
183
            // Must be multiple of width step
184
            _janelia.config._width_min = value;
×
185
            _reinitializeJanelia();
×
186
            break;
×
187
        }
188
        case WIDTH_MAX: {
×
189
            _janelia.config._width_max = value;
×
190
            _reinitializeJanelia();
×
191
            break;
×
192
        }
193
        case MIN_SIGNAL: {
×
194
            _janelia.config._min_signal = value;
×
195
            _reinitializeJanelia();
×
196
            break;
×
197
        }
198
        case MAX_DELTA_ANGLE: {
×
199
            _janelia.config._max_delta_angle = value;
×
200
            _reinitializeJanelia();
×
201
            break;
×
202
        }
203
        case MAX_DELTA_WIDTH: {
×
204
            _janelia.config._max_delta_width = value;
×
205
            _reinitializeJanelia();
×
206
            break;
×
207
        }
208
        case MAX_DELTA_OFFSET: {
×
209
            _janelia.config._max_delta_offset = value;
×
210
            _reinitializeJanelia();
×
211
            break;
×
212
        }
213
        case HALF_SPACE_ASSYMETRY_THRESH: {
×
214
            _janelia.config._half_space_assymetry = value;
×
215
            _reinitializeJanelia();
×
216
            break;
×
217
        }
218
        case HALF_SPACE_TUNNELING_MAX_MOVES: {
×
219
            _janelia.config._half_space_tunneling_max_moves = value;
×
220
            _reinitializeJanelia();
×
221
            break;
×
222
        }
223
    }
224
}
×
225

226
void WhiskerTracker::_reinitializeJanelia() {
124✔
227
    if (_janelia_init == false) {
124✔
228
        _janelia.bank = janelia::LineDetector(_janelia.config);
7✔
229
        _janelia.half_space_bank = janelia::HalfSpaceDetector(_janelia.config);
7✔
230
        _janelia_init = true;
7✔
231
    }
232
}
124✔
233

234
void WhiskerTracker::_connectToFaceMask(std::vector<Line2D> & whiskers)
112✔
235
{
236
    if (_face_mask.empty()) {
112✔
237
        return;
238
    }
239

240
    for (auto &w: whiskers) {
×
241

242
        whisker::extend_line_to_mask(w, _face_mask_set, _image_width, _image_height);
×
243
    }
244
}
245

246
} // namespace whisker
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc