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

paulmthompson / WhiskerToolbox / 17410599011

02 Sep 2025 04:52PM UTC coverage: 71.464% (+0.07%) from 71.394%
17410599011

push

github

paulmthompson
single channel colormaps added

0 of 155 new or added lines in 1 file covered. (0.0%)

75 existing lines in 4 files now uncovered.

31905 of 44645 relevant lines covered (71.46%)

1385.34 hits per line

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

82.69
/src/DataManager/transforms/Lines/Line_Alignment/line_alignment.cpp
1
#include "line_alignment.hpp"
2

3
#include "Lines/Line_Data.hpp"
4
#include "Media/Media_Data.hpp"
5
#include "CoreGeometry/line_geometry.hpp"
6

7
#include <algorithm>
8
#include <cmath>
9
#include <iostream>
10
#include <limits>
11
#include <map>
12
#include <vector>
13

14

15
template<typename T>
16
T get_pixel_value(Point2D<float> const & point,
30,847✔
17
                  std::vector<T> const & image_data,
18
                  ImageSize const & image_size) {
19
    int x = static_cast<int>(std::round(point.x));
30,847✔
20
    int y = static_cast<int>(std::round(point.y));
30,847✔
21

22
    // Check bounds
23
    if (x < 0 || x >= image_size.width || y < 0 || y >= image_size.height) {
30,847✔
24
        return 0;
1,224✔
25
    }
26

27
    size_t index = static_cast<size_t>(y * image_size.width + x);
29,623✔
28
    //size_t index = static_cast<size_t>(x * image_size.height + y);
29
    if (index < image_data.size()) {
29,623✔
30
        return image_data[index];
29,062✔
31
    }
32

33
    return 0;
561✔
34
}
35

36
// Calculate FWHM center point for a single vertex
37
template<typename T>
38
Point2D<float> calculate_fwhm_center(Point2D<float> const & vertex,
37✔
39
                                     Point2D<float> const & perpendicular_dir,
40
                                     int width,
41
                                     int perpendicular_range,
42
                                     std::vector<T> const & image_data,
43
                                     ImageSize const & image_size,
44
                                     FWHMApproach /*approach*/) {
45
    if (width <= 0) {
37✔
46
        return vertex;// Return original vertex if no width
1✔
47
    }
48

49
    std::vector<Point2D<float>> center_points;
36✔
50
    std::vector<float> intensities;
36✔
51

52
    // Sample multiple points along the width of the analysis strip
53
    for (int w = -width / 2; w <= width / 2; ++w) {
1,183✔
54
        // Calculate the sampling point along the width direction
55
        Point2D<float> width_dir{-perpendicular_dir.y, perpendicular_dir.x};// Perpendicular to perp dir
550✔
56

57
        Point2D<float> sample_start = {
550✔
58
                vertex.x + width_dir.x * static_cast<float>(w),
550✔
59
                vertex.y + width_dir.y * static_cast<float>(w)};
550✔
60

61
        // Sample intensity profile along the perpendicular direction
62
        std::vector<T> profile;
550✔
63
        std::vector<float> distances;
550✔
64

65
        // Sample up to perpendicular_range pixels in each direction along the perpendicular
66
        for (int d = -perpendicular_range / 2; d <= perpendicular_range / 2; ++d) {
28,182✔
67
            Point2D<float> sample_point = {
27,632✔
68
                    sample_start.x + perpendicular_dir.x * static_cast<float>(d),
27,632✔
69
                    sample_start.y + perpendicular_dir.y * static_cast<float>(d)};
27,632✔
70

71
            T intensity = get_pixel_value(sample_point, image_data, image_size);
27,632✔
72
            profile.push_back(intensity);
27,632✔
73
            distances.push_back(static_cast<float>(d));
27,632✔
74
        }
75

76
        // Find the maximum intensity in the profile
77
        auto max_it = std::max_element(profile.begin(), profile.end());
550✔
78
        if (max_it == profile.end()) {
550✔
UNCOV
79
            continue;
×
80
        }
81

82
        T max_intensity = *max_it;
550✔
83
        if (max_intensity == 0) {
550✔
84
            continue;// Skip if no signal
47✔
85
        }
86

87
        // Find all positions with the maximum intensity
88
        std::vector<int> max_indices;
503✔
89
        for (size_t i = 0; i < profile.size(); ++i) {
25,738✔
90
            if (profile[i] == max_intensity) {
25,235✔
91
                max_indices.push_back(static_cast<int>(i));
767✔
92
            }
93
        }
94

95
        // Calculate the average position of all maximum points
96
        float avg_max_index = 0.0f;
503✔
97
        for (int index: max_indices) {
1,270✔
98
            avg_max_index += static_cast<float>(index);
767✔
99
        }
100
        avg_max_index /= static_cast<float>(max_indices.size());
503✔
101

102
        int max_offset = static_cast<int>(std::round(avg_max_index)) - perpendicular_range / 2;
503✔
103

104
        // Find minimum in profile
105
        auto min_it = std::min_element(profile.begin(), profile.end());
503✔
106
        if (min_it == profile.end()) {
503✔
UNCOV
107
            continue;
×
108
        }
109

110
        T min_intensity = *min_it;
503✔
111

112
        // Find the half-maximum threshold
113
        float half_max = (static_cast<float>(max_intensity) + static_cast<float>(min_intensity)) / 2.0f;
503✔
114

115
        // Find the left and right boundaries at half maximum, starting from the average max position
116
        int left_bound = max_offset;
503✔
117
        int right_bound = max_offset;
503✔
118

119
        // Work leftward from the average max position to find the first half-maximum
120
        int avg_max_index_int = static_cast<int>(std::round(avg_max_index));
503✔
121
        for (int i = avg_max_index_int; i >= 0; --i) {
1,138✔
122
            if (profile[static_cast<size_t>(i)] < half_max) {
1,138✔
123
                left_bound = i + 1 - perpendicular_range / 2;// +1 to include the last point >= half_max
503✔
124
                break;
503✔
125
            }
126
        }
127

128
        // Work rightward from the average max position to find the first half-maximum
129
        for (size_t i = static_cast<size_t>(avg_max_index_int); i < profile.size(); ++i) {
1,138✔
130
            if (profile[i] < half_max) {
1,127✔
131
                right_bound = static_cast<int>(i) - 1 - perpendicular_range / 2;// -1 to include the last point >= half_max
492✔
132
                break;
492✔
133
            }
134
        }
135

136
        // Calculate the center point of the FWHM region
137
        float center_offset = (static_cast<float>(left_bound) + static_cast<float>(right_bound)) / 2.0f;
503✔
138
        Point2D<float> center_point = {
503✔
139
                sample_start.x + perpendicular_dir.x * center_offset,
503✔
140
                sample_start.y + perpendicular_dir.y * center_offset};
503✔
141
        center_points.push_back(center_point);
503✔
142
        intensities.push_back(static_cast<float>(max_intensity));
503✔
143
    }
144

145
    // Calculate weighted average center point based on intensity
146
    if (center_points.empty()) {
36✔
147
        return vertex;// Return original vertex if no center points found
4✔
148
    }
149

150
    float total_weight = 0.0f;
32✔
151
    Point2D<float> weighted_sum{0.0f, 0.0f};
32✔
152

153
    for (size_t i = 0; i < center_points.size(); ++i) {
535✔
154
        float weight = intensities[i];
503✔
155
        weighted_sum.x += center_points[i].x * weight;
503✔
156
        weighted_sum.y += center_points[i].y * weight;
503✔
157
        total_weight += weight;
503✔
158
    }
159

160
    if (total_weight > 0) {
32✔
161
        return Point2D<float>{weighted_sum.x / total_weight, weighted_sum.y / total_weight};
32✔
162
    }
163

UNCOV
164
    return vertex;// Return original vertex if no valid weights
×
165
}
36✔
166

167
// Calculate FWHM profile extents for a single vertex
168
template<typename T>
169
Line2D calculate_fwhm_profile_extents(Point2D<float> const & vertex,
3✔
170
                                      Point2D<float> const & perpendicular_dir,
171
                                      int width,
172
                                      int perpendicular_range,
173
                                      std::vector<T> const & image_data,
174
                                      ImageSize const & image_size,
175
                                      FWHMApproach /*approach*/) {
176
    if (width <= 0) {
3✔
177
        // Return a line with just the original vertex repeated
UNCOV
178
        Line2D debug_line;
×
179
        debug_line.push_back(vertex);
×
180
        debug_line.push_back(vertex);
×
181
        debug_line.push_back(vertex);
×
182
        return debug_line;
×
183
    }
×
184

185
    std::vector<Point2D<float>> left_extents;
3✔
186
    std::vector<Point2D<float>> right_extents;
3✔
187
    std::vector<Point2D<float>> max_points;
3✔
188
    std::vector<float> intensities;
3✔
189

190
    // Sample multiple points along the width of the analysis strip
191
    for (int w = -width / 2; w <= width / 2; ++w) {
130✔
192
        // Calculate the sampling point along the width direction
193
        Point2D<float> width_dir{-perpendicular_dir.y, perpendicular_dir.x};// Perpendicular to perp dir
63✔
194

195
        Point2D<float> sample_start = {
63✔
196
                vertex.x + width_dir.x * static_cast<float>(w),
63✔
197
                vertex.y + width_dir.y * static_cast<float>(w)};
63✔
198

199
        // Sample intensity profile along the perpendicular direction
200
        std::vector<T> profile;
63✔
201
        std::vector<float> distances;
63✔
202

203
        // Sample up to perpendicular_range pixels in each direction along the perpendicular
204
        for (int d = -perpendicular_range / 2; d <= perpendicular_range / 2; ++d) {
3,276✔
205
            Point2D<float> sample_point = {
3,213✔
206
                    sample_start.x + perpendicular_dir.x * static_cast<float>(d),
3,213✔
207
                    sample_start.y + perpendicular_dir.y * static_cast<float>(d)};
3,213✔
208

209
            T intensity = get_pixel_value(sample_point, image_data, image_size);
3,213✔
210
            profile.push_back(intensity);
3,213✔
211
            distances.push_back(static_cast<float>(d));
3,213✔
212
        }
213

214
        // Find the maximum intensity in the profile
215
        auto max_it = std::max_element(profile.begin(), profile.end());
63✔
216
        if (max_it == profile.end()) {
63✔
UNCOV
217
            continue;
×
218
        }
219

220
        T max_intensity = *max_it;
63✔
221
        if (max_intensity == 0) {
63✔
222
            continue;// Skip if no signal
1✔
223
        }
224

225
        // Find all positions with the maximum intensity
226
        std::vector<int> max_indices;
62✔
227
        for (size_t i = 0; i < profile.size(); ++i) {
3,224✔
228
            if (profile[i] == max_intensity) {
3,162✔
229
                max_indices.push_back(static_cast<int>(i));
62✔
230
            }
231
        }
232

233
        // Calculate the average position of all maximum points
234
        float avg_max_index = 0.0f;
62✔
235
        for (int index: max_indices) {
124✔
236
            avg_max_index += static_cast<float>(index);
62✔
237
        }
238
        avg_max_index /= static_cast<float>(max_indices.size());
62✔
239

240
        int max_offset = static_cast<int>(std::round(avg_max_index)) - perpendicular_range / 2;
62✔
241

242
        // Calculate the maximum point location
243
        Point2D<float> max_point = {
62✔
244
                sample_start.x + perpendicular_dir.x * static_cast<float>(max_offset),
62✔
245
                sample_start.y + perpendicular_dir.y * static_cast<float>(max_offset)};
62✔
246

247
        // Find minimum in profile
248
        auto min_it = std::min_element(profile.begin(), profile.end());
62✔
249
        if (min_it == profile.end()) {
62✔
UNCOV
250
            continue;
×
251
        }
252

253
        T min_intensity = *min_it;
62✔
254

255
        // Find the half-maximum threshold
256
        float half_max = (static_cast<float>(max_intensity) + static_cast<float>(min_intensity)) / 2.0f;
62✔
257

258
        // Find the left and right boundaries at half maximum, starting from the average max position
259
        int left_bound = max_offset;
62✔
260
        int right_bound = max_offset;
62✔
261

262
        // Work leftward from the average max position to find the first half-maximum
263
        int avg_max_index_int = static_cast<int>(std::round(avg_max_index));
62✔
264
        for (int i = avg_max_index_int; i >= 0; --i) {
124✔
265
            if (profile[static_cast<size_t>(i)] < half_max) {
124✔
266
                left_bound = i + 1 - perpendicular_range / 2;// +1 to include the last point >= half_max
62✔
267
                break;
62✔
268
            }
269
        }
270

271
        // Work rightward from the average max position to find the first half-maximum
272
        for (size_t i = static_cast<size_t>(avg_max_index_int); i < profile.size(); ++i) {
124✔
273
            if (profile[i] < half_max) {
124✔
274
                right_bound = static_cast<int>(i) - 1 - perpendicular_range / 2;// -1 to include the last point >= half_max
62✔
275
                break;
62✔
276
            }
277
        }
278

279
        // Calculate the left and right extent points
280
        Point2D<float> left_extent = {
62✔
281
                sample_start.x + perpendicular_dir.x * static_cast<float>(left_bound),
62✔
282
                sample_start.y + perpendicular_dir.y * static_cast<float>(left_bound)};
62✔
283

284
        Point2D<float> right_extent = {
62✔
285
                sample_start.x + perpendicular_dir.x * static_cast<float>(right_bound),
62✔
286
                sample_start.y + perpendicular_dir.y * static_cast<float>(right_bound)};
62✔
287

288
        left_extents.push_back(left_extent);
62✔
289
        right_extents.push_back(right_extent);
62✔
290
        max_points.push_back(max_point);
62✔
291
        intensities.push_back(static_cast<float>(max_intensity));
62✔
292
    }
293

294
    // Calculate weighted average extents based on intensity
295
    if (left_extents.empty()) {
3✔
296
        // Return a line with just the original vertex repeated
UNCOV
297
        Line2D debug_line;
×
UNCOV
298
        debug_line.push_back(vertex);
×
UNCOV
299
        debug_line.push_back(vertex);
×
UNCOV
300
        debug_line.push_back(vertex);
×
UNCOV
301
        return debug_line;
×
UNCOV
302
    }
×
303

304
    float total_weight = 0.0f;
3✔
305
    Point2D<float> weighted_left_sum{0.0f, 0.0f};
3✔
306
    Point2D<float> weighted_right_sum{0.0f, 0.0f};
3✔
307
    Point2D<float> weighted_max_sum{0.0f, 0.0f};
3✔
308

309
    for (size_t i = 0; i < left_extents.size(); ++i) {
65✔
310
        float weight = intensities[i];
62✔
311
        weighted_left_sum.x += left_extents[i].x * weight;
62✔
312
        weighted_left_sum.y += left_extents[i].y * weight;
62✔
313
        weighted_right_sum.x += right_extents[i].x * weight;
62✔
314
        weighted_right_sum.y += right_extents[i].y * weight;
62✔
315
        weighted_max_sum.x += max_points[i].x * weight;
62✔
316
        weighted_max_sum.y += max_points[i].y * weight;
62✔
317
        total_weight += weight;
62✔
318
    }
319

320
    if (total_weight > 0) {
3✔
321
        Point2D<float> avg_left = {weighted_left_sum.x / total_weight, weighted_left_sum.y / total_weight};
3✔
322
        Point2D<float> avg_right = {weighted_right_sum.x / total_weight, weighted_right_sum.y / total_weight};
3✔
323
        Point2D<float> avg_max = {weighted_max_sum.x / total_weight, weighted_max_sum.y / total_weight};
3✔
324

325
        Line2D debug_line;
3✔
326
        debug_line.push_back(avg_left);
3✔
327
        debug_line.push_back(avg_max);
3✔
328
        debug_line.push_back(avg_right);
3✔
329
        return debug_line;
3✔
330
    }
3✔
331

332
    // Return a line with just the original vertex repeated
UNCOV
333
    Line2D debug_line;
×
UNCOV
334
    debug_line.push_back(vertex);
×
UNCOV
335
    debug_line.push_back(vertex);
×
UNCOV
336
    debug_line.push_back(vertex);
×
UNCOV
337
    return debug_line;
×
338
}
3✔
339

340
// Helper function to process lines with image data of any type
341
template<typename T>
342
std::vector<Line2D> processLinesWithImageData(std::vector<Line2D> const & lines,
7✔
343
                                               std::vector<T> const & image_data,
344
                                               ImageSize const & image_size,
345
                                               int width,
346
                                               int perpendicular_range,
347
                                               FWHMApproach approach,
348
                                               LineAlignmentOutputMode output_mode) {
349
    std::vector<Line2D> aligned_lines;
7✔
350
    
351
    for (auto const & line : lines) {
14✔
352
        if (line.size() < 3) {
7✔
353
            // Skip lines with fewer than 3 vertices
UNCOV
354
            aligned_lines.push_back(line);
×
UNCOV
355
            continue;
×
356
        }
357

358
        if (output_mode == LineAlignmentOutputMode::FWHM_PROFILE_EXTENTS) {
7✔
359
            // Debug mode: create a debug line for each vertex
360
            for (size_t i = 0; i < line.size(); ++i) {
7✔
361
                Point2D<float> vertex = line[i];
3✔
362

363
                // Calculate perpendicular direction
364
                Point2D<float> perp_dir = calculate_perpendicular_direction(line, i);
3✔
365

366
                if (perp_dir.x == 0.0f && perp_dir.y == 0.0f) {
3✔
367
                    // If we can't calculate a perpendicular direction, create a debug line with just the vertex repeated
UNCOV
368
                    Line2D debug_line;
×
UNCOV
369
                    debug_line.push_back(vertex);
×
370
                    debug_line.push_back(vertex);
×
UNCOV
371
                    debug_line.push_back(vertex);
×
372
                    aligned_lines.push_back(debug_line);
×
UNCOV
373
                    continue;
×
UNCOV
374
                }
×
375

376
                // Debug mode: calculate FWHM profile extents
377
                Line2D debug_line = calculate_fwhm_profile_extents(vertex,
3✔
378
                                                                   perp_dir,
379
                                                                   width,
380
                                                                   perpendicular_range,
381
                                                                   image_data,
382
                                                                   image_size,
383
                                                                   approach);
384
                aligned_lines.push_back(debug_line);
3✔
385
            }
386
        } else {
387
            // Normal mode: create a single aligned line
388
            Line2D aligned_line;
6✔
389

390
            // Process each vertex in the line
391
            for (size_t i = 0; i < line.size(); ++i) {
24✔
392
                Point2D<float> vertex = line[i];
18✔
393

394
                // Calculate perpendicular direction
395
                Point2D<float> perp_dir = calculate_perpendicular_direction(line, i);
18✔
396

397
                if (perp_dir.x == 0.0f && perp_dir.y == 0.0f) {
18✔
398
                    // If we can't calculate a perpendicular direction, keep the original vertex
UNCOV
399
                    aligned_line.push_back(vertex);
×
UNCOV
400
                    continue;
×
401
                }
402

403
                // Normal mode: calculate FWHM center point
404
                Point2D<float> aligned_vertex = calculate_fwhm_center(vertex,
18✔
405
                                                                      perp_dir,
406
                                                                      width,
407
                                                                      perpendicular_range,
408
                                                                      image_data,
409
                                                                      image_size,
410
                                                                      approach);
411

412
                // Ensure the aligned vertex stays within image bounds
413
                aligned_vertex.x = std::max(0.0f, std::min(static_cast<float>(image_size.width - 1), aligned_vertex.x));
18✔
414
                aligned_vertex.y = std::max(0.0f, std::min(static_cast<float>(image_size.height - 1), aligned_vertex.y));
18✔
415

416
                aligned_line.push_back(aligned_vertex);
18✔
417
            }
418

419
            aligned_lines.push_back(aligned_line);
6✔
420
        }
6✔
421
    }
422
    
423
    return aligned_lines;
7✔
UNCOV
424
}
×
425

426
///////////////////////////////////////////////////////////////////////////////
427

428
std::string LineAlignmentOperation::getName() const {
148✔
429
    return "Line Alignment to Bright Features";
444✔
430
}
431

432
std::type_index LineAlignmentOperation::getTargetInputTypeIndex() const {
148✔
433
    return typeid(std::shared_ptr<LineData>);
148✔
434
}
435

436
bool LineAlignmentOperation::canApply(DataTypeVariant const & dataVariant) const {
3✔
437
    if (!std::holds_alternative<std::shared_ptr<LineData>>(dataVariant)) {
3✔
UNCOV
438
        return false;
×
439
    }
440

441
    auto const * ptr_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
3✔
442

443
    return ptr_ptr && *ptr_ptr;
3✔
444
}
445

446
std::unique_ptr<TransformParametersBase> LineAlignmentOperation::getDefaultParameters() const {
3✔
447
    return std::make_unique<LineAlignmentParameters>();
3✔
448
}
449

450
DataTypeVariant LineAlignmentOperation::execute(DataTypeVariant const & dataVariant,
×
451
                                                TransformParametersBase const * transformParameters) {
UNCOV
452
    return execute(dataVariant, transformParameters, [](int) {});
×
453
}
454

455
DataTypeVariant LineAlignmentOperation::execute(DataTypeVariant const & dataVariant,
3✔
456
                                                TransformParametersBase const * transformParameters,
457
                                                ProgressCallback progressCallback) {
458
    auto const * ptr_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
3✔
459
    if (!ptr_ptr || !(*ptr_ptr)) {
3✔
UNCOV
460
        std::cerr << "LineAlignmentOperation::execute: Incompatible variant type or null data." << std::endl;
×
UNCOV
461
        if (progressCallback) progressCallback(100);
×
UNCOV
462
        return {};
×
463
    }
464

465
    auto line_data = *ptr_ptr;
3✔
466

467
    auto const * typed_params =
468
            transformParameters ? dynamic_cast<LineAlignmentParameters const *>(transformParameters) : nullptr;
3✔
469

470
    // Auto-find media data if not provided in parameters
471
    std::shared_ptr<MediaData> media_data;
3✔
472
    if (typed_params && typed_params->media_data) {
3✔
473
        media_data = typed_params->media_data;
3✔
474
    } else {
UNCOV
475
        std::cerr << "LineAlignmentOperation::execute: No media data provided. Operation requires media data to align lines to bright features." << std::endl;
×
UNCOV
476
        if (progressCallback) progressCallback(100);
×
UNCOV
477
        return {};
×
478
    }
479

480
    if (progressCallback) progressCallback(0);
3✔
481

482
    // Use default parameters if none provided
483
    int width = typed_params ? typed_params->width : 20;
3✔
484
    int perpendicular_range = typed_params ? typed_params->perpendicular_range : 50;
3✔
485
    bool use_processed_data = typed_params ? typed_params->use_processed_data : true;
3✔
486
    FWHMApproach approach = typed_params ? typed_params->approach : FWHMApproach::PEAK_WIDTH_HALF_MAX;
3✔
487
    LineAlignmentOutputMode output_mode = typed_params ? typed_params->output_mode : LineAlignmentOutputMode::ALIGNED_VERTICES;
3✔
488

489
    std::shared_ptr<LineData> result = line_alignment(
3✔
490
            line_data.get(),
3✔
491
            media_data.get(),
492
            width,
493
            perpendicular_range,
494
            use_processed_data,
495
            approach,
496
            output_mode,
497
            progressCallback);
6✔
498

499
    if (!result) {
3✔
UNCOV
500
        std::cerr << "LineAlignmentOperation::execute: Alignment failed." << std::endl;
×
UNCOV
501
        return {};
×
502
    }
503

504
    std::cout << "LineAlignmentOperation executed successfully." << std::endl;
3✔
505
    return result;
3✔
506
}
3✔
507

508
std::shared_ptr<LineData> line_alignment(LineData const * line_data,
4✔
509
                                         MediaData * media_data,
510
                                         int width,
511
                                         int perpendicular_range,
512
                                         bool use_processed_data,
513
                                         FWHMApproach approach,
514
                                         LineAlignmentOutputMode output_mode) {
515
    return line_alignment(line_data, media_data, width, perpendicular_range, use_processed_data, approach, output_mode, [](int) {});
4✔
516
}
517

518
std::shared_ptr<LineData> line_alignment(LineData const * line_data,
7✔
519
                                         MediaData * media_data,
520
                                         int width,
521
                                         int perpendicular_range,
522
                                         bool use_processed_data,
523
                                         FWHMApproach approach,
524
                                         LineAlignmentOutputMode output_mode,
525
                                         ProgressCallback progressCallback) {
526
    if (!line_data || !media_data) {
7✔
UNCOV
527
        std::cerr << "LineAlignment: Null LineData or MediaData provided." << std::endl;
×
UNCOV
528
        if (progressCallback) progressCallback(100);
×
UNCOV
529
        return std::make_shared<LineData>();
×
530
    }
531

532
    // Check if line data has at least 3 vertices
533
    auto line_times = line_data->getTimesWithData();
7✔
534
    if (line_times.empty()) {
7✔
535
        if (progressCallback) progressCallback(100);
×
536
        return std::make_shared<LineData>();
×
537
    }
538

539
    // Create new LineData for the aligned lines
540
    auto aligned_line_data = std::make_shared<LineData>();
7✔
541
    aligned_line_data->setImageSize(line_data->getImageSize());
7✔
542

543
    size_t total_time_points = line_times.size();
7✔
544
    size_t processed_time_points = 0;
7✔
545
    if (progressCallback) progressCallback(0);
7✔
546

547
    // Process each time that has line data
548
    for (auto time: line_times) {
14✔
549
        // Get lines at this time
550
        auto const & lines = line_data->getAtTime(time);
7✔
551
        if (lines.empty()) {
7✔
UNCOV
552
            continue;
×
553
        }
554

555
        // Get image size
556
        ImageSize image_size = media_data->getImageSize();
7✔
557

558
        // Process each line based on the media data type
559
        std::vector<Line2D> aligned_lines;
7✔
560
        
561
        // Check if media data is 8-bit or 32-bit and process accordingly
562
        if (media_data->is8Bit()) {
7✔
563
            // Get 8-bit image data
564
            std::vector<uint8_t> image_data;
5✔
565
            if (use_processed_data) {
5✔
566
                image_data = media_data->getProcessedData8(static_cast<int>(time.getValue()));
3✔
567
            } else {
568
                image_data = media_data->getRawData8(static_cast<int>(time.getValue()));
2✔
569
            }
570

571
            if (image_data.empty()) {
5✔
UNCOV
572
                continue;
×
573
            }
574

575
            // Process lines with 8-bit data
576
            aligned_lines = processLinesWithImageData(lines, image_data, image_size, 
10✔
577
                                                    width, perpendicular_range, approach, output_mode);
5✔
578
        } else {
5✔
579
            // Get 32-bit image data
580
            std::vector<float> image_data;
2✔
581
            if (use_processed_data) {
2✔
UNCOV
582
                image_data = media_data->getProcessedData32(static_cast<int>(time.getValue()));
×
583
            } else {
584
                image_data = media_data->getRawData32(static_cast<int>(time.getValue()));
2✔
585
            }
586

587
            if (image_data.empty()) {
2✔
UNCOV
588
                continue;
×
589
            }
590

591
            // Process lines with 32-bit data
592
            aligned_lines = processLinesWithImageData(lines, image_data, image_size, 
4✔
593
                                                    width, perpendicular_range, approach, output_mode);
2✔
594
        }
2✔
595

596
        // Add the aligned lines to the new LineData
597
        for (auto const & aligned_line: aligned_lines) {
16✔
598
            aligned_line_data->addAtTime(time, aligned_line, false);
9✔
599
        }
600

601
        processed_time_points++;
7✔
602
        if (progressCallback) {
7✔
603
            int current_progress = static_cast<int>(std::round(static_cast<double>(processed_time_points) / static_cast<double>(total_time_points) * 100.0));
7✔
604
            progressCallback(current_progress);
7✔
605
        }
606
    }
7✔
607

608
    if (progressCallback) progressCallback(100);
7✔
609
    return aligned_line_data;
7✔
610
}
7✔
611

612
template uint8_t get_pixel_value<uint8_t>(Point2D<float> const & point,
613
                                          std::vector<uint8_t> const & image_data,
614
                                          ImageSize const & image_size);
615

616
template float get_pixel_value<float>(Point2D<float> const & point,
617
                                      std::vector<float> const & image_data,
618
                                      ImageSize const & image_size);
619

620

621
template Point2D<float> calculate_fwhm_center<uint8_t>(Point2D<float> const & vertex,
622
                                                       Point2D<float> const & perpendicular_dir,
623
                                                       int width,
624
                                                       int perpendicular_range,
625
                                                       std::vector<uint8_t> const & image_data,
626
                                                       ImageSize const & image_size,
627
                                                       FWHMApproach approach = FWHMApproach::PEAK_WIDTH_HALF_MAX);
628
template Point2D<float> calculate_fwhm_center<float>(Point2D<float> const & vertex,
629
                                                     Point2D<float> const & perpendicular_dir,
630
                                                     int width,
631
                                                     int perpendicular_range,
632
                                                     std::vector<float> const & image_data,
633
                                                     ImageSize const & image_size,
634
                                                     FWHMApproach approach = FWHMApproach::PEAK_WIDTH_HALF_MAX);
635

636

637
template Line2D calculate_fwhm_profile_extents<uint8_t>(Point2D<float> const & vertex,
638
                                                        Point2D<float> const & perpendicular_dir,
639
                                                        int width,
640
                                                        int perpendicular_range,
641
                                                        std::vector<uint8_t> const & image_data,
642
                                                        ImageSize const & image_size,
643
                                                        FWHMApproach approach = FWHMApproach::PEAK_WIDTH_HALF_MAX);
644
template Line2D calculate_fwhm_profile_extents<float>(Point2D<float> const & vertex,
645
                                                      Point2D<float> const & perpendicular_dir,
646
                                                      int width,
647
                                                      int perpendicular_range,
648
                                                      std::vector<float> const & image_data,
649
                                                      ImageSize const & image_size,
650
                                                      FWHMApproach approach = FWHMApproach::PEAK_WIDTH_HALF_MAX);
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