|
| 1 | +#include <catch2/catch_test_macros.hpp> |
| 2 | +#include <opencv2/core.hpp> |
| 3 | +// imwrite |
| 4 | +#include <opencv2/imgcodecs.hpp> |
| 5 | +#include <opencv2/imgproc.hpp> |
| 6 | + |
| 7 | +#include <videostrip_core/enhance/image_enhancers.hpp> |
| 8 | + |
| 9 | +using namespace videostrip; |
| 10 | + |
| 11 | +static cv::Mat toy2x2() { |
| 12 | + cv::Mat m(2,2,CV_8UC3); |
| 13 | + m.at<cv::Vec3b>(0,0) = {10,20,30}; |
| 14 | + m.at<cv::Vec3b>(0,1) = {40,50,60}; |
| 15 | + m.at<cv::Vec3b>(1,0) = {70,80,90}; |
| 16 | + m.at<cv::Vec3b>(1,1) = {100,110,120}; |
| 17 | + return m; |
| 18 | +} |
| 19 | + |
| 20 | +TEST_CASE("ContrastOffset exactness", "[enhance][ops]") { |
| 21 | + cv::Mat m = toy2x2(); |
| 22 | + Enhancer e; |
| 23 | + e.setSequence({ {EnhanceType::ContrastOffset, ContrastOffsetParams{2.0, 10.0}} }); |
| 24 | + REQUIRE(e.apply(m)); |
| 25 | + auto expect = [&](int r,int c){ |
| 26 | + auto v = m.at<cv::Vec3b>(r,c); |
| 27 | + auto in = toy2x2().at<cv::Vec3b>(r,c); |
| 28 | + for (int k=0;k<3;++k) { |
| 29 | + int ref = std::min(255, std::max(0, int(2*in[k] + 10))); |
| 30 | + CHECK(v[k] == ref); |
| 31 | + } |
| 32 | + }; |
| 33 | + expect(0,0); expect(0,1); expect(1,0); expect(1,1); |
| 34 | +} |
| 35 | + |
| 36 | +TEST_CASE("GrayWorld balances means", "[enhance][ops]") { |
| 37 | + cv::Mat m(32,32,CV_8UC3, cv::Scalar(10, 50, 200)); // B,G,R |
| 38 | + Enhancer e; |
| 39 | + e.setSequence({ {EnhanceType::GrayWorldWB, GrayWorldParams{}} }); |
| 40 | + REQUIRE(e.apply(m)); |
| 41 | + cv::Scalar mean = cv::mean(m); |
| 42 | + CHECK(std::abs(mean[0] - mean[1]) <= 1.5); |
| 43 | + CHECK(std::abs(mean[1] - mean[2]) <= 1.5); |
| 44 | +} |
| 45 | + |
| 46 | +TEST_CASE("Gamma LUT monotonic and anchors", "[enhance][ops]") { |
| 47 | + cv::Mat m(1,4,CV_8UC3); |
| 48 | + m.at<cv::Vec3b>(0,0) = {0,0,0}; |
| 49 | + m.at<cv::Vec3b>(0,1) = {64,64,64}; |
| 50 | + m.at<cv::Vec3b>(0,2) = {128,128,128}; |
| 51 | + m.at<cv::Vec3b>(0,3) = {255,255,255}; |
| 52 | + Enhancer e; |
| 53 | + e.setSequence({ {EnhanceType::Gamma, GammaParams{2.0}} }); |
| 54 | + REQUIRE(e.apply(m)); |
| 55 | + CHECK(m.at<cv::Vec3b>(0,0) == cv::Vec3b(0,0,0)); |
| 56 | + CHECK(m.at<cv::Vec3b>(0,3) == cv::Vec3b(255,255,255)); |
| 57 | + CHECK(m.at<cv::Vec3b>(0,1)[0] < m.at<cv::Vec3b>(0,2)[0]); // monotonic |
| 58 | + CHECK(m.at<cv::Vec3b>(0,2)[0] > 128); // compressed mid-tones |
| 59 | +} |
| 60 | + |
| 61 | +// TEST_CASE("CLAHE increases gray stddev", "[enhance][ops]") { |
| 62 | +// // if we use uniform input image clahe does nothing |
| 63 | +// // so we use a mid-gray image and check that CLAHE increases contrast |
| 64 | +// cv::Mat m(64,64,CV_8UC3, cv::Scalar(60,60,60)); // we operate on grayscale percentiles, assuming CLAHE will increase luminance range |
| 65 | +// // Then set a darker square in the middle (16x16) |
| 66 | +// for (int r=24; r<40; ++r) { |
| 67 | +// uchar* row = m.ptr<uchar>(r); |
| 68 | +// for (int c=24; c<40; ++c) { |
| 69 | +// row[3*c+0] = 30; |
| 70 | +// row[3*c+1] = 30; |
| 71 | +// row[3*c+2] = 30; |
| 72 | +// } |
| 73 | +// } |
| 74 | +// cv::imwrite("clahe_input.png", m); |
| 75 | +// // lambda with quick histogram estimation of percentiles |
| 76 | +// auto pctl = [](const cv::Mat& img, double p)->int { |
| 77 | +// cv::Mat g; cv::cvtColor(img, g, cv::COLOR_BGR2GRAY); |
| 78 | +// int hist[256] = {0}; |
| 79 | +// for (int r=0; r<g.rows; ++r) { |
| 80 | +// const uchar* row = g.ptr<uchar>(r); |
| 81 | +// for (int c=0; c<g.cols; ++c) ++hist[row[c]]; |
| 82 | +// } |
| 83 | +// const int N = g.rows * g.cols; |
| 84 | +// const int target = int(std::round(p * N)); |
| 85 | +// int acc = 0; |
| 86 | +// for (int v=0; v<256; ++v) { acc += hist[v]; if (acc >= target) return v; } |
| 87 | +// return 255; |
| 88 | +// }; |
| 89 | + |
| 90 | +// int before_p10 = pctl(m, 0.10); |
| 91 | +// int before_p90 = pctl(m, 0.90); |
| 92 | + |
| 93 | +// Enhancer e; |
| 94 | +// e.setSequence({ {EnhanceType::CLAHE, ClaheParams{2.0, {8,8}, ClaheSpace::YCrCb}} }); |
| 95 | +// REQUIRE(e.apply(m)); |
| 96 | + |
| 97 | +// int after_p10 = pctl(m, 0.10); |
| 98 | +// int after_p90 = pctl(m, 0.90); |
| 99 | + |
| 100 | +// cv::imwrite("clahe_output.png", m); |
| 101 | +// // CLAHE should increase dynamic range in luminance percentiles |
| 102 | +// CHECK((after_p90 - after_p10) > (before_p90 - before_p10)); |
| 103 | +// } |
0 commit comments