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

paulmthompson / WhiskerToolbox / 17402643318

02 Sep 2025 11:53AM UTC coverage: 71.394% (-0.3%) from 71.68%
17402643318

push

github

paulmthompson
remove unnecessary files

31766 of 44494 relevant lines covered (71.39%)

1386.13 hits per line

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

0.0
/src/ImageProcessing/src/OpenCVUtility.cpp
1
#include "ImageProcessing/OpenCVUtility.hpp"
2

3
#include <opencv2/imgcodecs.hpp>
4
#include <opencv2/imgproc.hpp>
5
#include <opencv2/opencv.hpp>
6
#include <opencv2/photo.hpp>
7
#include <iostream>
8

9
namespace ImageProcessing {
10

11
cv::Mat load_mask_from_image(std::string const & filename, bool const invert) {
×
12
    cv::Mat image = cv::imread(filename, cv::IMREAD_GRAYSCALE);
×
13

14
    if (image.empty()) {
×
15
        std::cerr << "Could not open or find the image: " << filename << std::endl;
×
16
        return cv::Mat(); // Return an empty matrix
×
17
    }
18

19
    if (invert) {
×
20
        cv::bitwise_not(image, image);
×
21
    }
22

23
    return image;
×
24
}
×
25

26
cv::Mat convert_vector_to_mat(std::vector<uint8_t> & vec, ImageSize const image_size) {
×
27
    // Determine the number of channels
28
    int channels = static_cast<int>(vec.size()) / (image_size.width * image_size.height);
×
29

30
    // Determine the OpenCV type based on the number of channels
31
    int cv_type;
32
    if (channels == 1) {
×
33
        cv_type = CV_8UC1; // Grayscale
×
34
    } else if (channels == 3) {
×
35
        cv_type = CV_8UC3; // BGR
×
36
    } else if (channels == 4) {
×
37
        cv_type = CV_8UC4; // BGRA
×
38
    } else {
39
        std::cerr << "Unsupported number of channels: " << channels << std::endl;
×
40
        return cv::Mat(); // Return an empty matrix
×
41
    }
42

43
    return cv::Mat(image_size.height, image_size.width, cv_type, vec.data());
×
44
}
45

46
cv::Mat convert_vector_to_mat(std::vector<Point2D<float>> & vec, ImageSize const image_size) {
×
47
    cv::Mat mask_image(image_size.height, image_size.width, CV_8UC1, cv::Scalar(0));
×
48

49
    for (auto const & point : vec) {
×
50
        int x = static_cast<int>(std::round(point.x));
×
51
        int y = static_cast<int>(std::round(point.y));
×
52

53
        // Check bounds
54
        if (x >= 0 && x < image_size.width && y >= 0 && y < image_size.height) {
×
55
            mask_image.at<uint8_t>(y, x) = 255; // Set pixel to white (255)
×
56
        }
57
    }
58

59
    return mask_image;
×
60
}
61

62
void convert_mat_to_vector(std::vector<uint8_t> & vec, cv::Mat & mat, ImageSize const image_size) {
×
63
    // Check if the matrix has the expected size
64
    if (mat.rows != image_size.height || mat.cols != image_size.width) {
×
65
        std::cerr << "Matrix size does not match the expected image size." << std::endl;
×
66
        return;
×
67
    }
68

69
    // Resize the vector to hold all the pixel data
70
    vec.resize(mat.rows * mat.cols * mat.channels());
×
71

72
    // Copy data from the matrix to the vector
73
    std::memcpy(vec.data(), mat.data, vec.size());
×
74
}
75

76
std::vector<Point2D<uint32_t>> create_mask(cv::Mat const & mat) {
×
77
    std::vector<Point2D<uint32_t>> mask;
×
78

79
    for (int y = 0; y < mat.rows; ++y) {
×
80
        for (int x = 0; x < mat.cols; ++x) {
×
81
            if (mat.at<uint8_t>(y, x) > 0) { // Assuming a binary mask where 0 is background
×
82
                mask.push_back({static_cast<uint32_t>(x), static_cast<uint32_t>(y)});
×
83
            }
84
        }
85
    }
86

87
    return mask;
×
88
}
×
89

90
void grow_mask(cv::Mat & mat, int const dilation_size) {
×
91
    cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE,
×
92
                                                cv::Size(2 * dilation_size + 1, 2 * dilation_size + 1),
×
93
                                                cv::Point(dilation_size, dilation_size));
×
94
    cv::dilate(mat, mat, element);
×
95
}
×
96

97
void median_blur(cv::Mat & mat, int const kernel_size) {
×
98
    cv::medianBlur(mat, mat, kernel_size);
×
99
}
×
100

101
// New options-based implementations
102
void linear_transform(cv::Mat & mat, ContrastOptions const& options) {
×
103
    double alpha = options.alpha;
×
104
    int beta = options.beta;
×
105

106
    // Always calculate alpha and beta from min/max values for consistent behavior
107
    if (options.display_max <= options.display_min) {
×
108
        alpha = 1.0;
×
109
        beta = 0;
×
110
    } else {
111
        alpha = 255.0 / (options.display_max - options.display_min);
×
112
        beta = static_cast<int>(-alpha * options.display_min);
×
113
    }
114

115
    mat.convertTo(mat, -1, alpha, beta);
×
116
}
×
117

118
void gamma_transform(cv::Mat & mat, GammaOptions const& options) {
×
119
    if (mat.depth() == CV_8U) {
×
120
        // Original 8-bit implementation using lookup table
121
        cv::Mat lookupTable(1, 256, CV_8U);
×
122
        uchar* p = lookupTable.ptr();
×
123
        for (int i = 0; i < 256; ++i) {
×
124
            p[i] = cv::saturate_cast<uchar>(pow(i / 255.0, options.gamma) * 255.0);
×
125
        }
126
        cv::LUT(mat, lookupTable, mat);
×
127
    } else if (mat.depth() == CV_32F) {
×
128
        // 32-bit float implementation using direct computation
129
        // For float data, we assume it's normalized to 0-255 range
130
        mat.forEach<float>([&options](float& pixel, const int* /*position*/) {
×
131
            // Normalize to 0-1 range, apply gamma, then scale back to 0-255
132
            float normalized = pixel / 255.0f;
×
133
            normalized = std::max(0.0f, std::min(1.0f, normalized)); // Clamp to valid range
×
134
            pixel = std::pow(normalized, options.gamma) * 255.0f;
×
135
        });
×
136
    } else {
137
        // For other bit depths, convert to 8-bit temporarily, apply gamma, then convert back
138
        cv::Mat temp;
×
139
        mat.convertTo(temp, CV_8U);
×
140
        
141
        cv::Mat lookupTable(1, 256, CV_8U);
×
142
        uchar* p = lookupTable.ptr();
×
143
        for (int i = 0; i < 256; ++i) {
×
144
            p[i] = cv::saturate_cast<uchar>(pow(i / 255.0, options.gamma) * 255.0);
×
145
        }
146
        cv::LUT(temp, lookupTable, temp);
×
147
        
148
        temp.convertTo(mat, mat.depth());
×
149
    }
×
150
}
×
151

152
void clahe(cv::Mat & mat, ClaheOptions const& options) {
×
153
    if (mat.depth() == CV_8U) {
×
154
        // CLAHE works directly with 8-bit data
155
        cv::Ptr<cv::CLAHE> clahe_ptr = cv::createCLAHE(options.clip_limit, cv::Size(options.grid_size, options.grid_size));
×
156
        clahe_ptr->apply(mat, mat);
×
157
    } else if (mat.depth() == CV_32F) {
×
158
        // For 32-bit float, convert to 8-bit, apply CLAHE, then convert back
159
        cv::Mat temp_8bit;
×
160
        mat.convertTo(temp_8bit, CV_8U);
×
161
        
162
        cv::Ptr<cv::CLAHE> clahe_ptr = cv::createCLAHE(options.clip_limit, cv::Size(options.grid_size, options.grid_size));
×
163
        clahe_ptr->apply(temp_8bit, temp_8bit);
×
164
        
165
        temp_8bit.convertTo(mat, CV_32F);
×
166
    } else {
×
167
        // For other bit depths, convert to 8-bit, apply CLAHE, then convert back
168
        cv::Mat temp_8bit;
×
169
        mat.convertTo(temp_8bit, CV_8U);
×
170
        
171
        cv::Ptr<cv::CLAHE> clahe_ptr = cv::createCLAHE(options.clip_limit, cv::Size(options.grid_size, options.grid_size));
×
172
        clahe_ptr->apply(temp_8bit, temp_8bit);
×
173
        
174
        temp_8bit.convertTo(mat, mat.depth());
×
175
    }
×
176
}
×
177

178
void sharpen_image(cv::Mat & mat, SharpenOptions const& options) {
×
179
    cv::Mat blurred;
×
180
    cv::GaussianBlur(mat, blurred, cv::Size(0, 0), options.sigma);
×
181
    cv::addWeighted(mat, 1.0 + 1.0, blurred, -1.0, 0, mat);
×
182
}
×
183

184
void bilateral_filter(cv::Mat & mat, BilateralOptions const& options) {
×
185
    cv::Mat temp;
×
186
    cv::bilateralFilter(mat, temp, options.diameter, options.sigma_color, options.sigma_spatial);
×
187
    mat = temp;
×
188
}
×
189

190
void median_filter(cv::Mat & mat, MedianOptions const& options) {
×
191
    if (options.kernel_size >= 3 && options.kernel_size % 2 == 1) {
×
192
        cv::medianBlur(mat, mat, options.kernel_size);
×
193
    }
194
}
×
195

196
std::vector<Point2D<uint32_t>> dilate_mask(std::vector<Point2D<uint32_t>> const& mask, ImageSize image_size, MaskDilationOptions const& options) {
×
197
    if (mask.empty() || !options.active) {
×
198
        return mask;
×
199
    }
200
    
201
    // Convert point-based mask to cv::Mat
202
    cv::Mat mask_mat = cv::Mat::zeros(image_size.height, image_size.width, CV_8UC1);
×
203
    
204
    // Fill the mask matrix with points
205
    for (auto const& point : mask) {
×
206
        int x = static_cast<int>(std::round(point.x));
×
207
        int y = static_cast<int>(std::round(point.y));
×
208
        if (x >= 0 && x < image_size.width && y >= 0 && y < image_size.height) {
×
209
            mask_mat.at<uint8_t>(y, x) = 255;
×
210
        }
211
    }
212
    
213
    // Apply dilation/erosion
214
    dilate_mask_mat(mask_mat, options);
×
215
    
216
    // Convert back to point-based representation
217
    return create_mask(mask_mat);
×
218
}
×
219

220
void dilate_mask_mat(cv::Mat& mat, MaskDilationOptions const& options) {
×
221
    if (!options.active) {
×
222
        return;
×
223
    }
224
    
225
    int kernel_size = options.is_grow_mode ? options.grow_size : options.shrink_size;
×
226
    
227
    if (kernel_size <= 0) {
×
228
        return;
×
229
    }
230
    
231
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(kernel_size, kernel_size));
×
232
    
233
    if (options.is_grow_mode) {
×
234
        cv::dilate(mat, mat, kernel);
×
235
    } else {
236
        cv::erode(mat, mat, kernel);
×
237
    }
238
}
×
239

240
std::vector<uint8_t> apply_magic_eraser_with_options(std::vector<uint8_t> const& image, 
×
241
                                                    ImageSize image_size, 
242
                                                    std::vector<uint8_t> const& mask,
243
                                                    MagicEraserOptions const& options) {
244
    // Create mutable copies for the conversion functions
245
    std::vector<uint8_t> image_copy = image;
×
246
    std::vector<uint8_t> mask_copy = mask;
×
247
    
248
    // Convert the input vector to a cv::Mat
249
    cv::Mat inputImage = convert_vector_to_mat(image_copy, image_size);
×
250
    cv::Mat inputImage3Channel;
×
251
    cv::cvtColor(inputImage, inputImage3Channel, cv::COLOR_GRAY2BGR);
×
252

253
    // Apply median blur filter with configurable size
254
    cv::Mat medianBlurredImage;
×
255
    int filter_size = options.median_filter_size;
×
256
    // Ensure filter size is odd and within valid range
257
    if (filter_size % 2 == 0) filter_size += 1;
×
258
    if (filter_size < 3) filter_size = 3;
×
259
    if (filter_size > 101) filter_size = 101;
×
260
    
261
    cv::medianBlur(inputImage, medianBlurredImage, filter_size);
×
262
    cv::Mat medianBlurredImage3Channel;
×
263
    cv::cvtColor(medianBlurredImage, medianBlurredImage3Channel, cv::COLOR_GRAY2BGR);
×
264

265
    cv::Mat maskImage = convert_vector_to_mat(mask_copy, image_size);
×
266

267
    cv::Mat smoothedMask;
×
268
    cv::GaussianBlur(maskImage, smoothedMask, cv::Size(15, 15), 0);
×
269

270
    cv::threshold(smoothedMask, smoothedMask, 1, 255, cv::THRESH_BINARY);
×
271

272
    for (int y = 0; y < smoothedMask.rows; ++y) {
×
273
        for (int x = 0; x < smoothedMask.cols; ++x) {
×
274
            if (smoothedMask.at<uint8_t>(y, x) == 0) {
×
275
                smoothedMask.at<uint8_t>(y, x) = 1;
×
276
            }
277
        }
278
    }
279

280
    cv::Mat mask3Channel;
×
281
    cv::cvtColor(smoothedMask, mask3Channel, cv::COLOR_GRAY2BGR);
×
282

283
    cv::Mat outputImage;
×
284
    cv::Point const center(image_size.width / 2, image_size.height / 2);
×
285

286
    cv::seamlessClone(medianBlurredImage3Channel, inputImage3Channel, mask3Channel, center, outputImage, cv::NORMAL_CLONE);
×
287

288
    cv::Mat outputImageGray;
×
289
    cv::cvtColor(outputImage, outputImageGray, cv::COLOR_BGR2GRAY);
×
290
    auto output = std::vector<uint8_t>(static_cast<size_t>(image_size.height * image_size.width));
×
291

292
    convert_mat_to_vector(output, outputImageGray, image_size);
×
293

294
    return output;
×
295
}
×
296

297
void apply_magic_eraser(cv::Mat& mat, MagicEraserOptions const& options) {
×
298

299
    auto mask = options.mask;
×
300
    // Only apply if we have a valid mask
301
    if (mask.empty() || options.image_size.width <= 0 || options.image_size.height <= 0) {
×
302
        return;
×
303
    }
304
    
305
    // Check if the image dimensions match the stored mask dimensions
306
    if (mat.rows != options.image_size.height || mat.cols != options.image_size.width) {
×
307
        std::cerr << "Magic eraser: Image dimensions don't match stored mask dimensions" << std::endl;
×
308
        return;
×
309
    }
310
    
311
    // Convert the image to 3-channel for seamless cloning
312
    cv::Mat inputImage3Channel;
×
313
    if (mat.channels() == 1) {
×
314
        cv::cvtColor(mat, inputImage3Channel, cv::COLOR_GRAY2BGR);
×
315
    } else {
316
        inputImage3Channel = mat.clone();
×
317
    }
318

319
    // Apply median blur filter with configurable size
320
    cv::Mat medianBlurredImage;
×
321
    int filter_size = options.median_filter_size;
×
322
    // Ensure filter size is odd and within valid range
323
    if (filter_size % 2 == 0) filter_size += 1;
×
324
    if (filter_size < 3) filter_size = 3;
×
325
    if (filter_size > 101) filter_size = 101;
×
326
    
327
    cv::Mat grayMat;
×
328
    if (mat.channels() == 3) {
×
329
        cv::cvtColor(mat, grayMat, cv::COLOR_BGR2GRAY);
×
330
    } else {
331
        grayMat = mat.clone();
×
332
    }
333
    
334
    cv::medianBlur(grayMat, medianBlurredImage, filter_size);
×
335
    cv::Mat medianBlurredImage3Channel;
×
336
    cv::cvtColor(medianBlurredImage, medianBlurredImage3Channel, cv::COLOR_GRAY2BGR);
×
337

338
    // Create mask image from stored mask data
339
    cv::Mat maskImage = convert_vector_to_mat(mask, options.image_size);
×
340

341
    cv::Mat smoothedMask;
×
342
    cv::GaussianBlur(maskImage, smoothedMask, cv::Size(15, 15), 0);
×
343

344
    cv::threshold(smoothedMask, smoothedMask, 1, 255, cv::THRESH_BINARY);
×
345

346
    // Ensure mask has valid values for seamless cloning
347
    for (int y = 0; y < smoothedMask.rows; ++y) {
×
348
        for (int x = 0; x < smoothedMask.cols; ++x) {
×
349
            if (smoothedMask.at<uint8_t>(y, x) == 0) {
×
350
                smoothedMask.at<uint8_t>(y, x) = 1;
×
351
            }
352
        }
353
    }
354

355
    cv::Mat mask3Channel;
×
356
    cv::cvtColor(smoothedMask, mask3Channel, cv::COLOR_GRAY2BGR);
×
357

358
    cv::Mat outputImage;
×
359
    cv::Point const center(options.image_size.width / 2, options.image_size.height / 2);
×
360

361
    cv::seamlessClone(medianBlurredImage3Channel, inputImage3Channel, mask3Channel, center, outputImage, cv::NORMAL_CLONE);
×
362

363
    // Convert back to the original format
364
    if (mat.channels() == 1) {
×
365
        cv::cvtColor(outputImage, mat, cv::COLOR_BGR2GRAY);
×
366
    } else {
367
        mat = outputImage;
×
368
    }
369
}
×
370

371
void apply_colormap(cv::Mat& mat, ColormapOptions const& options) {
×
372
    if (!options.active || options.colormap == ColormapType::None) {
×
373
        return;
×
374
    }
375
    
376
    // Only apply to grayscale images
377
    if (mat.channels() != 1) {
×
378
        return;
×
379
    }
380
    
381
    cv::Mat normalized_mat;
×
382
    if (options.normalize) {
×
383
        // Normalize to 0-255 range and convert to 8-bit for colormap application
384
        cv::normalize(mat, normalized_mat, 0, 255, cv::NORM_MINMAX, CV_8UC1);
×
385
    } else {
386
        // Convert to 8-bit if necessary for colormap application
387
        if (mat.depth() == CV_8U) {
×
388
            normalized_mat = mat.clone();
×
389
        } else {
390
            mat.convertTo(normalized_mat, CV_8U);
×
391
        }
392
    }
393
    
394
    // Apply the colormap
395
    cv::Mat colored_mat;
×
396
    int cv_colormap = cv::COLORMAP_JET; // default
×
397
    
398
    switch (options.colormap) {
×
399
        case ColormapType::Jet:
×
400
            cv_colormap = cv::COLORMAP_JET;
×
401
            break;
×
402
        case ColormapType::Hot:
×
403
            cv_colormap = cv::COLORMAP_HOT;
×
404
            break;
×
405
        case ColormapType::Cool:
×
406
            cv_colormap = cv::COLORMAP_COOL;
×
407
            break;
×
408
        case ColormapType::Spring:
×
409
            cv_colormap = cv::COLORMAP_SPRING;
×
410
            break;
×
411
        case ColormapType::Summer:
×
412
            cv_colormap = cv::COLORMAP_SUMMER;
×
413
            break;
×
414
        case ColormapType::Autumn:
×
415
            cv_colormap = cv::COLORMAP_AUTUMN;
×
416
            break;
×
417
        case ColormapType::Winter:
×
418
            cv_colormap = cv::COLORMAP_WINTER;
×
419
            break;
×
420
        case ColormapType::Rainbow:
×
421
            cv_colormap = cv::COLORMAP_RAINBOW;
×
422
            break;
×
423
        case ColormapType::Ocean:
×
424
            cv_colormap = cv::COLORMAP_OCEAN;
×
425
            break;
×
426
        case ColormapType::Pink:
×
427
            cv_colormap = cv::COLORMAP_PINK;
×
428
            break;
×
429
        case ColormapType::HSV:
×
430
            cv_colormap = cv::COLORMAP_HSV;
×
431
            break;
×
432
        case ColormapType::Parula:
×
433
            cv_colormap = cv::COLORMAP_PARULA;
×
434
            break;
×
435
        case ColormapType::Viridis:
×
436
            cv_colormap = cv::COLORMAP_VIRIDIS;
×
437
            break;
×
438
        case ColormapType::Plasma:
×
439
            cv_colormap = cv::COLORMAP_PLASMA;
×
440
            break;
×
441
        case ColormapType::Inferno:
×
442
            cv_colormap = cv::COLORMAP_INFERNO;
×
443
            break;
×
444
        case ColormapType::Magma:
×
445
            cv_colormap = cv::COLORMAP_MAGMA;
×
446
            break;
×
447
        case ColormapType::Turbo:
×
448
            cv_colormap = cv::COLORMAP_TURBO;
×
449
            break;
×
450
        default:
×
451
            cv_colormap = cv::COLORMAP_JET;
×
452
            break;
×
453
    }
454
    
455
    cv::applyColorMap(normalized_mat, colored_mat, cv_colormap);
×
456
    
457
    // Apply alpha blending if needed
458
    if (options.alpha < 1.0) {
×
459
        // Convert original grayscale to BGR for blending
460
        cv::Mat gray_bgr;
×
461
        cv::cvtColor(normalized_mat, gray_bgr, cv::COLOR_GRAY2BGR);
×
462
        
463
        // Blend colored image with grayscale
464
        cv::addWeighted(colored_mat, options.alpha, gray_bgr, 1.0 - options.alpha, 0, colored_mat);
×
465
    }
×
466
    
467
    // Return BGR format (not BGRA) to maintain compatibility
468
    mat = colored_mat;
×
469
}
×
470

471
/**
472
 * @brief Apply colormap to grayscale data for display purposes only
473
 * @param grayscale_data Input grayscale image data
474
 * @param image_size Dimensions of the image
475
 * @param options Colormap options
476
 * @return BGR image data if colormap is applied, empty vector if not
477
 */
478
std::vector<uint8_t> apply_colormap_for_display(std::vector<uint8_t> const& grayscale_data, 
×
479
                                               ImageSize image_size,
480
                                               ColormapOptions const& options) {
481
    if (!options.active || options.colormap == ColormapType::None) {
×
482
        return {}; // Return empty vector to indicate no colormap applied
×
483
    }
484
    
485
    // Create cv::Mat from grayscale data
486
    cv::Mat gray_mat(image_size.height, image_size.width, CV_8UC1, 
×
487
                     const_cast<uint8_t*>(grayscale_data.data()));
×
488
    
489
    cv::Mat normalized_mat;
×
490
    if (options.normalize) {
×
491
        cv::normalize(gray_mat, normalized_mat, 0, 255, cv::NORM_MINMAX, CV_8UC1);
×
492
    } else {
493
        normalized_mat = gray_mat.clone();
×
494
    }
495
    
496
    // Apply the colormap
497
    cv::Mat colored_mat;
×
498
    int cv_colormap = cv::COLORMAP_JET; // default
×
499
    
500
    switch (options.colormap) {
×
501
        case ColormapType::Jet:
×
502
            cv_colormap = cv::COLORMAP_JET;
×
503
            break;
×
504
        case ColormapType::Hot:
×
505
            cv_colormap = cv::COLORMAP_HOT;
×
506
            break;
×
507
        case ColormapType::Cool:
×
508
            cv_colormap = cv::COLORMAP_COOL;
×
509
            break;
×
510
        case ColormapType::Spring:
×
511
            cv_colormap = cv::COLORMAP_SPRING;
×
512
            break;
×
513
        case ColormapType::Summer:
×
514
            cv_colormap = cv::COLORMAP_SUMMER;
×
515
            break;
×
516
        case ColormapType::Autumn:
×
517
            cv_colormap = cv::COLORMAP_AUTUMN;
×
518
            break;
×
519
        case ColormapType::Winter:
×
520
            cv_colormap = cv::COLORMAP_WINTER;
×
521
            break;
×
522
        case ColormapType::Rainbow:
×
523
            cv_colormap = cv::COLORMAP_RAINBOW;
×
524
            break;
×
525
        case ColormapType::Ocean:
×
526
            cv_colormap = cv::COLORMAP_OCEAN;
×
527
            break;
×
528
        case ColormapType::Pink:
×
529
            cv_colormap = cv::COLORMAP_PINK;
×
530
            break;
×
531
        case ColormapType::HSV:
×
532
            cv_colormap = cv::COLORMAP_HSV;
×
533
            break;
×
534
        case ColormapType::Parula:
×
535
            cv_colormap = cv::COLORMAP_PARULA;
×
536
            break;
×
537
        case ColormapType::Viridis:
×
538
            cv_colormap = cv::COLORMAP_VIRIDIS;
×
539
            break;
×
540
        case ColormapType::Plasma:
×
541
            cv_colormap = cv::COLORMAP_PLASMA;
×
542
            break;
×
543
        case ColormapType::Inferno:
×
544
            cv_colormap = cv::COLORMAP_INFERNO;
×
545
            break;
×
546
        case ColormapType::Magma:
×
547
            cv_colormap = cv::COLORMAP_MAGMA;
×
548
            break;
×
549
        case ColormapType::Turbo:
×
550
            cv_colormap = cv::COLORMAP_TURBO;
×
551
            break;
×
552
        default:
×
553
            cv_colormap = cv::COLORMAP_JET;
×
554
            break;
×
555
    }
556
    
557
    cv::applyColorMap(normalized_mat, colored_mat, cv_colormap);
×
558
    
559
    // Apply alpha blending if needed
560
    if (options.alpha < 1.0) {
×
561
        cv::Mat gray_bgr;
×
562
        cv::cvtColor(normalized_mat, gray_bgr, cv::COLOR_GRAY2BGR);
×
563
        cv::addWeighted(colored_mat, options.alpha, gray_bgr, 1.0 - options.alpha, 0, colored_mat);
×
564
    }
×
565
    
566
    // Convert to BGRA for Qt display
567
    cv::Mat bgra_mat;
×
568
    cv::cvtColor(colored_mat, bgra_mat, cv::COLOR_BGR2BGRA);
×
569
    
570
    // Convert to vector
571
    std::vector<uint8_t> result(bgra_mat.total() * bgra_mat.elemSize());
×
572
    std::memcpy(result.data(), bgra_mat.data, result.size());
×
573
    
574
    return result;
×
575
}
×
576

577
} // namespace ImageProcessing
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