| 299 | } |
| 300 | |
| 301 | static bool compareImages( |
| 302 | const std::filesystem::path& pathA, |
| 303 | const std::filesystem::path& pathB, |
| 304 | ErrorMetric metric, |
| 305 | float threshold, |
| 306 | bool alpha, |
| 307 | const std::filesystem::path& heatMapPath |
| 308 | ) |
| 309 | { |
| 310 | auto loadImage = [](const std::filesystem::path& path) |
| 311 | { |
| 312 | try |
| 313 | { |
| 314 | return Image::loadFromFile(path); |
| 315 | } |
| 316 | catch (const std::runtime_error& e) |
| 317 | { |
| 318 | std::cerr << "Cannot load image from '" << path.string() << "' (Error: " << e.what() << ")." << std::endl; |
| 319 | return std::shared_ptr<Image>{}; |
| 320 | } |
| 321 | }; |
| 322 | |
| 323 | auto saveImage = [](const Image& image, const std::filesystem::path& path) |
| 324 | { |
| 325 | try |
| 326 | { |
| 327 | image.saveToFile(path); |
| 328 | } |
| 329 | catch (const std::runtime_error& e) |
| 330 | { |
| 331 | std::cerr << "Cannot save image to '" << path.string() << "' (Error: " << e.what() << ")." << std::endl; |
| 332 | } |
| 333 | }; |
| 334 | |
| 335 | // Load images. |
| 336 | auto imageA = loadImage(pathA); |
| 337 | if (!imageA) |
| 338 | return false; |
| 339 | auto imageB = loadImage(pathB); |
| 340 | if (!imageB) |
| 341 | return false; |
| 342 | |
| 343 | // Check resolution. |
| 344 | if (imageA->getWidth() != imageB->getWidth() || imageA->getHeight() != imageB->getHeight()) |
| 345 | { |
| 346 | std::cerr << "Cannot compare images with different resolutions." << std::endl; |
| 347 | return false; |
| 348 | } |
| 349 | |
| 350 | uint32_t width = imageA->getWidth(); |
| 351 | uint32_t height = imageB->getHeight(); |
| 352 | |
| 353 | // Compare images. |
| 354 | std::unique_ptr<float[]> errorMap = heatMapPath.empty() ? nullptr : std::make_unique<float[]>(width * height); |
| 355 | double error = metric.compare(*imageA, *imageB, alpha, errorMap.get()); |
| 356 | |
| 357 | // Generate heat map. |
| 358 | if (errorMap) |