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

paulmthompson / WhiskerToolbox / 18685379784

21 Oct 2025 01:25PM UTC coverage: 72.522% (+0.1%) from 72.391%
18685379784

push

github

paulmthompson
fix failing tests

18 of 40 new or added lines in 1 file covered. (45.0%)

1765 existing lines in 32 files now uncovered.

53998 of 74457 relevant lines covered (72.52%)

46177.73 hits per line

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

82.41
/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
    std::vector<T> profile = std::vector<T>(perpendicular_range+1, 0);
108✔
52
    std::vector<float> distances = std::vector<float>(perpendicular_range+1, 0);
108✔
53

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

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

63
        // Sample intensity profile along the perpendicular direction
64
        profile.assign(perpendicular_range+1, 0);
550✔
65
        distances.assign(perpendicular_range+1, 0);
550✔
66

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

74
            T intensity = get_pixel_value(sample_point, image_data, image_size);
27,632✔
75
            profile[index] = intensity;
27,632✔
76
            distances[index] = static_cast<float>(d);
27,632✔
77
            index++;
27,632✔
78
        }
79

80
        // Find the maximum intensity in the profile
81
        auto max_it = std::max_element(profile.begin(), profile.end());
550✔
82
        if (max_it == profile.end()) {
550✔
UNCOV
83
            continue;
×
84
        }
85

86
        T max_intensity = *max_it;
550✔
87
        if (max_intensity == 0) {
550✔
88
            continue;// Skip if no signal
47✔
89
        }
90

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

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

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

108
        // Find minimum in profile
109
        auto min_it = std::min_element(profile.begin(), profile.end());
503✔
110
        if (min_it == profile.end()) {
503✔
UNCOV
111
            continue;
×
112
        }
113

114
        T min_intensity = *min_it;
503✔
115

116
        // Find the half-maximum threshold
117
        float half_max = (static_cast<float>(max_intensity) + static_cast<float>(min_intensity)) / 2.0f;
503✔
118

119
        // Find the left and right boundaries at half maximum, starting from the average max position
120
        int left_bound = max_offset;
503✔
121
        int right_bound = max_offset;
503✔
122

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

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

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

149
    // Calculate weighted average center point based on intensity
150
    if (center_points.empty()) {
36✔
151
        return vertex;// Return original vertex if no center points found
4✔
152
    }
153

154
    float total_weight = 0.0f;
32✔
155
    Point2D<float> weighted_sum{0.0f, 0.0f};
32✔
156

157
    for (size_t i = 0; i < center_points.size(); ++i) {
535✔
158
        float weight = intensities[i];
503✔
159
        weighted_sum.x += center_points[i].x * weight;
503✔
160
        weighted_sum.y += center_points[i].y * weight;
503✔
161
        total_weight += weight;
503✔
162
    }
163

164
    if (total_weight > 0) {
32✔
165
        return Point2D<float>{weighted_sum.x / total_weight, weighted_sum.y / total_weight};
32✔
166
    }
167

UNCOV
168
    return vertex;// Return original vertex if no valid weights
×
169
}
36✔
170

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

189
    std::vector<Point2D<float>> left_extents;
3✔
190
    std::vector<Point2D<float>> right_extents;
3✔
191
    std::vector<Point2D<float>> max_points;
3✔
192
    std::vector<float> intensities;
3✔
193

194
    std::vector<T> profile = std::vector<T>(perpendicular_range+1, 0);
9✔
195
    std::vector<float> distances = std::vector<float>(perpendicular_range+1, 0);
9✔
196

197
    // Sample multiple points along the width of the analysis strip
198
    for (int w = -width / 2; w <= width / 2; ++w) {
128✔
199
        // Calculate the sampling point along the width direction
200
        Point2D<float> width_dir{-perpendicular_dir.y, perpendicular_dir.x};// Perpendicular to perp dir
63✔
201

202
        Point2D<float> sample_start = {
63✔
203
                vertex.x + width_dir.x * static_cast<float>(w),
63✔
204
                vertex.y + width_dir.y * static_cast<float>(w)};
63✔
205

206
        // Sample intensity profile along the perpendicular direction
207
        profile.assign(perpendicular_range+1, 0);
63✔
208
        distances.assign(perpendicular_range+1, 0);
63✔
209

210
        // Sample up to perpendicular_range pixels in each direction along the perpendicular
211
        int index = 0;
63✔
212
        for (int d = -perpendicular_range / 2; d <= perpendicular_range / 2; ++d) {
3,276✔
213
            Point2D<float> sample_point = {
3,213✔
214
                    sample_start.x + perpendicular_dir.x * static_cast<float>(d),
3,213✔
215
                    sample_start.y + perpendicular_dir.y * static_cast<float>(d)};
3,213✔
216

217
            T intensity = get_pixel_value(sample_point, image_data, image_size);
3,213✔
218
            profile[index] = intensity;
3,213✔
219
            distances[index] = static_cast<float>(d);
3,213✔
220
            index++;
3,213✔
221
        }
222

223
        // Find the maximum intensity in the profile
224
        auto max_it = std::max_element(profile.begin(), profile.end());
63✔
225
        if (max_it == profile.end()) {
63✔
UNCOV
226
            continue;
×
227
        }
228

229
        T max_intensity = *max_it;
63✔
230
        if (max_intensity == 0) {
63✔
231
            continue;// Skip if no signal
1✔
232
        }
233

234
        // Find all positions with the maximum intensity
235
        std::vector<int> max_indices;
62✔
236
        for (size_t i = 0; i < profile.size(); ++i) {
3,224✔
237
            if (profile[i] == max_intensity) {
3,162✔
238
                max_indices.push_back(static_cast<int>(i));
62✔
239
            }
240
        }
241

242
        // Calculate the average position of all maximum points
243
        float avg_max_index = 0.0f;
62✔
244
        for (int index: max_indices) {
124✔
245
            avg_max_index += static_cast<float>(index);
62✔
246
        }
247
        avg_max_index /= static_cast<float>(max_indices.size());
62✔
248

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

251
        // Calculate the maximum point location
252
        Point2D<float> max_point = {
62✔
253
                sample_start.x + perpendicular_dir.x * static_cast<float>(max_offset),
62✔
254
                sample_start.y + perpendicular_dir.y * static_cast<float>(max_offset)};
62✔
255

256
        // Find minimum in profile
257
        auto min_it = std::min_element(profile.begin(), profile.end());
62✔
258
        if (min_it == profile.end()) {
62✔
UNCOV
259
            continue;
×
260
        }
261

262
        T min_intensity = *min_it;
62✔
263

264
        // Find the half-maximum threshold
265
        float half_max = (static_cast<float>(max_intensity) + static_cast<float>(min_intensity)) / 2.0f;
62✔
266

267
        // Find the left and right boundaries at half maximum, starting from the average max position
268
        int left_bound = max_offset;
62✔
269
        int right_bound = max_offset;
62✔
270

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

280
        // Work rightward from the average max position to find the first half-maximum
281
        for (size_t i = static_cast<size_t>(avg_max_index_int); i < profile.size(); ++i) {
124✔
282
            if (profile[i] < half_max) {
124✔
283
                right_bound = static_cast<int>(i) - 1 - perpendicular_range / 2;// -1 to include the last point >= half_max
62✔
284
                break;
62✔
285
            }
286
        }
287

288
        // Calculate the left and right extent points
289
        Point2D<float> left_extent = {
62✔
290
                sample_start.x + perpendicular_dir.x * static_cast<float>(left_bound),
62✔
291
                sample_start.y + perpendicular_dir.y * static_cast<float>(left_bound)};
62✔
292

293
        Point2D<float> right_extent = {
62✔
294
                sample_start.x + perpendicular_dir.x * static_cast<float>(right_bound),
62✔
295
                sample_start.y + perpendicular_dir.y * static_cast<float>(right_bound)};
62✔
296

297
        left_extents.push_back(left_extent);
62✔
298
        right_extents.push_back(right_extent);
62✔
299
        max_points.push_back(max_point);
62✔
300
        intensities.push_back(static_cast<float>(max_intensity));
62✔
301
    }
302

303
    // Calculate weighted average extents based on intensity
304
    if (left_extents.empty()) {
3✔
305
        // Return a line with just the original vertex repeated
UNCOV
306
        Line2D debug_line;
×
UNCOV
307
        debug_line.push_back(vertex);
×
308
        debug_line.push_back(vertex);
×
309
        debug_line.push_back(vertex);
×
310
        return debug_line;
×
311
    }
×
312

313
    float total_weight = 0.0f;
3✔
314
    Point2D<float> weighted_left_sum{0.0f, 0.0f};
3✔
315
    Point2D<float> weighted_right_sum{0.0f, 0.0f};
3✔
316
    Point2D<float> weighted_max_sum{0.0f, 0.0f};
3✔
317

318
    for (size_t i = 0; i < left_extents.size(); ++i) {
65✔
319
        float weight = intensities[i];
62✔
320
        weighted_left_sum.x += left_extents[i].x * weight;
62✔
321
        weighted_left_sum.y += left_extents[i].y * weight;
62✔
322
        weighted_right_sum.x += right_extents[i].x * weight;
62✔
323
        weighted_right_sum.y += right_extents[i].y * weight;
62✔
324
        weighted_max_sum.x += max_points[i].x * weight;
62✔
325
        weighted_max_sum.y += max_points[i].y * weight;
62✔
326
        total_weight += weight;
62✔
327
    }
328

329
    if (total_weight > 0) {
3✔
330
        Point2D<float> avg_left = {weighted_left_sum.x / total_weight, weighted_left_sum.y / total_weight};
3✔
331
        Point2D<float> avg_right = {weighted_right_sum.x / total_weight, weighted_right_sum.y / total_weight};
3✔
332
        Point2D<float> avg_max = {weighted_max_sum.x / total_weight, weighted_max_sum.y / total_weight};
3✔
333

334
        Line2D debug_line;
3✔
335
        debug_line.push_back(avg_left);
3✔
336
        debug_line.push_back(avg_max);
3✔
337
        debug_line.push_back(avg_right);
3✔
338
        return debug_line;
3✔
339
    }
3✔
340

341
    // Return a line with just the original vertex repeated
UNCOV
342
    Line2D debug_line;
×
UNCOV
343
    debug_line.push_back(vertex);
×
344
    debug_line.push_back(vertex);
×
345
    debug_line.push_back(vertex);
×
346
    return debug_line;
×
347
}
3✔
348

349
// Helper function to process lines with image data of any type
350
template<typename T>
351
std::vector<Line2D> processLinesWithImageData(std::vector<Line2D> const & lines,
7✔
352
                                               std::vector<T> const & image_data,
353
                                               ImageSize const & image_size,
354
                                               int width,
355
                                               int perpendicular_range,
356
                                               FWHMApproach approach,
357
                                               LineAlignmentOutputMode output_mode) {
358
    std::vector<Line2D> aligned_lines;
7✔
359
    
360
    for (auto const & line : lines) {
14✔
361
        if (line.size() < 3) {
7✔
362
            // Skip lines with fewer than 3 vertices
UNCOV
363
            aligned_lines.push_back(line);
×
UNCOV
364
            continue;
×
365
        }
366

367
        if (output_mode == LineAlignmentOutputMode::FWHM_PROFILE_EXTENTS) {
7✔
368
            // Debug mode: create a debug line for each vertex
369
            for (size_t i = 0; i < line.size(); ++i) {
7✔
370
                Point2D<float> vertex = line[i];
3✔
371

372
                // Calculate perpendicular direction
373
                Point2D<float> perp_dir = calculate_perpendicular_direction(line, i);
3✔
374

375
                if (perp_dir.x == 0.0f && perp_dir.y == 0.0f) {
3✔
376
                    // If we can't calculate a perpendicular direction, create a debug line with just the vertex repeated
UNCOV
377
                    Line2D debug_line;
×
UNCOV
378
                    debug_line.push_back(vertex);
×
379
                    debug_line.push_back(vertex);
×
380
                    debug_line.push_back(vertex);
×
381
                    aligned_lines.push_back(debug_line);
×
382
                    continue;
×
383
                }
×
384

385
                // Debug mode: calculate FWHM profile extents
386
                Line2D debug_line = calculate_fwhm_profile_extents(vertex,
3✔
387
                                                                   perp_dir,
388
                                                                   width,
389
                                                                   perpendicular_range,
390
                                                                   image_data,
391
                                                                   image_size,
392
                                                                   approach);
393
                aligned_lines.push_back(debug_line);
3✔
394
            }
395
        } else {
396
            // Normal mode: create a single aligned line
397
            Line2D aligned_line;
6✔
398

399
            // Process each vertex in the line
400
            for (size_t i = 0; i < line.size(); ++i) {
24✔
401
                Point2D<float> vertex = line[i];
18✔
402

403
                // Calculate perpendicular direction
404
                Point2D<float> perp_dir = calculate_perpendicular_direction(line, i);
18✔
405

406
                if (perp_dir.x == 0.0f && perp_dir.y == 0.0f) {
18✔
407
                    // If we can't calculate a perpendicular direction, keep the original vertex
UNCOV
408
                    aligned_line.push_back(vertex);
×
UNCOV
409
                    continue;
×
410
                }
411

412
                // Normal mode: calculate FWHM center point
413
                Point2D<float> aligned_vertex = calculate_fwhm_center(vertex,
18✔
414
                                                                      perp_dir,
415
                                                                      width,
416
                                                                      perpendicular_range,
417
                                                                      image_data,
418
                                                                      image_size,
419
                                                                      approach);
420

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

425
                aligned_line.push_back(aligned_vertex);
18✔
426
            }
427

428
            aligned_lines.push_back(aligned_line);
6✔
429
        }
6✔
430
    }
431
    
432
    return aligned_lines;
7✔
UNCOV
433
}
×
434

435
///////////////////////////////////////////////////////////////////////////////
436

437
std::string LineAlignmentOperation::getName() const {
148✔
438
    return "Line Alignment to Bright Features";
444✔
439
}
440

441
std::type_index LineAlignmentOperation::getTargetInputTypeIndex() const {
148✔
442
    return typeid(std::shared_ptr<LineData>);
148✔
443
}
444

445
bool LineAlignmentOperation::canApply(DataTypeVariant const & dataVariant) const {
3✔
446
    if (!std::holds_alternative<std::shared_ptr<LineData>>(dataVariant)) {
3✔
UNCOV
447
        return false;
×
448
    }
449

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

452
    return ptr_ptr && *ptr_ptr;
3✔
453
}
454

455
std::unique_ptr<TransformParametersBase> LineAlignmentOperation::getDefaultParameters() const {
3✔
456
    return std::make_unique<LineAlignmentParameters>();
3✔
457
}
458

UNCOV
459
DataTypeVariant LineAlignmentOperation::execute(DataTypeVariant const & dataVariant,
×
460
                                                TransformParametersBase const * transformParameters) {
UNCOV
461
    return execute(dataVariant, transformParameters, [](int) {});
×
462
}
463

464
DataTypeVariant LineAlignmentOperation::execute(DataTypeVariant const & dataVariant,
3✔
465
                                                TransformParametersBase const * transformParameters,
466
                                                ProgressCallback progressCallback) {
467
    auto const * ptr_ptr = std::get_if<std::shared_ptr<LineData>>(&dataVariant);
3✔
468
    if (!ptr_ptr || !(*ptr_ptr)) {
3✔
UNCOV
469
        std::cerr << "LineAlignmentOperation::execute: Incompatible variant type or null data." << std::endl;
×
UNCOV
470
        if (progressCallback) progressCallback(100);
×
UNCOV
471
        return {};
×
472
    }
473

474
    auto line_data = *ptr_ptr;
3✔
475

476
    auto const * typed_params =
477
            transformParameters ? dynamic_cast<LineAlignmentParameters const *>(transformParameters) : nullptr;
3✔
478

479
    // Check if we have valid parameters
480
    if (!typed_params) {
3✔
UNCOV
481
        std::cerr << "LineAlignmentOperation::execute: Invalid parameters provided. Operation requires LineAlignmentParameters." << std::endl;
×
UNCOV
482
        if (progressCallback) progressCallback(100);
×
UNCOV
483
        return {};
×
484
    }
485

486
    // Auto-find media data if not provided in parameters
487
    std::shared_ptr<MediaData> media_data;
3✔
488
    if (typed_params->media_data) {
3✔
489
        media_data = typed_params->media_data;
3✔
490
    } else {
UNCOV
491
        std::cerr << "LineAlignmentOperation::execute: No media data provided. Operation requires media data to align lines to bright features." << std::endl;
×
492
        if (progressCallback) progressCallback(100);
×
493
        return {};
×
494
    }
495

496
    if (progressCallback) progressCallback(0);
3✔
497

498
    // Use parameters from the typed_params object
499
    int width = typed_params->width;
3✔
500
    int perpendicular_range = typed_params->perpendicular_range;
3✔
501
    bool use_processed_data = typed_params->use_processed_data;
3✔
502
    FWHMApproach approach = typed_params->approach;
3✔
503
    LineAlignmentOutputMode output_mode = typed_params->output_mode;
3✔
504

505
    std::shared_ptr<LineData> result = line_alignment(
3✔
506
            line_data.get(),
3✔
507
            media_data.get(),
508
            width,
509
            perpendicular_range,
510
            use_processed_data,
511
            approach,
512
            output_mode,
513
            typed_params,
514
            progressCallback);
6✔
515

516
    if (!result) {
3✔
UNCOV
517
        std::cerr << "LineAlignmentOperation::execute: Alignment failed." << std::endl;
×
UNCOV
518
        return {};
×
519
    }
520

521
    std::cout << "LineAlignmentOperation executed successfully." << std::endl;
3✔
522
    return result;
3✔
523
}
3✔
524

525
std::shared_ptr<LineData> line_alignment(LineData const * line_data,
4✔
526
                                         MediaData * media_data,
527
                                         int width,
528
                                         int perpendicular_range,
529
                                         bool use_processed_data,
530
                                         FWHMApproach approach,
531
                                         LineAlignmentOutputMode output_mode,
532
                                         LineAlignmentParameters const * params) {
533
    return line_alignment(line_data, media_data, width, perpendicular_range, use_processed_data, approach, output_mode, params, [](int) {});
4✔
534
}
535

536
std::shared_ptr<LineData> line_alignment(LineData const * line_data,
7✔
537
                                         MediaData * media_data,
538
                                         int width,
539
                                         int perpendicular_range,
540
                                         bool use_processed_data,
541
                                         FWHMApproach approach,
542
                                         LineAlignmentOutputMode output_mode,
543
                                         LineAlignmentParameters const * params,
544
                                         ProgressCallback progressCallback) {
545
    if (!line_data || !media_data) {
7✔
UNCOV
546
        std::cerr << "LineAlignment: Null LineData or MediaData provided." << std::endl;
×
UNCOV
547
        if (progressCallback) progressCallback(100);
×
UNCOV
548
        return std::make_shared<LineData>();
×
549
    }
550

551
    // Check if line data has at least 3 vertices
552
    auto line_times = line_data->getTimesWithData();
7✔
553
    if (line_times.empty()) {
7✔
UNCOV
554
        if (progressCallback) progressCallback(100);
×
UNCOV
555
        return std::make_shared<LineData>();
×
556
    }
557

558
    // Create new LineData for the aligned lines
559
    auto aligned_line_data = std::make_shared<LineData>();
7✔
560
    aligned_line_data->setImageSize(line_data->getImageSize());
7✔
561

562
    size_t total_time_points = line_times.size();
7✔
563
    size_t processed_time_points = 0;
7✔
564
    if (progressCallback) progressCallback(0);
7✔
565

566
    // Process each time that has line data
567
    for (auto time: line_times) {
14✔
568
        // Get lines at this time
569
        auto const & lines = line_data->getAtTime(time);
7✔
570
        if (lines.empty()) {
7✔
UNCOV
571
            continue;
×
572
        }
573

574
        // Get image size
575
        ImageSize image_size = media_data->getImageSize();
7✔
576

577
        // Process each line based on the media data type
578
        std::vector<Line2D> aligned_lines;
7✔
579
        
580
        // Check if media data is 8-bit or 32-bit and process accordingly
581
        if (media_data->is8Bit()) {
7✔
582
            // Get 8-bit image data
583
            std::vector<uint8_t> image_data;
5✔
584
            if (use_processed_data) {
5✔
585
                image_data = media_data->getProcessedData8(static_cast<int>(time.getValue()));
3✔
586
            } else {
587
                image_data = media_data->getRawData8(static_cast<int>(time.getValue()));
2✔
588
            }
589

590
            if (image_data.empty()) {
5✔
591
                continue;
×
592
            }
593

594
            // Process lines with 8-bit data
595
            aligned_lines = processLinesWithImageData(lines, image_data, image_size, 
10✔
596
                                                    width, perpendicular_range, approach, output_mode);
5✔
597
        } else {
5✔
598
            // Get 32-bit image data
599
            std::vector<float> image_data;
2✔
600
            if (use_processed_data) {
2✔
601
                image_data = media_data->getProcessedData32(static_cast<int>(time.getValue()));
×
602
            } else {
603
                image_data = media_data->getRawData32(static_cast<int>(time.getValue()));
2✔
604
            }
605

606
            if (image_data.empty()) {
2✔
UNCOV
607
                continue;
×
608
            }
609

610
            // Process lines with 32-bit data
611
            aligned_lines = processLinesWithImageData(lines, image_data, image_size, 
4✔
612
                                                    width, perpendicular_range, approach, output_mode);
2✔
613
        }
2✔
614

615
        // Add the aligned lines to the new LineData
616
        for (auto const & aligned_line: aligned_lines) {
16✔
617
            aligned_line_data->addAtTime(time, aligned_line, false);
9✔
618
        }
619

620
        processed_time_points++;
7✔
621
        if (progressCallback) {
7✔
622
            int current_progress = static_cast<int>(std::round(static_cast<double>(processed_time_points) / static_cast<double>(total_time_points) * 100.0));
7✔
623
            progressCallback(current_progress);
7✔
624
        }
625
    }
7✔
626

627
    if (progressCallback) progressCallback(100);
7✔
628
    return aligned_line_data;
7✔
629
}
7✔
630

631
template uint8_t get_pixel_value<uint8_t>(Point2D<float> const & point,
632
                                          std::vector<uint8_t> const & image_data,
633
                                          ImageSize const & image_size);
634

635
template float get_pixel_value<float>(Point2D<float> const & point,
636
                                      std::vector<float> const & image_data,
637
                                      ImageSize const & image_size);
638

639

640
template Point2D<float> calculate_fwhm_center<uint8_t>(Point2D<float> const & vertex,
641
                                                       Point2D<float> const & perpendicular_dir,
642
                                                       int width,
643
                                                       int perpendicular_range,
644
                                                       std::vector<uint8_t> const & image_data,
645
                                                       ImageSize const & image_size,
646
                                                       FWHMApproach approach = FWHMApproach::PEAK_WIDTH_HALF_MAX);
647
template Point2D<float> calculate_fwhm_center<float>(Point2D<float> const & vertex,
648
                                                     Point2D<float> const & perpendicular_dir,
649
                                                     int width,
650
                                                     int perpendicular_range,
651
                                                     std::vector<float> const & image_data,
652
                                                     ImageSize const & image_size,
653
                                                     FWHMApproach approach = FWHMApproach::PEAK_WIDTH_HALF_MAX);
654

655

656
template Line2D calculate_fwhm_profile_extents<uint8_t>(Point2D<float> const & vertex,
657
                                                        Point2D<float> const & perpendicular_dir,
658
                                                        int width,
659
                                                        int perpendicular_range,
660
                                                        std::vector<uint8_t> const & image_data,
661
                                                        ImageSize const & image_size,
662
                                                        FWHMApproach approach = FWHMApproach::PEAK_WIDTH_HALF_MAX);
663
template Line2D calculate_fwhm_profile_extents<float>(Point2D<float> const & vertex,
664
                                                      Point2D<float> const & perpendicular_dir,
665
                                                      int width,
666
                                                      int perpendicular_range,
667
                                                      std::vector<float> const & image_data,
668
                                                      ImageSize const & image_size,
669
                                                      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