diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 9e2cf07083..39839e45f5 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -1009,93 +1009,105 @@ double CV_ColorLabTest::get_success_error_level( int /*test_case_idx*/, int i, i static const double _1_3 = 0.333333333333; +const static float _1_3f = static_cast(_1_3); -void CV_ColorLabTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) + +void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* dst_row, int n) { int depth = test_mat[INPUT][0].depth(); float Lscale = depth == CV_8U ? 255.f/100.f : depth == CV_16U ? 65535.f/100.f : 1.f; float ab_bias = depth == CV_8U ? 128.f : depth == CV_16U ? 32768.f : 0.f; - int j; float M[9]; - - for( j = 0; j < 9; j++ ) + + for (int j = 0; j < 9; j++ ) M[j] = (float)RGB2XYZ[j]; - - for( j = 0; j < n*3; j += 3 ) + + for (int x = 0; x < n*3; x += 3) { - float r = src_row[j+2]; - float g = src_row[j+1]; - float b = src_row[j]; - - float X = (r*M[0] + g*M[1] + b*M[2])*(1.f/Xn); - float Y = r*M[3] + g*M[4] + b*M[5]; - float Z = (r*M[6] + g*M[7] + b*M[8])*(1.f/Zn); - float fX, fY, fZ; - - float L, a; - - if( Y > 0.008856 ) + float R = src_row[x + 2]; + float G = src_row[x + 1]; + float B = src_row[x]; + + float X = (R * M[0] + G * M[1] + B * M[2]) / Xn; + float Y = R * M[3] + G * M[4] + B * M[5]; + float Z = (R * M[6] + G * M[7] + B * M[8]) / Zn; + float fX = X > 0.008856f ? pow(X, _1_3f) : + (7.787f * X + 16.f / 116.f); + float fZ = Z > 0.008856f ? pow(Z, _1_3f): + (7.787f * Z + 16.f / 116.f); + + float L = 0.0f, fY = 0.0f; + if (Y > 0.008856f) { - fY = (float)pow((double)Y,_1_3); - L = 116.f*fY - 16.f; + fY = pow(Y, _1_3f); + L = 116.f * fY - 16.f; } else { - fY = 7.787f*Y + 16.f/116.f; - L = 903.3f*Y; + fY = 7.787f * Y + 16.f / 116.f; + L = 903.3f * Y; } - - if( X > 0.008856 ) - fX = (float)pow((double)X,_1_3); - else - fX = 7.787f*X + 16.f/116.f; - - if( Z > 0.008856 ) - fZ = (float)pow((double)Z,_1_3); - else - fZ = 7.787f*Z + 16.f/116.f; - - a = 500.f*(fX - fY); - b = 200.f*(fY - fZ); - - dst_row[j] = L*Lscale; - dst_row[j+1] = a + ab_bias; - dst_row[j+2] = b + ab_bias; + + float a = 500.f * (fX - fY); + float b = 200.f * (fY - fZ); + + dst_row[x] = L * Lscale; + dst_row[x + 1] = a + ab_bias; + dst_row[x + 2] = b + ab_bias; } } - void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) { int depth = test_mat[INPUT][0].depth(); float Lscale = depth == CV_8U ? 100.f/255.f : depth == CV_16U ? 100.f/65535.f : 1.f; float ab_bias = depth == CV_8U ? 128.f : depth == CV_16U ? 32768.f : 0.f; - int j; float M[9]; - - for( j = 0; j < 9; j++ ) + + for(int j = 0; j < 9; j++ ) M[j] = (float)XYZ2RGB[j]; - - for( j = 0; j < n*3; j += 3 ) + + static const float lthresh = 903.3f * 0.008856f; + static const float thresh = 7.787f * 0.008856f + 16.0f / 116.0f; + for (int x = 0, end = n * 3; x < end; x += 3) { - float L = src_row[j]*Lscale; - float a = src_row[j+1] - ab_bias; - float b = src_row[j+2] - ab_bias; - - float P = (L + 16.f)*(1.f/116.f); - float X = (P + a*0.002f); - float Z = (P - b*0.005f); - float Y = P*P*P; - X = Xn*X*X*X; - Z = Zn*Z*Z*Z; - - float r = M[0]*X + M[1]*Y + M[2]*Z; - float g = M[3]*X + M[4]*Y + M[5]*Z; - b = M[6]*X + M[7]*Y + M[8]*Z; - - dst_row[j] = b; - dst_row[j+1] = g; - dst_row[j+2] = r; + float L = src_row[x] * Lscale; + float a = src_row[x + 1] - ab_bias; + float b = src_row[x + 2] - ab_bias; + + float FY = 0.0f, Y = 0.0f; + if (L <= lthresh) + { + Y = L / 903.3f; + FY = 7.787f * Y + 16.0f / 116.0f; + } + else + { + FY = (L + 16.0f) / 116.0f; + Y = FY * FY * FY; + } + + float FX = a / 500.0f + FY; + float FZ = FY - b / 200.0f; + + float FXZ[] = { FX, FZ }; + for (int k = 0; k < 2; ++k) + { + if (FXZ[k] <= thresh) + FXZ[k] = (FXZ[k] - 16.0f / 116.0f) / 7.787f; + else + FXZ[k] = FXZ[k] * FXZ[k] * FXZ[k]; + } + float X = FXZ[0] * Xn; + float Z = FXZ[1] * Zn; + + float R = M[0] * X + M[1] * Y + M[2] * Z; + float G = M[3] * X + M[4] * Y + M[5] * Z; + float B = M[6] * X + M[7] * Y + M[8] * Z; + + dst_row[x] = B; + dst_row[x + 1] = G; + dst_row[x + 2] = R; } } @@ -1869,3 +1881,98 @@ TEST(Imgproc_ColorBayerVNG_Strict, regression) } } } + + +void GetTestMatrix(Mat& src) +{ + Size ssize(1000, 1000); + src.create(ssize, CV_32FC3); + int szm = ssize.width - 1; + float pi2 = 2 * 3.1415f; + // Generate a pretty test image + for (int i = 0; i < ssize.height; i++) + { + for (int j = 0; j < ssize.width; j++) + { + float b = (1 + cos((szm - i) * (szm - j) * pi2 / (10 * float(szm)))) / 2; + float g = (1 + cos((szm - i) * j * pi2 / (10 * float(szm)))) / 2; + float r = (1 + sin(i * j * pi2 / (10 * float(szm)))) / 2; + + // The following lines aren't necessary, but just to prove that + // the BGR values all lie in [0,1]... + if (b < 0) b = 0; else if (b > 1) b = 1; + if (g < 0) g = 0; else if (g > 1) g = 1; + if (r < 0) r = 0; else if (r > 1) r = 1; + src.at(i, j) = cv::Vec3f(b, g, r); + } + } +} + +void validate_result(const Mat& reference, const Mat& actual, const Mat& src = Mat(), int mode = -1) +{ + cvtest::TS* ts = cvtest::TS::ptr(); + Size ssize = reference.size(); + + int cn = reference.channels(); + ssize.width *= cn; + bool next = true; + + for (int y = 0; y < ssize.height && next; ++y) + { + const float* rD = reference.ptr(y); + const float* D = actual.ptr(y); + for (int x = 0; x < ssize.width && next; ++x) + if (fabs(rD[x] - D[x]) > 0.0001f) + { + next = false; + ts->printf(cvtest::TS::SUMMARY, "Error in: (%d, %d)\n", x / cn, y); + ts->printf(cvtest::TS::SUMMARY, "Reference value: %f\n", rD[x]); + ts->printf(cvtest::TS::SUMMARY, "Actual value: %f\n", D[x]); + if (!src.empty()) + ts->printf(cvtest::TS::SUMMARY, "Src value: %f\n", src.ptr(y)[x]); + ts->printf(cvtest::TS::SUMMARY, "Size: (%d, %d)\n", reference.rows, reference.cols); + + if (mode >= 0) + { + cv::Mat lab; + cv::cvtColor(src, lab, mode); + std::cout << "lab: " << lab(cv::Rect(y, x / cn, 1, 1)) << std::endl; + } + std::cout << "src: " << src(cv::Rect(y, x / cn, 1, 1)) << std::endl; + + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + ts->set_gtest_status(); + } + } +} + +TEST(Imgproc_ColorLab_Full, accuracy) +{ + Mat src; + GetTestMatrix(src); + Mat reference(src.size(), CV_32FC3); + Size ssize = src.size(); + CV_Assert(ssize.width == ssize.height); + + RNG& rng = cvtest::TS::ptr()->get_rng(); + int blueInd = rng.uniform(0., 1.) > 0.5 ? 0 : 2; + bool srgb = rng.uniform(0., 1.) > 0.5; + + // Convert test image to LAB + cv::Mat lab; + int forward_code = blueInd ? srgb ? CV_BGR2Lab : CV_LBGR2Lab : srgb ? CV_RGB2Lab : CV_LRGB2Lab; + int inverse_code = blueInd ? srgb ? CV_Lab2BGR : CV_Lab2LBGR : srgb ? CV_Lab2RGB : CV_Lab2LRGB; + cv::cvtColor(src, lab, forward_code); + // Convert LAB image back to BGR(RGB) + cv::Mat recons; + cv::cvtColor(lab, recons, inverse_code); + + validate_result(src, recons, src, forward_code); + +// src *= 255.0f; +// recons *= 255.0f; + +// imshow("Test", src); +// imshow("OpenCV", recons); +// waitKey(); +}