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

paulmthompson / WhiskerToolbox / 17700769195

13 Sep 2025 06:37PM UTC coverage: 71.744% (-0.1%) from 71.842%
17700769195

push

github

paulmthompson
update to new version of whisker tracker

7 of 84 new or added lines in 2 files covered. (8.33%)

112 existing lines in 5 files now uncovered.

37096 of 51706 relevant lines covered (71.74%)

1307.13 hits per line

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

2.29
/src/DataManager/transforms/Media/whisker_tracing.cpp
1
#include "whisker_tracing.hpp"
2

3
#include "Masks/Mask_Data.hpp"
4
#include "whiskertracker.hpp"
5

6
#include <algorithm>
7
#include <cmath>
8
#include <iostream>
9
#include <memory>
10
#include <vector>
11

12
namespace {
13
constexpr uint8_t MASK_TRUE_VALUE = 255;
14
constexpr int PROGRESS_COMPLETE = 100;
15
constexpr double PROGRESS_SCALE = 100.0;
16
}// namespace
17

18
// Convert whisker::Line2D to Line2D
19
Line2D WhiskerTracingOperation::convert_to_Line2D(whisker::Line2D const & whisker_line) {
×
20
    Line2D line;
×
21

22
    for (auto const & point: whisker_line) {
×
23
        line.push_back(Point2D<float>{point.x, point.y});
×
24
    }
25

26
    return line;
×
27
}
×
28

29
// Convert mask data to binary mask format for whisker tracker
NEW
30
std::vector<uint8_t> WhiskerTracingOperation::convert_mask_to_binary(MaskData const * mask_data,
×
31
                                                                     int time_index,
32
                                                                     ImageSize const & image_size) {
NEW
33
    std::vector<uint8_t> binary_mask(static_cast<size_t>(image_size.width * image_size.height), 0);
×
34

NEW
35
    if (!mask_data) {
×
NEW
36
        return binary_mask;// Return empty mask if no mask data
×
37
    }
38

39
    // Get mask at the specified time
NEW
40
    auto const & masks_at_time = mask_data->getAtTime(TimeFrameIndex(time_index));
×
NEW
41
    if (masks_at_time.empty()) {
×
NEW
42
        return binary_mask;// Return empty mask if no mask at this time
×
43
    }
44

45
    // Convert mask points to binary image
46
    // Combine all masks at this time point
NEW
47
    for (auto const & mask: masks_at_time) {
×
NEW
48
        for (auto const & point: mask) {
×
NEW
49
            if (point.x < image_size.width && point.y < image_size.height) {
×
NEW
50
                auto index = static_cast<size_t>(point.y) * static_cast<size_t>(image_size.width) + static_cast<size_t>(point.x);
×
NEW
51
                if (index < binary_mask.size()) {
×
NEW
52
                    binary_mask[index] = MASK_TRUE_VALUE;// Set to 255 for true pixels
×
53
                }
54
            }
55
        }
56
    }
57

58
    return binary_mask;
NEW
59
}
×
60

61
// Clip whisker line by removing points from the end
62
void WhiskerTracingOperation::clip_whisker(Line2D & line, int clip_length) {
×
63
    if (line.size() <= static_cast<std::size_t>(clip_length)) {
×
64
        return;
×
65
    }
66

67
    line.erase(line.end() - clip_length, line.end());
×
68
}
69

70
// Trace whiskers in a single image
71
std::vector<Line2D> WhiskerTracingOperation::trace_single_image(
×
72
        whisker::WhiskerTracker & whisker_tracker,
73
        std::vector<uint8_t> const & image_data,
74
        ImageSize const & image_size,
75
        int clip_length,
76
        MaskData const * mask_data,
77
        int time_index) {
78

79
    std::vector<Line2D> whisker_lines;
×
80

NEW
81
    if (mask_data) {
×
82
        // Use mask-based tracing
NEW
83
        auto binary_mask = convert_mask_to_binary(mask_data, time_index, image_size);
×
NEW
84
        auto whiskers = whisker_tracker.trace_with_mask(image_data, binary_mask, image_size.height, image_size.width);
×
85

NEW
86
        whisker_lines.reserve(whiskers.size());
×
NEW
87
        for (auto const & whisker: whiskers) {
×
NEW
88
            Line2D line = convert_to_Line2D(whisker);
×
NEW
89
            clip_whisker(line, clip_length);
×
NEW
90
            whisker_lines.push_back(std::move(line));
×
NEW
91
        }
×
NEW
92
    } else {
×
93
        // Use standard tracing
NEW
94
        auto whiskers = whisker_tracker.trace(image_data, image_size.height, image_size.width);
×
95

NEW
96
        whisker_lines.reserve(whiskers.size());
×
NEW
97
        for (auto const & whisker: whiskers) {
×
NEW
98
            Line2D line = convert_to_Line2D(whisker);
×
NEW
99
            clip_whisker(line, clip_length);
×
NEW
100
            whisker_lines.push_back(std::move(line));
×
NEW
101
        }
×
UNCOV
102
    }
×
103

104
    return whisker_lines;
×
105
}
×
106

107
// Trace whiskers in multiple images in parallel
108
std::vector<std::vector<Line2D>> WhiskerTracingOperation::trace_multiple_images(
×
109
        whisker::WhiskerTracker & whisker_tracker,
110
        std::vector<std::vector<uint8_t>> const & images,
111
        ImageSize const & image_size,
112
        int clip_length,
113
        MaskData const * mask_data,
114
        std::vector<int> const & time_indices) {
115

NEW
116
    std::vector<std::vector<Line2D>> result;
×
NEW
117
    result.reserve(images.size());
×
118

NEW
119
    if (mask_data && !time_indices.empty()) {
×
120
        // Use mask-based parallel tracing
NEW
121
        std::vector<std::vector<uint8_t>> masks;
×
NEW
122
        masks.reserve(images.size());
×
123

NEW
124
        for (size_t i = 0; i < images.size(); ++i) {
×
NEW
125
            int const time_idx = (i < time_indices.size()) ? time_indices[i] : 0;
×
NEW
126
            auto binary_mask = convert_mask_to_binary(mask_data, time_idx, image_size);
×
NEW
127
            masks.push_back(std::move(binary_mask));
×
NEW
128
        }
×
129

NEW
130
        auto whiskers_batch = whisker_tracker.trace_multiple_images_with_masks(images, masks, image_size.height, image_size.width);
×
131

NEW
132
        for (auto const & whiskers: whiskers_batch) {
×
NEW
133
            std::vector<Line2D> whisker_lines;
×
NEW
134
            whisker_lines.reserve(whiskers.size());
×
135

NEW
136
            for (auto const & whisker: whiskers) {
×
NEW
137
                Line2D line = convert_to_Line2D(whisker);
×
NEW
138
                clip_whisker(line, clip_length);
×
NEW
139
                whisker_lines.push_back(std::move(line));
×
NEW
140
            }
×
141

NEW
142
            result.push_back(std::move(whisker_lines));
×
UNCOV
143
        }
×
NEW
144
    } else {
×
145
        // Use standard parallel tracing
NEW
146
        auto whiskers_batch = whisker_tracker.trace_multiple_images(images, image_size.height, image_size.width);
×
147

NEW
148
        for (auto const & whiskers: whiskers_batch) {
×
NEW
149
            std::vector<Line2D> whisker_lines;
×
NEW
150
            whisker_lines.reserve(whiskers.size());
×
151

NEW
152
            for (auto const & whisker: whiskers) {
×
NEW
153
                Line2D line = convert_to_Line2D(whisker);
×
NEW
154
                clip_whisker(line, clip_length);
×
NEW
155
                whisker_lines.push_back(std::move(line));
×
NEW
156
            }
×
157

NEW
158
            result.push_back(std::move(whisker_lines));
×
NEW
159
        }
×
UNCOV
160
    }
×
161

162
    return result;
×
163
}
×
164

165
std::string WhiskerTracingOperation::getName() const {
148✔
166
    return "Whisker Tracing";
444✔
167
}
168

169
std::type_index WhiskerTracingOperation::getTargetInputTypeIndex() const {
148✔
170
    return typeid(std::shared_ptr<MediaData>);
148✔
171
}
172

173
bool WhiskerTracingOperation::canApply(DataTypeVariant const & dataVariant) const {
×
174
    if (!std::holds_alternative<std::shared_ptr<MediaData>>(dataVariant)) {
×
175
        return false;
×
176
    }
177

178
    auto const * ptr_ptr = std::get_if<std::shared_ptr<MediaData>>(&dataVariant);
×
179
    return ptr_ptr && *ptr_ptr;
×
180
}
181

182
std::unique_ptr<TransformParametersBase> WhiskerTracingOperation::getDefaultParameters() const {
×
183
    return std::make_unique<WhiskerTracingParameters>();
×
184
}
185

186
DataTypeVariant WhiskerTracingOperation::execute(DataTypeVariant const & dataVariant,
×
187
                                                 TransformParametersBase const * transformParameters) {
188
    return execute(dataVariant, transformParameters, [](int) {});
×
189
}
190

191
DataTypeVariant WhiskerTracingOperation::execute(DataTypeVariant const & dataVariant,
×
192
                                                 TransformParametersBase const * transformParameters,
193
                                                 ProgressCallback progressCallback) {
194
    auto const * ptr_ptr = std::get_if<std::shared_ptr<MediaData>>(&dataVariant);
×
195
    if (!ptr_ptr || !(*ptr_ptr)) {
×
196
        std::cerr << "WhiskerTracingOperation::execute: Incompatible variant type or null data." << std::endl;
×
NEW
197
        if (progressCallback) progressCallback(PROGRESS_COMPLETE);
×
198
        return {};
×
199
    }
200

201
    auto media_data = *ptr_ptr;
×
202

203
    auto const * typed_params =
204
            transformParameters ? dynamic_cast<WhiskerTracingParameters const *>(transformParameters) : nullptr;
×
205

206
    if (!typed_params) {
×
207
        std::cerr << "WhiskerTracingOperation::execute: Invalid parameters." << std::endl;
×
NEW
208
        if (progressCallback) progressCallback(PROGRESS_COMPLETE);
×
209
        return {};
×
210
    }
211

212
    auto whisker_tracker = std::make_unique<whisker::WhiskerTracker>();
×
213
    std::cout << "Whisker Tracker Initialized" << std::endl;
×
214

215
    whisker_tracker->setWhiskerLengthThreshold(typed_params->whisker_length_threshold);
×
216

217
    if (progressCallback) progressCallback(0);
×
218

219
    // Create new LineData for the traced whiskers
220
    auto traced_whiskers = std::make_shared<LineData>();
×
221
    traced_whiskers->setImageSize(media_data->getImageSize());
×
222

223
    // Get times with data
224
    auto total_frame_count = media_data->getTotalFrameCount();
×
225
    if (total_frame_count <= 0) {
×
226
        std::cerr << "WhiskerTracingOperation::execute: No data available in media." << std::endl;
×
NEW
227
        if (progressCallback) progressCallback(PROGRESS_COMPLETE);
×
228
        return {};
×
229
    }
230

231
    auto total_time_points = static_cast<size_t>(total_frame_count);
×
232
    size_t processed_time_points = 0;
×
233

234
    // Process frames in batches for parallel processing
235
    if (typed_params->use_parallel_processing && typed_params->batch_size > 1) {
×
236
        for (size_t i = 0; i < total_time_points; i += static_cast<size_t>(typed_params->batch_size)) {
×
237
            std::vector<std::vector<uint8_t>> batch_images;
×
238
            std::vector<int> batch_times;
×
239

240
            // Collect images for this batch
241
            for (size_t j = 0; j < static_cast<size_t>(typed_params->batch_size) && (i + j) < total_time_points; ++j) {
×
242
                auto time = i + j;
×
243
                std::vector<uint8_t> image_data;
×
244

245
                if (typed_params->use_processed_data) {
×
246
                    // whisker tracking expects 8 bit
247
                    image_data = media_data->getProcessedData8(static_cast<int>(time));
×
248
                } else {
249
                    image_data = media_data->getRawData8(static_cast<int>(time));
×
250
                }
251

252
                if (!image_data.empty()) {
×
253
                    batch_images.push_back(std::move(image_data));
×
254
                    batch_times.push_back(static_cast<int>(time));
×
255
                }
256
            }
×
257

258
            if (!batch_images.empty()) {
×
259
                // Trace whiskers in parallel for this batch
NEW
260
                auto batch_results = trace_multiple_images(*whisker_tracker, batch_images, media_data->getImageSize(),
×
NEW
261
                                                           typed_params->clip_length,
×
NEW
262
                                                           typed_params->use_mask_data ? typed_params->mask_data.get() : nullptr,
×
NEW
263
                                                           batch_times);
×
264

265
                // Add results to LineData
266
                for (size_t j = 0; j < batch_results.size(); ++j) {
×
267
                    for (auto const & line: batch_results[j]) {
×
268
                        traced_whiskers->addAtTime(TimeFrameIndex(batch_times[j]), line, false);
×
269
                    }
270
                }
271

272
                processed_time_points += batch_images.size();
×
273
                std::cout << "Processed " << processed_time_points << " time points" << std::endl;
×
274
            }
×
275

276
            if (progressCallback) {
×
NEW
277
                int const current_progress = static_cast<int>(std::round(static_cast<double>(processed_time_points) / static_cast<double>(total_time_points) * PROGRESS_SCALE));
×
278
                progressCallback(current_progress);
×
279
            }
280
        }
×
281
    } else {
×
282
        // Process frames one by one
283
        for (size_t time = 0; time < total_time_points; ++time) {
×
284
            std::vector<uint8_t> image_data;
×
285

286
            if (typed_params->use_processed_data) {
×
287
                image_data = media_data->getProcessedData8(static_cast<int>(time));
×
288
            } else {
289
                image_data = media_data->getRawData8(static_cast<int>(time));
×
290
            }
291

292
            if (!image_data.empty()) {
×
NEW
293
                auto whisker_lines = trace_single_image(*whisker_tracker, image_data, media_data->getImageSize(),
×
NEW
294
                                                        typed_params->clip_length,
×
NEW
295
                                                        typed_params->use_mask_data ? typed_params->mask_data.get() : nullptr,
×
NEW
296
                                                        static_cast<int>(time));
×
297

298
                for (auto const & line: whisker_lines) {
×
299
                    traced_whiskers->addAtTime(TimeFrameIndex(static_cast<int64_t>(time)), line, false);
×
300
                }
301
            }
×
302

303
            processed_time_points++;
×
304
            if (progressCallback) {
×
NEW
305
                int const current_progress = static_cast<int>(std::round(static_cast<double>(processed_time_points) / static_cast<double>(total_time_points) * PROGRESS_SCALE));
×
306
                progressCallback(current_progress);
×
307
            }
308
        }
×
309
    }
310

NEW
311
    if (progressCallback) progressCallback(PROGRESS_COMPLETE);
×
312

313
    std::cout << "WhiskerTracingOperation executed successfully. Traced "
×
314
              << traced_whiskers->GetAllLinesAsRange().size() << " whiskers across "
×
315
              << total_frame_count << " time points." << std::endl;
×
316

317
    return traced_whiskers;
×
318
}
×
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