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

paulmthompson / Whisker-Analysis / 14520820659

17 Apr 2025 04:57PM UTC coverage: 79.869% (-0.02%) from 79.893%
14520820659

push

github

paulmthompson
fix error in const with intersect method

20 of 33 new or added lines in 1 file covered. (60.61%)

3 existing lines in 1 file now uncovered.

1337 of 1674 relevant lines covered (79.87%)

63565625.03 hits per line

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

64.63
/src/WhiskerParameterization/whisker.cpp
1
#include "whisker.hpp"
2

3
#include "Geometry/lines.hpp"
4
#include "Geometry/mask.hpp"
5
#include "Geometry/vector.hpp"
6

7
#include <algorithm>
8
#include <cmath>
9
#include <cstddef>
10
#include <cstdint>
11
#include <functional>
12
#include <iostream>
13
#include <limits>
14
#include <numeric>
15

16
namespace whisker {
17

18
std::set<Point2D<int>> create_set(std::vector<Point2D<int>> const & points) {
2,130,654✔
19
    return std::set<Point2D<int>>{points.begin(), points.end()};
2,130,654✔
20
}
21

22
std::set<Point2D<int>> create_set(std::vector<Point2D<float>> const & points) {
69,156✔
23
    std::set<Point2D<int>> s;
69,156✔
24
    for (auto const & p: points) {
7,730,214✔
25
        s.insert(Point2D<int>{
7,661,058✔
26
                static_cast<int>(std::lround(p.x)),
7,661,058✔
27
                static_cast<int>(std::lround(p.y))});
7,661,058✔
28
    }
29
    return s;
69,156✔
30
}
×
31

32
bool intersect(Point2D<float> const p1, Mask2D const & mask) {
2,130,653✔
33
    auto mask_set = create_set(mask);
2,130,653✔
34

35
    return intersect(p1, mask_set);
2,130,653✔
36
}
2,130,653✔
37

38
bool intersect(Point2D<float> const p1, std::set<Point2D<int>> const & mask_set) {
2,130,653✔
39
    Point2D<int> p{
2,130,653✔
40
            static_cast<int>(std::lround(p1.x)),
2,130,653✔
41
            static_cast<int>(std::lround(p1.y))};
2,130,653✔
42

43
    return mask_set.find(p) != mask_set.end();
2,130,653✔
44
}
45

46
float calculate_overlap_iou(std::set<Point2D<int>> const & l1_set, std::set<Point2D<int>> const & l2_set) {
16,584✔
47

48
    std::vector<whisker::Point2D<int>> v_intersection;
16,584✔
49
    std::vector<whisker::Point2D<int>> v_union;
16,584✔
50

51
    std::set_intersection(l1_set.begin(), l1_set.end(), l2_set.begin(), l2_set.end(),
16,584✔
52
                          std::back_inserter(v_intersection));
53

54
    std::set_union(l1_set.begin(), l1_set.end(), l2_set.begin(), l2_set.end(),
16,584✔
55
                   std::back_inserter(v_union));
56

57
    return static_cast<float>(v_intersection.size()) / static_cast<float>(v_union.size());
33,168✔
58
}
16,584✔
59

60
float calculate_overlap_iou(Line2D const & line, Line2D const & line2) {
16,584✔
61
    auto l1_set = create_set(line);
16,584✔
62
    auto l2_set = create_set(line2);
16,584✔
63

64
    return calculate_overlap_iou(l1_set, l2_set);
16,584✔
65
}
16,584✔
66

67
float calculate_overlap_iou_relative(std::set<Point2D<int>> const & l1_set, std::set<Point2D<int>> const & l2_set) {
50,640✔
68

69
    std::vector<whisker::Point2D<int>> v_intersection;
50,640✔
70

71
    std::set_intersection(l1_set.begin(), l1_set.end(), l2_set.begin(), l2_set.end(),
50,640✔
72
                          std::back_inserter(v_intersection));
73

74
    float iou_1 = static_cast<float>(v_intersection.size()) / static_cast<float>(l1_set.size());
50,640✔
75
    float iou_2 = static_cast<float>(v_intersection.size()) / static_cast<float>(l2_set.size());
50,640✔
76

77
    return std::max(iou_1, iou_2);
101,280✔
78
}
50,640✔
79

80
float calculate_overlap_iou_relative(Line2D const & line, Line2D const & line2) {
16,584✔
81
    auto l1_set = create_set(line);
16,584✔
82
    auto l2_set = create_set(line2);
16,584✔
83

84
    return calculate_overlap_iou_relative(l1_set, l2_set);
16,584✔
85
}
16,584✔
86

NEW
87
void extend_line_to_mask(Line2D & line, std::set<Point2D<int>> const & mask, int const x_bound, int const y_bound) {
×
UNCOV
88
    std::size_t vec_index = 5;
×
NEW
89
    auto const line_spacing = 2.0f;
×
90

91
    auto point = line[0];
×
92

NEW
93
    if (vec_index > line.size()) {
×
UNCOV
94
        std::cout << "Requested index " << vec_index << " out of bounds" << std::endl;
×
95
        vec_index = 1;
96
    }
97

98
    auto reference_point = line[vec_index];
×
NEW
99
    auto dir_vector = create_vector(reference_point, point);
×
100

101
    dir_vector = normalize(dir_vector);
×
102

103
    point = create_point(dir_vector, point);
×
104
    reference_point = point;
×
105

NEW
106
    while (!whisker::intersect(point, mask)) {
×
107
        point = create_point(dir_vector, point);
×
NEW
108
        if (point.x < 0 || point.y < 0 || point.x > x_bound || point.y > y_bound) {
×
109
            break;
110
        }
111
    }
112

113
    auto extended_line = linspace(point, line[0], line_spacing);
×
114

115
    line.insert(line.begin(), extended_line.begin(), extended_line.end() - 1);
×
UNCOV
116
}
×
117

118
void remove_duplicates(std::vector<Line2D> & whiskers) {
112✔
119

120
    struct correlation_matrix {
112✔
121
        int i;
122
        int j;
123
        float corr;
124
    };
125

126
    auto correlation_threshold = 0.2f;
112✔
127

128
    auto cor_mat = std::vector<correlation_matrix>();
112✔
129

130
    auto whisker_sets = std::vector<std::set<whisker::Point2D<int>>>();
112✔
131
    for (auto const & w: whiskers) {
2,928✔
132
        whisker_sets.push_back(whisker::create_set(w));
5,632✔
133
    }
134

135
    for (int i = 0; i < whisker_sets.size(); i++) {
2,928✔
136

137
        for (int j = i + 1; j < whisker_sets.size(); j++) {
36,872✔
138

139
            auto this_cor = calculate_overlap_iou_relative(whisker_sets[i], whisker_sets[j]);
34,056✔
140

141
            if (this_cor > correlation_threshold) {
34,056✔
142
                cor_mat.push_back(correlation_matrix{i, j, this_cor});
4,734✔
143
            }
144
        }
145
    }
146

147
    auto erase_inds = std::vector<std::size_t>();
112✔
148
    for (std::size_t i = 0; i < cor_mat.size(); i++) {
4,846✔
149
        //std::cout << "Whiskers " << cor_mat[i].i << " and " << cor_mat[i].j << " are the same" << std::endl;
150

151
        if (length(whiskers[cor_mat[i].i]) > length(whiskers[cor_mat[i].j])) {
4,734✔
152
            erase_inds.push_back(cor_mat[i].j);
2,202✔
153
        } else {
154
            erase_inds.push_back(cor_mat[i].i);
2,532✔
155
        }
156
    }
157

158
    erase_whiskers(whiskers, erase_inds);
112✔
159
}
112✔
160

161
void erase_whiskers(std::vector<Line2D> & whiskers, std::vector<std::size_t> & erase_inds) {
224✔
162
    std::ranges::sort(erase_inds, std::greater<>());
224✔
163
    auto last = std::unique(erase_inds.begin(), erase_inds.end());
224✔
164
    erase_inds.erase(last, erase_inds.end());
224✔
165

166
    for (auto & erase_ind: erase_inds) {
2,480✔
167
        whiskers.erase(whiskers.begin() + erase_ind);
2,256✔
168
    }
169
}
224✔
170

171
std::tuple<float, int> get_nearest_whisker(std::vector<Line2D> & whiskers, float x_p, float y_p) {
×
172

173
    float nearest_distance = std::numeric_limits<float>::max();
×
174
    int whisker_id = 0;
×
175

176
    int current_whisker_id = 0;
×
177

NEW
178
    for (auto & w: whiskers) {
×
179
        for (std::size_t i = 0; i < w.size(); i++) {
×
180
            float dx = x_p - w[i].x;
×
181
            float dy = y_p - w[i].y;
×
NEW
182
            float current_d2 = dx * dx + dy * dy;
×
183
            if (current_d2 < nearest_distance) {
×
184
                nearest_distance = current_d2;
×
185
                whisker_id = current_whisker_id;
×
186
            }
187
        }
188
        current_whisker_id += 1;
×
189
    }
190
    nearest_distance = static_cast<float>(std::sqrt(static_cast<double>(nearest_distance)));
×
191
    return std::make_tuple(nearest_distance, whisker_id);
×
192
}
193

194

195
void align_whisker_to_follicle(Line2D & whisker, whisker::Point2D<float> const whisker_pad) {
780✔
196

197
    //Check if whisker is empty
198
    if (whisker.empty()) {
780✔
199
        return;
200
    }
201

202
    auto start_distance = distance2(whisker[0], whisker_pad);
780✔
203

204
    auto end_distance = distance2(whisker.back(), whisker_pad);
780✔
205

206
    if (start_distance > end_distance) {
780✔
207
        std::ranges::reverse(whisker);
780✔
208
    }
209
}
210

211
void order_whiskers(std::vector<Line2D> & whiskers, GeomVector const & head_direction_vector) {
112✔
212
    std::vector<float> w_projection_vector;
112✔
213
    for (auto const & w: whiskers) {
672✔
214
        w_projection_vector.push_back(project(head_direction_vector, w[0]));
560✔
215
    }
216

217
    auto position_order = std::vector<std::size_t>(w_projection_vector.size());
112✔
218
    std::iota(position_order.begin(), position_order.end(), 0);
112✔
219
    std::sort(
112✔
220
            std::begin(position_order),
221
            std::end(position_order),
222
            [&](std::size_t i1, std::size_t i2) { return w_projection_vector[i1] > w_projection_vector[i2]; });
1,014✔
223

224
    /*
225
    for (std::size_t i = 0; i < position_order.size(); i++) {
226

227
        std::cout << "The " << i << " position whisker is " << position_order[i];
228
        std::cout << " with follicle at " << "(" << whiskers[position_order[i]][0].x << ","
229
                << whiskers[position_order[i]][0].y << ")" << std::endl;
230
    }
231
    */
232

233
    std::vector<Line2D> sorted_whiskers;
112✔
234
    for (std::size_t i: position_order) {
672✔
235
        sorted_whiskers.push_back(whiskers[i]);
560✔
236
    }
237

238
    whiskers = sorted_whiskers;
112✔
239
}
112✔
240

241
void remove_whiskers_outside_radius(std::vector<Line2D> & whiskers, Point2D<float> whisker_pad, float radius) {
112✔
242

243
    auto erase_inds = std::vector<std::size_t>();
112✔
244

245
    for (std::size_t i = 0; i < whiskers.size(); i++) {
892✔
246
        auto distance_to_follicle = distance(whiskers[i][0], whisker_pad);
780✔
247

248
        if (distance_to_follicle > radius) {
780✔
249
            erase_inds.push_back(i);
220✔
250
        }
251
    }
252

253
    whisker::erase_whiskers(whiskers, erase_inds);
112✔
254
}
112✔
255

256
//This was based on python code found here that was very helpful.
257
//https://github.com/joaofig/discrete-frechet
258
/*
259
MIT License
260

261
Copyright (c) 2019 João Paulo Figueira
262

263
Permission is hereby granted, free of charge, to any person obtaining a copy
264
of this software and associated documentation files (the "Software"), to deal
265
in the Software without restriction, including without limitation the rights
266
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
267
copies of the Software, and to permit persons to whom the Software is
268
furnished to do so, subject to the following conditions:
269

270
The above copyright notice and this permission notice shall be included in all
271
copies or substantial portions of the Software.
272

273
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
274
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
275
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
276
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
277
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
278
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
279
SOFTWARE.
280
*/
NEW
281
float fast_discrete_frechet_matrix(Line2D const & P, Line2D const & Q) {
×
282
    size_t n = P.size();
×
283
    size_t m = Q.size();
×
284
    std::vector<std::vector<float>> ca(n, std::vector<float>(m, -1.0f));
×
285

286
    std::function<float(std::size_t, std::size_t)> c = [&](size_t i, size_t j) -> float {
×
287
        if (ca[i][j] > -1) {
×
288
            return ca[i][j];
289
        } else if (i == 0 && j == 0) {
×
290
            ca[i][j] = distance(P[0], Q[0]);
×
291
        } else if (i > 0 && j == 0) {
×
NEW
292
            ca[i][j] = std::max(c(i - 1, 0), distance(P[i], Q[0]));
×
293
        } else if (i == 0 && j > 0) {
×
NEW
294
            ca[i][j] = std::max(c(0, j - 1), distance(P[0], Q[j]));
×
295
        } else if (i > 0 && j > 0) {
×
NEW
296
            ca[i][j] = std::max(std::min({c(i - 1, j), c(i - 1, j - 1), c(i, j - 1)}), distance(P[i], Q[j]));
×
297
        } else {
298
            ca[i][j] = std::numeric_limits<float>::infinity();
×
299
        }
300
        return ca[i][j];
×
301
    };
×
302

NEW
303
    return c(n - 1, m - 1);
×
304
}
×
305

306
}// 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