| 267 | }; |
| 268 | |
| 269 | static std::shared_ptr<Image> generateHeatMap(uint32_t width, uint32_t height, const float* errorMap) |
| 270 | { |
| 271 | auto writeColor = [](float t, float* dst) |
| 272 | { |
| 273 | static const float colors[5][3] = { |
| 274 | {0.f, 0.f, 1.f}, // blue |
| 275 | {0.f, 1.f, 1.f}, // teal |
| 276 | {0.f, 1.f, 0.f}, // green |
| 277 | {1.f, 1.f, 0.f}, // yellow |
| 278 | {1.f, 0.f, 0.f}, // red |
| 279 | }; |
| 280 | |
| 281 | int c = clamp(int(std::floor(t * 4.f)), 0, 3); |
| 282 | for (size_t i = 0; i < 3; ++i) |
| 283 | *dst++ = lerp(colors[c][i], colors[c + 1][i], t * 4.f - c); |
| 284 | *dst++ = 1.f; |
| 285 | }; |
| 286 | |
| 287 | const auto [minValue, maxValue] = std::minmax_element(errorMap, errorMap + width * height); |
| 288 | const float range = std::max(1e-5f, *maxValue - *minValue); |
| 289 | auto image = Image::create(width, height); |
| 290 | float* dst = image->getData(); |
| 291 | for (size_t i = 0; i < width * height; ++i) |
| 292 | { |
| 293 | float t = clamp((errorMap[i] - *minValue) / range, 0.f, 1.f); |
| 294 | writeColor(t, dst); |
| 295 | dst += 4; |
| 296 | } |
| 297 | |
| 298 | return image; |
| 299 | } |
| 300 | |
| 301 | static bool compareImages( |
| 302 | const std::filesystem::path& pathA, |