From 0e6c335077e94f483f0cc110d86b77bc1d287338 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Thu, 10 Aug 2017 16:36:46 +0300 Subject: [PATCH 01/14] Imgproc_ColorLab_Full.accuracy test fixed --- modules/imgproc/test/test_color.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 75ae3ddddf..e920ad4646 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -1898,13 +1898,15 @@ static void validateResult(const Mat& reference, const Mat& actual, const Mat& s int cn = reference.channels(); ssize.width *= cn; bool next = true; + //RGB2Lab_f works throug LUT and brings additional error + static const float maxErr = 1.f/200.f; 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) + if(fabs(rD[x] - D[x]) > maxErr) { next = false; ts->printf(cvtest::TS::SUMMARY, "Error in: (%d, %d)\n", x / cn, y); From f0ef7bd14913887f0021878b27d8dd9f2f90e8b7 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Thu, 10 Aug 2017 17:26:42 +0300 Subject: [PATCH 02/14] Lab and Luv tests: rewritten, constants explained --- modules/imgproc/test/test_color.cpp | 129 ++++++++++++++++------------ 1 file changed, 76 insertions(+), 53 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index e920ad4646..deeeb8da91 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -1010,10 +1010,6 @@ 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) { int depth = test_mat[INPUT][0].depth(); @@ -1021,6 +1017,15 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds float ab_bias = depth == CV_8U ? 128.f : depth == CV_16U ? 32768.f : 0.f; float M[9]; + // 7.787f = (29/3)^3/(29*4) + static const float lowScale = 29.f*29.f/(27.f*4.f); + // 0.008856f = (6/29)^3 + static const float lthresh = 6.f*6.f*6.f/(29.f*29.f*29.f); + // 903.3 = (29/3)^3 + static const float yscale = 29.f*29.f*29.f/27.f; + + static const float f16of116 = 16.f/116.f; + for (int j = 0; j < 9; j++ ) M[j] = (float)RGB2XYZ[j]; @@ -1031,25 +1036,14 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds 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 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 = pow(Y, _1_3f); - L = 116.f * fY - 16.f; - } - else - { - fY = 7.787f * Y + 16.f / 116.f; - L = 903.3f * Y; - } + float fX = X > lthresh ? cubeRoot(X) : (lowScale * X + f16of116); + float fY = Y > lthresh ? cubeRoot(Y) : (lowScale * Y + f16of116); + float fZ = Z > lthresh ? cubeRoot(Z) : (lowScale * Z + f16of116); + float L = Y > lthresh ? (116.f*fY - 16.f) : (yscale*Y); float a = 500.f * (fX - fY); float b = 200.f * (fY - fZ); @@ -1069,8 +1063,16 @@ void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d for(int j = 0; j < 9; j++ ) M[j] = (float)XYZ2RGB[j]; - static const float lthresh = 903.3f * 0.008856f; - static const float thresh = 7.787f * 0.008856f + 16.0f / 116.0f; + // 0.008856f * 903.3f = (6/29)^3*(29/3)^3 = 8 + static const float lThresh = 8.f; + // 7.787f * 0.008856f + 16.0f / 116.0f = 6/29 + static const float fThresh = 6.f/29.f; + static const float lbias = 16.f/116.f; + // 7.787f = (29/3)^3/(29*4) + static const float lowScale = 29.f*29.f/(27.f*4.f); + // 903.3 = (29/3)^3 + static const float yscale = 29.f*29.f*29.f/27.f; + for (int x = 0, end = n * 3; x < end; x += 3) { float L = src_row[x] * Lscale; @@ -1078,10 +1080,10 @@ void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d float b = src_row[x + 2] - ab_bias; float FY = 0.0f, Y = 0.0f; - if (L <= lthresh) + if (L <= lThresh) { - Y = L / 903.3f; - FY = 7.787f * Y + 16.0f / 116.0f; + Y = L / yscale; + FY = lowScale * Y + lbias; } else { @@ -1095,8 +1097,8 @@ void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d 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; + if (FXZ[k] <= fThresh) + FXZ[k] = (FXZ[k] - lbias) / lowScale; else FXZ[k] = FXZ[k] * FXZ[k] * FXZ[k]; } @@ -1155,25 +1157,37 @@ void CV_ColorLuvTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* d { 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; + static const float uLow = -134.f, uHigh = 220.f, uRange = uHigh - uLow; + static const float vLow = -140.f, vHigh = 122.f, vRange = vHigh - vLow; int j; float M[9]; - float un = 4.f*Xn/(Xn + 15.f*1.f + 3*Zn); - float vn = 9.f*1.f/(Xn + 15.f*1.f + 3*Zn); + // Yn == 1 + float dd = Xn + 15.f*1.f + 3.f*Zn; + float un = 4.f*13.f*Xn/dd; + float vn = 9.f*13.f/dd; + float u_scale = 1.f, u_bias = 0.f; float v_scale = 1.f, v_bias = 0.f; for( j = 0; j < 9; j++ ) M[j] = (float)RGB2XYZ[j]; + //0.72033 = 255/(220+134), 96.525 = 134*255/(220+134) + //0.9732 = 255/(140+122), 136.259 = 140*255/(140+122) if( depth == CV_8U ) { - u_scale = 0.720338983f; - u_bias = 96.5254237f; - v_scale = 0.973282442f; - v_bias = 136.2595419f; + u_scale = 255.f/uRange; + u_bias = -uLow*255.f/uRange; + v_scale = 255.f/vRange; + v_bias = -vLow*255.f/vRange; } + // 0.008856f = (6/29)^3 + static const float lthresh = 6.f*6.f*6.f/(29.f*29.f*29.f); + // 903.3 = (29/3)^3 + static const float yscale = 29.f*29.f*29.f/27.f; + for( j = 0; j < n*3; j += 3 ) { float r = src_row[j+2]; @@ -1189,14 +1203,14 @@ void CV_ColorLuvTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* d L = u = v = 0; else { - if( Y > 0.008856f ) - L = (float)(116.*pow((double)Y,_1_3) - 16.); + if( Y > lthresh ) + L = 116.f*cubeRoot(Y) - 16.f; else - L = 903.3f * Y; + L = yscale * Y; - d = 1.f/d; - u = 13*L*(4*X*d - un); - v = 13*L*(9*Y*d - vn); + d = 4.f*13.f/d; + u = L*(X*d - un); + v = L*(9.f/4.f*Y*d - vn); } dst_row[j] = L*Lscale; dst_row[j+1] = u*u_scale + u_bias; @@ -1209,24 +1223,34 @@ void CV_ColorLuvTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d { 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; + static const float uLow = -134.f, uHigh = 220.f, uRange = uHigh - uLow; + static const float vLow = -140.f, vHigh = 122.f, vRange = vHigh - vLow; + int j; float M[9]; - float un = 4.f*Xn/(Xn + 15.f*1.f + 3*Zn); - float vn = 9.f*1.f/(Xn + 15.f*1.f + 3*Zn); + // Yn == 1 + float dd = Xn + 15.f*1.f + 3.f*Zn; + float un = 4*13.f*Xn/dd; + float vn = 9*13.f*1.f/dd; + float u_scale = 1.f, u_bias = 0.f; float v_scale = 1.f, v_bias = 0.f; for( j = 0; j < 9; j++ ) M[j] = (float)XYZ2RGB[j]; + //0.72033 = 255/(220+134), 96.525 = 134*255/(220+134) + //0.9732 = 255/(140+122), 136.259 = 140*255/(140+122) if( depth == CV_8U ) { - u_scale = 1.f/0.720338983f; - u_bias = 96.5254237f; - v_scale = 1.f/0.973282442f; - v_bias = 136.2595419f; + u_scale = uRange/255.f; + u_bias = -uLow*255.f/uRange; + v_scale = vRange/255.f; + v_bias = -vLow*255.f/vRange; } + // (1 / 903.3) = (3/29)^3 + static const float yscale = 27.f/(29.f*29.f*29.f); for( j = 0; j < n*3; j += 3 ) { float L = src_row[j]*Lscale; @@ -1241,16 +1265,15 @@ void CV_ColorLuvTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d } else { - Y = L * (1.f/903.3f); - if( L == 0 ) - L = 0.001f; + Y = L * yscale; } - u = u/(13*L) + un; - v = v/(13*L) + vn; - - X = -9*Y*u/((u - 4)*v - u*v); - Z = (9*Y - 15*v*Y - v*X)/(3*v); + float up = 3.f*(u + L*un); + float vp = 0.25f/(v + L*vn); + if(vp > 0.25f) vp = 0.25f; + if(vp < -0.25f) vp = -0.25f; + X = Y*3.f*up*vp; + Z = Y*(((12.f*13.f)*L - up)*vp - 5.f); float r = M[0]*X + M[1]*Y + M[2]*Z; float g = M[3]*X + M[4]*Y + M[5]*Z; From f5dff87242a9a1cfb5dceda520be79234b3ebfab Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Thu, 10 Aug 2017 19:01:36 +0300 Subject: [PATCH 03/14] CV_ColorCvtBaseTest: added methods for 8u implementations --- modules/imgproc/test/test_color.cpp | 76 ++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index deeeb8da91..91faeb2c27 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -68,6 +68,14 @@ protected: // called from default implementation of convert_backward virtual void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); + // called from default implementation of convert_backward + // for cases of bit-exact functions + virtual int convert_row_abc2bgr_8u_c3( const uchar* src_row, uchar* dst_row, int n ); + + // called from default implementation of convert_forward + // for cases of bit-exact functions + virtual int convert_row_bgr2abc_8u_c3(const uchar *src_row, uchar *dst_row, int n ); + const char* fwd_code_str; const char* inv_code_str; @@ -226,19 +234,23 @@ void CV_ColorCvtBaseTest::convert_forward( const Mat& src, Mat& dst ) const uchar* src_row = src.ptr(i); uchar* dst_row = dst.ptr(i); - for( j = 0; j < cols; j++ ) + int processed = convert_row_bgr2abc_8u_c3( src_row, dst_row, cols ); + if(processed != cols) { - src_buf[j*3] = src_row[j*cn + blue_idx]*c8u; - src_buf[j*3+1] = src_row[j*cn + 1]*c8u; - src_buf[j*3+2] = src_row[j*cn + (blue_idx^2)]*c8u; - } + for( j = 0; j < cols; j++ ) + { + src_buf[j*3] = src_row[j*cn + blue_idx]*c8u; + src_buf[j*3+1] = src_row[j*cn + 1]*c8u; + src_buf[j*3+2] = src_row[j*cn + (blue_idx^2)]*c8u; + } - convert_row_bgr2abc_32f_c3( src_buf, dst_buf, cols ); + convert_row_bgr2abc_32f_c3( src_buf, dst_buf, cols ); - for( j = 0; j < dst_cols_n; j++ ) - { - int t = cvRound( dst_buf[j] ); - dst_row[j] = saturate_cast(t); + for( j = 0; j < dst_cols_n; j++ ) + { + int t = cvRound( dst_buf[j] ); + dst_row[j] = saturate_cast(t); + } } } break; @@ -297,6 +309,19 @@ void CV_ColorCvtBaseTest::convert_row_abc2bgr_32f_c3( const float* /*src_row*/, } +int CV_ColorCvtBaseTest::convert_row_abc2bgr_8u_c3(const uchar * /*src_row*/, + uchar * /*dst_row*/, int /*n*/ ) +{ + return 0; +} + + +int CV_ColorCvtBaseTest::convert_row_bgr2abc_8u_c3( const uchar* /*src_row*/, + uchar* /*dst_row*/, int /*n*/ ) +{ + return 0; +} + void CV_ColorCvtBaseTest::convert_backward( const Mat& src, const Mat& dst, Mat& dst2 ) { if( custom_inv_transform ) @@ -321,21 +346,26 @@ void CV_ColorCvtBaseTest::convert_backward( const Mat& src, const Mat& dst, Mat& const uchar* src_row = dst.ptr(i); uchar* dst_row = dst2.ptr(i); - for( j = 0; j < cols_n; j++ ) - src_buf[j] = src_row[j]; + int processed = convert_row_abc2bgr_8u_c3(src_row, dst_row, dst_cols); - convert_row_abc2bgr_32f_c3( src_buf, dst_buf, dst_cols ); - - for( j = 0; j < dst_cols; j++ ) + if(processed != dst_cols) { - int b = cvRound( dst_buf[j*3]*255. ); - int g = cvRound( dst_buf[j*3+1]*255. ); - int r = cvRound( dst_buf[j*3+2]*255. ); - dst_row[j*cn + blue_idx] = saturate_cast(b); - dst_row[j*cn + 1] = saturate_cast(g); - dst_row[j*cn + (blue_idx^2)] = saturate_cast(r); - if( cn == 4 ) - dst_row[j*cn + 3] = 255; + for( j = 0; j < cols_n; j++ ) + src_buf[j] = src_row[j]; + + convert_row_abc2bgr_32f_c3( src_buf, dst_buf, dst_cols ); + + for( j = 0; j < dst_cols; j++ ) + { + int b = cvRound( dst_buf[j*3]*255. ); + int g = cvRound( dst_buf[j*3+1]*255. ); + int r = cvRound( dst_buf[j*3+2]*255. ); + dst_row[j*cn + blue_idx] = saturate_cast(b); + dst_row[j*cn + 1] = saturate_cast(g); + dst_row[j*cn + (blue_idx^2)] = saturate_cast(r); + if( cn == 4 ) + dst_row[j*cn + 3] = 255; + } } } break; From 5ea1c72291fd40cb721e8ade9cd2552758354138 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Tue, 15 Aug 2017 18:15:13 +0300 Subject: [PATCH 04/14] Lab2RGB_b: bit-exactness enabled for all modes; non-vectorized code fixed to comply with vectorized --- modules/imgproc/src/color.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index 5a6274c9e8..801bf6bd7b 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -6771,7 +6771,10 @@ struct Lab2RGBinteger //float fxz[] = { ai / 500.0f + fy, fy - bi / 200.0f }; int adiv, bdiv; - adiv = aa*BASE/500 - 128*BASE/500, bdiv = bb*BASE/200 - 128*BASE/200; + //adiv = aa*BASE/500 - 128*BASE/500, bdiv = bb*BASE/200 - 128*BASE/200; + //approximations with reasonable precision + adiv = ((5*aa*53687 + (1 << 7)) >> 13) - 128*BASE/500; + bdiv = (( bb*41943 + (1 << 4)) >> 9) - 128*BASE/200+1; int ifxz[] = {ify + adiv, ify - bdiv}; @@ -7104,8 +7107,6 @@ struct Lab2RGB_b const float* _whitept, bool _srgb ) : fcvt(3, _blueIdx, _coeffs, _whitept, _srgb ), icvt(_dstcn, _blueIdx, _coeffs, _whitept, _srgb), dstcn(_dstcn) { - useBitExactness = (!_coeffs && !_whitept && _srgb && enableBitExactness); - #if CV_NEON v_scale_inv = vdupq_n_f32(100.f/255.f); v_scale = vdupq_n_f32(255.f); @@ -7162,7 +7163,7 @@ struct Lab2RGB_b void operator()(const uchar* src, uchar* dst, int n) const { - if(useBitExactness) + if(enableBitExactness) { icvt(src, dst, n); return; @@ -7328,7 +7329,6 @@ struct Lab2RGB_b __m128i v_zero; bool haveSIMD; #endif - bool useBitExactness; int dstcn; }; From 121fa04906c5060c249e5c9520c74c24f7c6c4b5 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Tue, 15 Aug 2017 18:21:25 +0300 Subject: [PATCH 05/14] srgb support added --- modules/imgproc/test/test_color.cpp | 86 +++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 91faeb2c27..674fd467ed 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -87,6 +87,7 @@ protected: int fwd_code, inv_code; bool test_cpp; int hue_range; + bool srgb; }; @@ -108,6 +109,7 @@ CV_ColorCvtBaseTest::CV_ColorCvtBaseTest( bool _custom_inv_transform, bool _allo test_cpp = false; hue_range = 0; blue_idx = 0; + srgb = false; inplace = false; } @@ -146,6 +148,7 @@ void CV_ColorCvtBaseTest::get_test_array_types_and_sizes( int test_case_idx, cn = (cvtest::randInt(rng) & 1) + 3; blue_idx = cvtest::randInt(rng) & 1 ? 2 : 0; + srgb = (cvtest::randInt(rng) & 1) != 0; types[INPUT][0] = CV_MAKETYPE(depth, cn); types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_MAKETYPE(depth, 3); @@ -1004,6 +1007,16 @@ void CV_ColorXYZTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d //// rgb <=> L*a*b* +static inline float applyGamma(float x) +{ + return x <= 0.04045f ? x*(1.f/12.92f) : (float)std::pow((double)(x + 0.055)*(1./1.055), 2.4); +} + +static inline float applyInvGamma(float x) +{ + return x <= 0.0031308 ? x*12.92f : (float)(1.055*std::pow((double)x, 1./2.4) - 0.055); +} + class CV_ColorLabTest : public CV_ColorCvtBaseTest { public: @@ -1026,10 +1039,20 @@ void CV_ColorLabTest::get_test_array_types_and_sizes( int test_case_idx, vector< { CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); - if( blue_idx == 0 ) - fwd_code = CV_LBGR2Lab, inv_code = CV_Lab2LBGR; + if(srgb) + { + if( blue_idx == 0 ) + fwd_code = CV_BGR2Lab, inv_code = CV_Lab2BGR; + else + fwd_code = CV_RGB2Lab, inv_code = CV_Lab2RGB; + } else - fwd_code = CV_LRGB2Lab, inv_code = CV_Lab2LRGB; + { + if( blue_idx == 0 ) + fwd_code = CV_LBGR2Lab, inv_code = CV_Lab2LBGR; + else + fwd_code = CV_LRGB2Lab, inv_code = CV_Lab2LRGB; + } } @@ -1065,6 +1088,16 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds float G = src_row[x + 1]; float B = src_row[x]; + R = std::min(std::max(R, 0.f), 1.f); + G = std::min(std::max(G, 0.f), 1.f); + B = std::min(std::max(B, 0.f), 1.f); + if (srgb) + { + R = applyGamma(R); + G = applyGamma(G); + B = applyGamma(B); + } + 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; @@ -1139,6 +1172,16 @@ void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d float G = M[3] * X + M[4] * Y + M[5] * Z; float B = M[6] * X + M[7] * Y + M[8] * Z; + R = std::min(std::max(R, 0.f), 1.f); + G = std::min(std::max(G, 0.f), 1.f); + B = std::min(std::max(B, 0.f), 1.f); + if (srgb) + { + R = applyInvGamma(R); + G = applyInvGamma(G); + B = applyInvGamma(B); + } + dst_row[x] = B; dst_row[x + 1] = G; dst_row[x + 2] = R; @@ -1169,10 +1212,20 @@ void CV_ColorLuvTest::get_test_array_types_and_sizes( int test_case_idx, vector< { CV_ColorCvtBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); - if( blue_idx == 0 ) - fwd_code = CV_LBGR2Luv, inv_code = CV_Luv2LBGR; + if(srgb) + { + if( blue_idx == 0 ) + fwd_code = CV_BGR2Luv, inv_code = CV_Luv2BGR; + else + fwd_code = CV_RGB2Luv, inv_code = CV_Luv2RGB; + } else - fwd_code = CV_LRGB2Luv, inv_code = CV_Luv2LRGB; + { + if( blue_idx == 0 ) + fwd_code = CV_LBGR2Luv, inv_code = CV_Luv2LBGR; + else + fwd_code = CV_LRGB2Luv, inv_code = CV_Luv2LRGB; + } } @@ -1224,6 +1277,16 @@ void CV_ColorLuvTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* d float g = src_row[j+1]; float b = src_row[j]; + r = std::min(std::max(r, 0.f), 1.f); + g = std::min(std::max(g, 0.f), 1.f); + b = std::min(std::max(b, 0.f), 1.f); + if( srgb ) + { + r = applyGamma(r); + g = applyGamma(g); + b = applyGamma(b); + } + float X = r*M[0] + g*M[1] + b*M[2]; float Y = r*M[3] + g*M[4] + b*M[5]; float Z = r*M[6] + g*M[7] + b*M[8]; @@ -1309,6 +1372,17 @@ void CV_ColorLuvTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d float g = M[3]*X + M[4]*Y + M[5]*Z; float b = M[6]*X + M[7]*Y + M[8]*Z; + r = std::min(std::max(r, 0.f), 1.f); + g = std::min(std::max(g, 0.f), 1.f); + b = std::min(std::max(b, 0.f), 1.f); + + if( srgb ) + { + r = applyInvGamma(r); + g = applyInvGamma(g); + b = applyInvGamma(b); + } + dst_row[j] = b; dst_row[j+1] = g; dst_row[j+2] = r; From 758f9142491615ebc45667c8197dcd27328a8078 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Tue, 15 Aug 2017 18:22:19 +0300 Subject: [PATCH 06/14] XYZ constants made softdouble --- modules/imgproc/test/test_color.cpp | 72 +++++++++++++++++++---------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 674fd467ed..4db94772bc 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -899,24 +899,42 @@ void CV_ColorHLSTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d } } - -static const double RGB2XYZ[] = +// 0.412453, 0.357580, 0.180423, +// 0.212671, 0.715160, 0.072169, +// 0.019334, 0.119193, 0.950227 +static const softdouble RGB2XYZ[] = { - 0.412453, 0.357580, 0.180423, - 0.212671, 0.715160, 0.072169, - 0.019334, 0.119193, 0.950227 + softdouble::fromRaw(0x3fda65a14488c60d), + softdouble::fromRaw(0x3fd6e297396d0918), + softdouble::fromRaw(0x3fc71819d2391d58), + softdouble::fromRaw(0x3fcb38cda6e75ff6), + softdouble::fromRaw(0x3fe6e297396d0918), + softdouble::fromRaw(0x3fb279aae6c8f755), + softdouble::fromRaw(0x3f93cc4ac6cdaf4b), + softdouble::fromRaw(0x3fbe836eb4e98138), + softdouble::fromRaw(0x3fee68427418d691) }; - -static const double XYZ2RGB[] = +// 3.240479, -1.53715, -0.498535, +// -0.969256, 1.875991, 0.041556, +// 0.055648, -0.204043, 1.057311 +static const softdouble XYZ2RGB[] = { - 3.240479, -1.53715, -0.498535, - -0.969256, 1.875991, 0.041556, - 0.055648, -0.204043, 1.057311 + softdouble::fromRaw(0x4009ec804102ff8f), + softdouble::fromRaw(0xbff8982a9930be0e), + softdouble::fromRaw(0xbfdfe7ff583a53b9), + softdouble::fromRaw(0xbfef042528ae74f3), + softdouble::fromRaw(0x3ffe040f23897204), + softdouble::fromRaw(0x3fa546d3f9e7b80b), + softdouble::fromRaw(0x3fac7de5082cf52c), + softdouble::fromRaw(0xbfca1e14bdfd2631), + softdouble::fromRaw(0x3ff0eabef06b3786) }; -static const float Xn = 0.950456f; -static const float Zn = 1.088754f; +//0.950456 +static const softdouble Xn = softdouble::fromRaw(0x3fee6a22b3892ee8); +//1.088754 +static const softdouble Zn = softdouble::fromRaw(0x3ff16b8950763a19); //// rgb <=> xyz @@ -959,12 +977,13 @@ double CV_ColorXYZTest::get_success_error_level( int /*test_case_idx*/, int i, i void CV_ColorXYZTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ) { int depth = test_mat[INPUT][0].depth(); - double scale = depth == CV_8U ? 255 : depth == CV_16U ? 65535 : 1; + softdouble scale(depth == CV_8U ? 255 : + depth == CV_16U ? 65535 : 1); double M[9]; int j; for( j = 0; j < 9; j++ ) - M[j] = RGB2XYZ[j]*scale; + M[j] = (double)(RGB2XYZ[j]*scale); for( j = 0; j < n*3; j += 3 ) { @@ -984,12 +1003,13 @@ void CV_ColorXYZTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* d void CV_ColorXYZTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) { int depth = test_mat[INPUT][0].depth(); - double scale = depth == CV_8U ? 1./255 : depth == CV_16U ? 1./65535 : 1; + softdouble scale(depth == CV_8U ? 1./255 : + depth == CV_16U ? 1./65535 : 1); double M[9]; int j; for( j = 0; j < 9; j++ ) - M[j] = XYZ2RGB[j]*scale; + M[j] = (double)(XYZ2RGB[j]*scale); for( j = 0; j < n*3; j += 3 ) { @@ -1082,6 +1102,7 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds for (int j = 0; j < 9; j++ ) M[j] = (float)RGB2XYZ[j]; + float xn = (float)Xn, zn = (float)Zn; for (int x = 0; x < n*3; x += 3) { float R = src_row[x + 2]; @@ -1098,9 +1119,9 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds B = applyGamma(B); } - float X = (R * M[0] + G * M[1] + B * M[2]) / Xn; + 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 Z = (R * M[6] + G * M[7] + B * M[8]) / zn; float fX = X > lthresh ? cubeRoot(X) : (lowScale * X + f16of116); float fY = Y > lthresh ? cubeRoot(Y) : (lowScale * Y + f16of116); @@ -1136,6 +1157,7 @@ void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d // 903.3 = (29/3)^3 static const float yscale = 29.f*29.f*29.f/27.f; + float xn = (float)Xn, zn = (float)Zn; for (int x = 0, end = n * 3; x < end; x += 3) { float L = src_row[x] * Lscale; @@ -1165,8 +1187,8 @@ void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d else FXZ[k] = FXZ[k] * FXZ[k] * FXZ[k]; } - float X = FXZ[0] * Xn; - float Z = FXZ[1] * Zn; + 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; @@ -1246,8 +1268,9 @@ void CV_ColorLuvTest::convert_row_bgr2abc_32f_c3( const float* src_row, float* d float M[9]; // Yn == 1 - float dd = Xn + 15.f*1.f + 3.f*Zn; - float un = 4.f*13.f*Xn/dd; + float xn = (float)Xn, zn = (float)Zn; + float dd = xn + 15.f*1.f + 3.f*zn; + float un = 4.f*13.f*xn/dd; float vn = 9.f*13.f/dd; float u_scale = 1.f, u_bias = 0.f; @@ -1322,8 +1345,9 @@ void CV_ColorLuvTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d int j; float M[9]; // Yn == 1 - float dd = Xn + 15.f*1.f + 3.f*Zn; - float un = 4*13.f*Xn/dd; + float xn = (float)Xn, zn = (float)Zn; + float dd = xn + 15.f*1.f + 3.f*zn; + float un = 4*13.f*xn/dd; float vn = 9*13.f*1.f/dd; float u_scale = 1.f, u_bias = 0.f; From 375ec5ed7eb2ec9e226de854d07090682cb2b371 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Tue, 15 Aug 2017 18:43:55 +0300 Subject: [PATCH 07/14] bit-exact tests written for Lab --- modules/imgproc/test/test_color.cpp | 263 +++++++++++++++++++++++++++- 1 file changed, 255 insertions(+), 8 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 4db94772bc..32d46dd79e 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -70,11 +70,11 @@ protected: // called from default implementation of convert_backward // for cases of bit-exact functions - virtual int convert_row_abc2bgr_8u_c3( const uchar* src_row, uchar* dst_row, int n ); + virtual int convert_row_abc2bgr_8u_c3( const uchar* src_row, uchar* dst_row, int n, int cn ); // called from default implementation of convert_forward // for cases of bit-exact functions - virtual int convert_row_bgr2abc_8u_c3(const uchar *src_row, uchar *dst_row, int n ); + virtual int convert_row_bgr2abc_8u_c3(const uchar *src_row, uchar *dst_row, int n, int cn); const char* fwd_code_str; const char* inv_code_str; @@ -237,7 +237,7 @@ void CV_ColorCvtBaseTest::convert_forward( const Mat& src, Mat& dst ) const uchar* src_row = src.ptr(i); uchar* dst_row = dst.ptr(i); - int processed = convert_row_bgr2abc_8u_c3( src_row, dst_row, cols ); + int processed = convert_row_bgr2abc_8u_c3( src_row, dst_row, cols, cn ); if(processed != cols) { for( j = 0; j < cols; j++ ) @@ -313,14 +313,14 @@ void CV_ColorCvtBaseTest::convert_row_abc2bgr_32f_c3( const float* /*src_row*/, int CV_ColorCvtBaseTest::convert_row_abc2bgr_8u_c3(const uchar * /*src_row*/, - uchar * /*dst_row*/, int /*n*/ ) + uchar * /*dst_row*/, int /*n*/, int /*cn*/ ) { return 0; } -int CV_ColorCvtBaseTest::convert_row_bgr2abc_8u_c3( const uchar* /*src_row*/, - uchar* /*dst_row*/, int /*n*/ ) +int CV_ColorCvtBaseTest::convert_row_bgr2abc_8u_c3(const uchar* /*src_row*/, + uchar* /*dst_row*/, int /*n*/ , int /*cn*/) { return 0; } @@ -349,7 +349,7 @@ void CV_ColorCvtBaseTest::convert_backward( const Mat& src, const Mat& dst, Mat& const uchar* src_row = dst.ptr(i); uchar* dst_row = dst2.ptr(i); - int processed = convert_row_abc2bgr_8u_c3(src_row, dst_row, dst_cols); + int processed = convert_row_abc2bgr_8u_c3(src_row, dst_row, dst_cols, cn); if(processed != dst_cols) { @@ -1027,6 +1027,57 @@ void CV_ColorXYZTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d //// rgb <=> L*a*b* + +// taken from color.cpp + +static ushort sRGBGammaTab_b[256], linearGammaTab_b[256]; +enum { inv_gamma_shift = 12, INV_GAMMA_TAB_SIZE = (1 << inv_gamma_shift) }; +static ushort sRGBInvGammaTab_b[INV_GAMMA_TAB_SIZE], linearInvGammaTab_b[INV_GAMMA_TAB_SIZE]; +#undef lab_shift +// #define lab_shift xyz_shift +#define lab_shift 12 +#define gamma_shift 3 +#define lab_shift2 (lab_shift + gamma_shift) +#define LAB_CBRT_TAB_SIZE_B (256*3/2*(1<> (n)) + +static ushort LabToYF_b[256*2]; +static const int minABvalue = -8145; +static int abToXZ_b[LAB_BASE*9/4]; + +//all constants should be presented through integers to keep bit-exactness +static const softdouble gammaThreshold = softdouble(809)/softdouble(20000); // 0.04045 +static const softdouble gammaInvThreshold = softdouble(7827)/softdouble(2500000); // 0.0031308 +static const softdouble gammaLowScale = softdouble(323)/softdouble(25); // 12.92 +static const softdouble gammaPower = softdouble(12)/softdouble(5); // 2.4 +static const softdouble gammaXshift = softdouble(11)/softdouble(200); // 0.055 + +static inline softfloat applyGamma(softfloat x) +{ + //return x <= 0.04045f ? x*(1.f/12.92f) : (float)std::pow((double)(x + 0.055)*(1./1.055), 2.4); + softdouble xd = x; + return (xd <= gammaThreshold ? + xd/gammaLowScale : + pow((xd + gammaXshift)/(softdouble::one()+gammaXshift), gammaPower)); +} + +static inline softfloat applyInvGamma(softfloat x) +{ + //return x <= 0.0031308 ? x*12.92f : (float)(1.055*std::pow((double)x, 1./2.4) - 0.055); + softdouble xd = x; + return (xd <= gammaInvThreshold ? + xd*gammaLowScale : + pow(xd, softdouble::one()/gammaPower)*(softdouble::one()+gammaXshift) - gammaXshift); +} + static inline float applyGamma(float x) { return x <= 0.04045f ? x*(1.f/12.92f) : (float)std::pow((double)(x + 0.055)*(1./1.055), 2.4); @@ -1037,6 +1088,90 @@ static inline float applyInvGamma(float x) return x <= 0.0031308 ? x*12.92f : (float)(1.055*std::pow((double)x, 1./2.4) - 0.055); } +static void initLabTabs() +{ + static bool initialized = false; + if(!initialized) + { + static const softfloat lthresh = softfloat(216) / softfloat(24389); // 0.008856f = (6/29)^3 + static const softfloat lscale = softfloat(841) / softfloat(108); // 7.787f = (29/3)^3/(29*4) + static const softfloat lbias = softfloat(16) / softfloat(116); + static const softfloat f255(255); + + static const softfloat intScale(255*(1 << gamma_shift)); + for(int i = 0; i < 256; i++) + { + softfloat x = softfloat(i)/f255; + sRGBGammaTab_b[i] = (ushort)(cvRound(intScale*applyGamma(x))); + linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift)); + } + static const softfloat invScale = softfloat::one()/softfloat((int)INV_GAMMA_TAB_SIZE); + for(int i = 0; i < INV_GAMMA_TAB_SIZE; i++) + { + softfloat x = invScale*softfloat(i); + sRGBInvGammaTab_b[i] = (ushort)(cvRound(f255*applyInvGamma(x))); + linearInvGammaTab_b[i] = (ushort)(cvTrunc(f255*x)); + } + + static const softfloat cbTabScale(softfloat::one()/(f255*(1 << gamma_shift))); + static const softfloat lshift2(1 << lab_shift2); + for(int i = 0; i < LAB_CBRT_TAB_SIZE_B; i++) + { + softfloat x = cbTabScale*softfloat(i); + LabCbrtTab_b[i] = (ushort)(cvRound(lshift2 * (x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x)))); + } + + //Lookup table for L to y and ify calculations + static const int BASE = (1 << 14); + for(int i = 0; i < 256; i++) + { + int y, ify; + //8 * 255.0 / 100.0 == 20.4 + if( i <= 20) + { + //yy = li / 903.3f; + //y = L*100/903.3f; 903.3f = (29/3)^3, 255 = 17*3*5 + y = cvRound(softfloat(i*BASE*20*9)/softfloat(17*29*29*29)); + //fy = 7.787f * yy + 16.0f / 116.0f; 7.787f = (29/3)^3/(29*4) + ify = cvRound(softfloat(BASE)*(softfloat(16)/softfloat(116) + softfloat(i*5)/softfloat(3*17*29))); + } + else + { + //fy = (li + 16.0f) / 116.0f; + softfloat fy = (softfloat(i*100*BASE)/softfloat(255*116) + + softfloat(16*BASE)/softfloat(116)); + ify = cvRound(fy); + //yy = fy * fy * fy; + y = cvRound(fy*fy*fy/softfloat(BASE*BASE)); + } + + LabToYF_b[i*2 ] = (ushort)y; // 2260 <= y <= BASE + LabToYF_b[i*2+1] = (ushort)ify; // 0 <= ify <= BASE + } + + //Lookup table for a,b to x,z conversion + for(int i = minABvalue; i < LAB_BASE*9/4+minABvalue; i++) + { + int v; + //6.f/29.f*BASE = 3389.730 + if(i <= 3390) + { + //fxz[k] = (fxz[k] - 16.0f / 116.0f) / 7.787f; + // 7.787f = (29/3)^3/(29*4) + v = i*108/841 - BASE*16/116*108/841; + } + else + { + //fxz[k] = fxz[k] * fxz[k] * fxz[k]; + v = i*i/BASE*i/BASE; + } + abToXZ_b[i-minABvalue] = v; // -1335 <= v <= 88231 + } + + initialized = true; + } +} + class CV_ColorLabTest : public CV_ColorCvtBaseTest { public: @@ -1046,11 +1181,14 @@ protected: double get_success_error_level( int test_case_idx, int i, int j ); void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); + int convert_row_bgr2abc_8u_c3( const uchar *src_row, uchar *dst_row, int n, int cn ); + int convert_row_abc2bgr_8u_c3( const uchar *src_row, uchar *dst_row, int n, int cn ); }; CV_ColorLabTest::CV_ColorLabTest() : CV_ColorCvtBaseTest( true, true, false ) { + initLabTabs(); INIT_FWD_INV_CODES( BGR2Lab, Lab2BGR ); } @@ -1079,7 +1217,50 @@ void CV_ColorLabTest::get_test_array_types_and_sizes( int test_case_idx, vector< double CV_ColorLabTest::get_success_error_level( int /*test_case_idx*/, int i, int j ) { int depth = test_mat[i][j].depth(); - return depth == CV_8U ? 16 : depth == CV_16U ? 32 : 1e-3; + // j == 0 is for forward code, j == 1 is for inverse code + return (depth == CV_8U) ? 0 : + //16u is forbidden + //(depth == CV_16U) ? 32 : + srgb ? ((j == 0) ? 0.4 : 0.0055) : 1e-3; +} + + +int CV_ColorLabTest::convert_row_bgr2abc_8u_c3(const uchar* src_row, uchar *dst_row, int n, int cn) +{ + int coeffs[9]; + softdouble whitept[3] = {Xn, softdouble::one(), Zn}; + + static const softdouble lshift(1 << lab_shift); + for(int i = 0; i < 3; i++) + { + coeffs[i*3 + (blue_idx^2)] = cvRound(lshift*RGB2XYZ[i*3 ]/whitept[i]); + coeffs[i*3 + 1 ] = cvRound(lshift*RGB2XYZ[i*3+1]/whitept[i]); + coeffs[i*3 + (blue_idx )] = cvRound(lshift*RGB2XYZ[i*3+2]/whitept[i]); + } + + const int Lscale = (116*255+50)/100; + const int Lshift = -((16*255*(1 << lab_shift2) + 50)/100); + const ushort* tab = srgb ? sRGBGammaTab_b : linearGammaTab_b; + for (int x = 0; x < n; x++) + { + int R = src_row[x*cn + 0], + G = src_row[x*cn + 1], + B = src_row[x*cn + 2]; + R = tab[R], G = tab[G], B = tab[B]; + int fX = LabCbrtTab_b[CV_DESCALE(R*coeffs[0] + G*coeffs[1] + B*coeffs[2], lab_shift)]; + int fY = LabCbrtTab_b[CV_DESCALE(R*coeffs[3] + G*coeffs[4] + B*coeffs[5], lab_shift)]; + int fZ = LabCbrtTab_b[CV_DESCALE(R*coeffs[6] + G*coeffs[7] + B*coeffs[8], lab_shift)]; + + int L = CV_DESCALE( Lscale*fY + Lshift, lab_shift2 ); + int a = CV_DESCALE( 500*(fX - fY) + 128*(1 << lab_shift2), lab_shift2 ); + int b = CV_DESCALE( 200*(fY - fZ) + 128*(1 << lab_shift2), lab_shift2 ); + + dst_row[x*3 ] = saturate_cast(L); + dst_row[x*3 + 1] = saturate_cast(a); + dst_row[x*3 + 2] = saturate_cast(b); + } + + return n; } @@ -1137,6 +1318,71 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds } } +int CV_ColorLabTest::convert_row_abc2bgr_8u_c3(const uchar* src_row, uchar *dst_row, int n, int cn) +{ + static const int base_shift = 14; + static const int BASE = (1 << base_shift); + static const int shift = lab_shift+(base_shift-inv_gamma_shift); + + int coeffs[9]; + softdouble whitept[3] = {Xn, softdouble::one(), Zn}; + + static const softdouble lshift(1 << lab_shift); + for(int i = 0; i < 3; i++) + { + coeffs[i+(blue_idx )*3] = cvRound(lshift*XYZ2RGB[i ]*whitept[i]); + coeffs[i+ 1*3] = cvRound(lshift*XYZ2RGB[i+3]*whitept[i]); + coeffs[i+(blue_idx^2)*3] = cvRound(lshift*XYZ2RGB[i+6]*whitept[i]); + } + ushort* tab = srgb ? sRGBInvGammaTab_b : linearInvGammaTab_b; + + for(int x = 0; x < n; x++) + { + uchar LL = src_row[x*3 ]; + uchar aa = src_row[x*3 + 1]; + uchar bb = src_row[x*3 + 2]; + + int ro, go, bo, xx, yy, zz, ify; + + yy = LabToYF_b[LL*2 ]; + ify = LabToYF_b[LL*2+1]; + + int adiv, bdiv; + //adiv = aa*BASE/500 - 128*BASE/500, bdiv = bb*BASE/200 - 128*BASE/200; + //approximations with reasonable precision + adiv = ((5*aa*53687 + (1 << 7)) >> 13) - 128*BASE/500; + bdiv = (( bb*41943 + (1 << 4)) >> 9) - 128*BASE/200+1; + + int ifxz[] = {ify + adiv, ify - bdiv}; + + for(int k = 0; k < 2; k++) + { + int& v = ifxz[k]; + v = abToXZ_b[v-minABvalue]; + } + xx = ifxz[0]; /* yy = yy */; zz = ifxz[1]; + + ro = CV_DESCALE(coeffs[0]*xx + coeffs[1]*yy + coeffs[2]*zz, shift); + go = CV_DESCALE(coeffs[3]*xx + coeffs[4]*yy + coeffs[5]*zz, shift); + bo = CV_DESCALE(coeffs[6]*xx + coeffs[7]*yy + coeffs[8]*zz, shift); + + ro = max(0, min((int)INV_GAMMA_TAB_SIZE-1, ro)); + go = max(0, min((int)INV_GAMMA_TAB_SIZE-1, go)); + bo = max(0, min((int)INV_GAMMA_TAB_SIZE-1, bo)); + + ro = tab[ro]; + go = tab[go]; + bo = tab[bo]; + + dst_row[x*cn ] = saturate_cast(bo); + dst_row[x*cn + 1] = saturate_cast(go); + dst_row[x*cn + 2] = saturate_cast(ro); + if(cn == 4) dst_row[x*cn + 3] = 255; + } + + return n; +} + void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) { int depth = test_mat[INPUT][0].depth(); @@ -1226,6 +1472,7 @@ protected: CV_ColorLuvTest::CV_ColorLuvTest() : CV_ColorCvtBaseTest( true, true, false ) { + initLabTabs(); INIT_FWD_INV_CODES( BGR2Luv, Luv2BGR ); } From 62ee92e3e2acb1beb7ff0921d76e9d44b078325a Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Tue, 15 Aug 2017 18:44:59 +0300 Subject: [PATCH 08/14] ColorLab_full test fixed --- modules/imgproc/test/test_color.cpp | 28 ++++++++++++++------------- modules/imgproc/test/test_precomp.hpp | 1 + 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 32d46dd79e..34d30c5f96 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -2297,7 +2297,7 @@ static void validateResult(const Mat& reference, const Mat& actual, const Mat& s ssize.width *= cn; bool next = true; //RGB2Lab_f works throug LUT and brings additional error - static const float maxErr = 1.f/200.f; + static const float maxErr = 1.f/192.f; for (int y = 0; y < ssize.height && next; ++y) { @@ -2335,20 +2335,22 @@ TEST(Imgproc_ColorLab_Full, accuracy) 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; + for(int i = 0; i < 4; i++) + { + int blueInd = (i%2) > 0 ? 0 : 2; + bool srgb = i > 1; - // 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); + // 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); - validateResult(src, recons, src, forward_code); + validateResult(src, recons, src, forward_code); + } } static void test_Bayer2RGB_EdgeAware_8u(const Mat& src, Mat& dst, int code) diff --git a/modules/imgproc/test/test_precomp.hpp b/modules/imgproc/test/test_precomp.hpp index 249ec8d629..ed8fbbbe87 100644 --- a/modules/imgproc/test/test_precomp.hpp +++ b/modules/imgproc/test/test_precomp.hpp @@ -14,6 +14,7 @@ #include "opencv2/core/private.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" +#include "opencv2/core/softfloat.hpp" #include "opencv2/imgproc/imgproc_c.h" From d25344c2572d104f42cfe55b4b23fd22c41b3032 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Thu, 17 Aug 2017 02:13:11 +0300 Subject: [PATCH 09/14] reverted: no 8u convertors for CV_ColorCvtBaseTest --- modules/imgproc/test/test_color.cpp | 295 +++------------------------- 1 file changed, 26 insertions(+), 269 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 34d30c5f96..f18853ae6d 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -68,14 +68,6 @@ protected: // called from default implementation of convert_backward virtual void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); - // called from default implementation of convert_backward - // for cases of bit-exact functions - virtual int convert_row_abc2bgr_8u_c3( const uchar* src_row, uchar* dst_row, int n, int cn ); - - // called from default implementation of convert_forward - // for cases of bit-exact functions - virtual int convert_row_bgr2abc_8u_c3(const uchar *src_row, uchar *dst_row, int n, int cn); - const char* fwd_code_str; const char* inv_code_str; @@ -237,23 +229,19 @@ void CV_ColorCvtBaseTest::convert_forward( const Mat& src, Mat& dst ) const uchar* src_row = src.ptr(i); uchar* dst_row = dst.ptr(i); - int processed = convert_row_bgr2abc_8u_c3( src_row, dst_row, cols, cn ); - if(processed != cols) + for( j = 0; j < cols; j++ ) { - for( j = 0; j < cols; j++ ) - { - src_buf[j*3] = src_row[j*cn + blue_idx]*c8u; - src_buf[j*3+1] = src_row[j*cn + 1]*c8u; - src_buf[j*3+2] = src_row[j*cn + (blue_idx^2)]*c8u; - } + src_buf[j*3] = src_row[j*cn + blue_idx]*c8u; + src_buf[j*3+1] = src_row[j*cn + 1]*c8u; + src_buf[j*3+2] = src_row[j*cn + (blue_idx^2)]*c8u; + } - convert_row_bgr2abc_32f_c3( src_buf, dst_buf, cols ); + convert_row_bgr2abc_32f_c3( src_buf, dst_buf, cols ); - for( j = 0; j < dst_cols_n; j++ ) - { - int t = cvRound( dst_buf[j] ); - dst_row[j] = saturate_cast(t); - } + for( j = 0; j < dst_cols_n; j++ ) + { + int t = cvRound( dst_buf[j] ); + dst_row[j] = saturate_cast(t); } } break; @@ -312,19 +300,6 @@ void CV_ColorCvtBaseTest::convert_row_abc2bgr_32f_c3( const float* /*src_row*/, } -int CV_ColorCvtBaseTest::convert_row_abc2bgr_8u_c3(const uchar * /*src_row*/, - uchar * /*dst_row*/, int /*n*/, int /*cn*/ ) -{ - return 0; -} - - -int CV_ColorCvtBaseTest::convert_row_bgr2abc_8u_c3(const uchar* /*src_row*/, - uchar* /*dst_row*/, int /*n*/ , int /*cn*/) -{ - return 0; -} - void CV_ColorCvtBaseTest::convert_backward( const Mat& src, const Mat& dst, Mat& dst2 ) { if( custom_inv_transform ) @@ -349,26 +324,21 @@ void CV_ColorCvtBaseTest::convert_backward( const Mat& src, const Mat& dst, Mat& const uchar* src_row = dst.ptr(i); uchar* dst_row = dst2.ptr(i); - int processed = convert_row_abc2bgr_8u_c3(src_row, dst_row, dst_cols, cn); + for( j = 0; j < cols_n; j++ ) + src_buf[j] = src_row[j]; - if(processed != dst_cols) + convert_row_abc2bgr_32f_c3( src_buf, dst_buf, dst_cols ); + + for( j = 0; j < dst_cols; j++ ) { - for( j = 0; j < cols_n; j++ ) - src_buf[j] = src_row[j]; - - convert_row_abc2bgr_32f_c3( src_buf, dst_buf, dst_cols ); - - for( j = 0; j < dst_cols; j++ ) - { - int b = cvRound( dst_buf[j*3]*255. ); - int g = cvRound( dst_buf[j*3+1]*255. ); - int r = cvRound( dst_buf[j*3+2]*255. ); - dst_row[j*cn + blue_idx] = saturate_cast(b); - dst_row[j*cn + 1] = saturate_cast(g); - dst_row[j*cn + (blue_idx^2)] = saturate_cast(r); - if( cn == 4 ) - dst_row[j*cn + 3] = 255; - } + int b = cvRound( dst_buf[j*3]*255. ); + int g = cvRound( dst_buf[j*3+1]*255. ); + int r = cvRound( dst_buf[j*3+2]*255. ); + dst_row[j*cn + blue_idx] = saturate_cast(b); + dst_row[j*cn + 1] = saturate_cast(g); + dst_row[j*cn + (blue_idx^2)] = saturate_cast(r); + if( cn == 4 ) + dst_row[j*cn + 3] = 255; } } break; @@ -1028,30 +998,7 @@ void CV_ColorXYZTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d //// rgb <=> L*a*b* -// taken from color.cpp - -static ushort sRGBGammaTab_b[256], linearGammaTab_b[256]; -enum { inv_gamma_shift = 12, INV_GAMMA_TAB_SIZE = (1 << inv_gamma_shift) }; -static ushort sRGBInvGammaTab_b[INV_GAMMA_TAB_SIZE], linearInvGammaTab_b[INV_GAMMA_TAB_SIZE]; -#undef lab_shift -// #define lab_shift xyz_shift -#define lab_shift 12 -#define gamma_shift 3 -#define lab_shift2 (lab_shift + gamma_shift) -#define LAB_CBRT_TAB_SIZE_B (256*3/2*(1<> (n)) - -static ushort LabToYF_b[256*2]; -static const int minABvalue = -8145; -static int abToXZ_b[LAB_BASE*9/4]; +//taken from color.cpp //all constants should be presented through integers to keep bit-exactness static const softdouble gammaThreshold = softdouble(809)/softdouble(20000); // 0.04045 @@ -1088,90 +1035,6 @@ static inline float applyInvGamma(float x) return x <= 0.0031308 ? x*12.92f : (float)(1.055*std::pow((double)x, 1./2.4) - 0.055); } -static void initLabTabs() -{ - static bool initialized = false; - if(!initialized) - { - static const softfloat lthresh = softfloat(216) / softfloat(24389); // 0.008856f = (6/29)^3 - static const softfloat lscale = softfloat(841) / softfloat(108); // 7.787f = (29/3)^3/(29*4) - static const softfloat lbias = softfloat(16) / softfloat(116); - static const softfloat f255(255); - - static const softfloat intScale(255*(1 << gamma_shift)); - for(int i = 0; i < 256; i++) - { - softfloat x = softfloat(i)/f255; - sRGBGammaTab_b[i] = (ushort)(cvRound(intScale*applyGamma(x))); - linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift)); - } - static const softfloat invScale = softfloat::one()/softfloat((int)INV_GAMMA_TAB_SIZE); - for(int i = 0; i < INV_GAMMA_TAB_SIZE; i++) - { - softfloat x = invScale*softfloat(i); - sRGBInvGammaTab_b[i] = (ushort)(cvRound(f255*applyInvGamma(x))); - linearInvGammaTab_b[i] = (ushort)(cvTrunc(f255*x)); - } - - static const softfloat cbTabScale(softfloat::one()/(f255*(1 << gamma_shift))); - static const softfloat lshift2(1 << lab_shift2); - for(int i = 0; i < LAB_CBRT_TAB_SIZE_B; i++) - { - softfloat x = cbTabScale*softfloat(i); - LabCbrtTab_b[i] = (ushort)(cvRound(lshift2 * (x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x)))); - } - - //Lookup table for L to y and ify calculations - static const int BASE = (1 << 14); - for(int i = 0; i < 256; i++) - { - int y, ify; - //8 * 255.0 / 100.0 == 20.4 - if( i <= 20) - { - //yy = li / 903.3f; - //y = L*100/903.3f; 903.3f = (29/3)^3, 255 = 17*3*5 - y = cvRound(softfloat(i*BASE*20*9)/softfloat(17*29*29*29)); - //fy = 7.787f * yy + 16.0f / 116.0f; 7.787f = (29/3)^3/(29*4) - ify = cvRound(softfloat(BASE)*(softfloat(16)/softfloat(116) + softfloat(i*5)/softfloat(3*17*29))); - } - else - { - //fy = (li + 16.0f) / 116.0f; - softfloat fy = (softfloat(i*100*BASE)/softfloat(255*116) + - softfloat(16*BASE)/softfloat(116)); - ify = cvRound(fy); - //yy = fy * fy * fy; - y = cvRound(fy*fy*fy/softfloat(BASE*BASE)); - } - - LabToYF_b[i*2 ] = (ushort)y; // 2260 <= y <= BASE - LabToYF_b[i*2+1] = (ushort)ify; // 0 <= ify <= BASE - } - - //Lookup table for a,b to x,z conversion - for(int i = minABvalue; i < LAB_BASE*9/4+minABvalue; i++) - { - int v; - //6.f/29.f*BASE = 3389.730 - if(i <= 3390) - { - //fxz[k] = (fxz[k] - 16.0f / 116.0f) / 7.787f; - // 7.787f = (29/3)^3/(29*4) - v = i*108/841 - BASE*16/116*108/841; - } - else - { - //fxz[k] = fxz[k] * fxz[k] * fxz[k]; - v = i*i/BASE*i/BASE; - } - abToXZ_b[i-minABvalue] = v; // -1335 <= v <= 88231 - } - - initialized = true; - } -} - class CV_ColorLabTest : public CV_ColorCvtBaseTest { public: @@ -1188,7 +1051,6 @@ protected: CV_ColorLabTest::CV_ColorLabTest() : CV_ColorCvtBaseTest( true, true, false ) { - initLabTabs(); INIT_FWD_INV_CODES( BGR2Lab, Lab2BGR ); } @@ -1218,52 +1080,12 @@ double CV_ColorLabTest::get_success_error_level( int /*test_case_idx*/, int i, i { int depth = test_mat[i][j].depth(); // j == 0 is for forward code, j == 1 is for inverse code - return (depth == CV_8U) ? 0 : - //16u is forbidden - //(depth == CV_16U) ? 32 : + return (depth == CV_8U) ? (srgb ? 32 : 8) : + //(depth == CV_16U) ? 32 : // 16u is disabled srgb ? ((j == 0) ? 0.4 : 0.0055) : 1e-3; } -int CV_ColorLabTest::convert_row_bgr2abc_8u_c3(const uchar* src_row, uchar *dst_row, int n, int cn) -{ - int coeffs[9]; - softdouble whitept[3] = {Xn, softdouble::one(), Zn}; - - static const softdouble lshift(1 << lab_shift); - for(int i = 0; i < 3; i++) - { - coeffs[i*3 + (blue_idx^2)] = cvRound(lshift*RGB2XYZ[i*3 ]/whitept[i]); - coeffs[i*3 + 1 ] = cvRound(lshift*RGB2XYZ[i*3+1]/whitept[i]); - coeffs[i*3 + (blue_idx )] = cvRound(lshift*RGB2XYZ[i*3+2]/whitept[i]); - } - - const int Lscale = (116*255+50)/100; - const int Lshift = -((16*255*(1 << lab_shift2) + 50)/100); - const ushort* tab = srgb ? sRGBGammaTab_b : linearGammaTab_b; - for (int x = 0; x < n; x++) - { - int R = src_row[x*cn + 0], - G = src_row[x*cn + 1], - B = src_row[x*cn + 2]; - R = tab[R], G = tab[G], B = tab[B]; - int fX = LabCbrtTab_b[CV_DESCALE(R*coeffs[0] + G*coeffs[1] + B*coeffs[2], lab_shift)]; - int fY = LabCbrtTab_b[CV_DESCALE(R*coeffs[3] + G*coeffs[4] + B*coeffs[5], lab_shift)]; - int fZ = LabCbrtTab_b[CV_DESCALE(R*coeffs[6] + G*coeffs[7] + B*coeffs[8], lab_shift)]; - - int L = CV_DESCALE( Lscale*fY + Lshift, lab_shift2 ); - int a = CV_DESCALE( 500*(fX - fY) + 128*(1 << lab_shift2), lab_shift2 ); - int b = CV_DESCALE( 200*(fY - fZ) + 128*(1 << lab_shift2), lab_shift2 ); - - dst_row[x*3 ] = saturate_cast(L); - dst_row[x*3 + 1] = saturate_cast(a); - dst_row[x*3 + 2] = saturate_cast(b); - } - - return 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(); @@ -1318,70 +1140,6 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds } } -int CV_ColorLabTest::convert_row_abc2bgr_8u_c3(const uchar* src_row, uchar *dst_row, int n, int cn) -{ - static const int base_shift = 14; - static const int BASE = (1 << base_shift); - static const int shift = lab_shift+(base_shift-inv_gamma_shift); - - int coeffs[9]; - softdouble whitept[3] = {Xn, softdouble::one(), Zn}; - - static const softdouble lshift(1 << lab_shift); - for(int i = 0; i < 3; i++) - { - coeffs[i+(blue_idx )*3] = cvRound(lshift*XYZ2RGB[i ]*whitept[i]); - coeffs[i+ 1*3] = cvRound(lshift*XYZ2RGB[i+3]*whitept[i]); - coeffs[i+(blue_idx^2)*3] = cvRound(lshift*XYZ2RGB[i+6]*whitept[i]); - } - ushort* tab = srgb ? sRGBInvGammaTab_b : linearInvGammaTab_b; - - for(int x = 0; x < n; x++) - { - uchar LL = src_row[x*3 ]; - uchar aa = src_row[x*3 + 1]; - uchar bb = src_row[x*3 + 2]; - - int ro, go, bo, xx, yy, zz, ify; - - yy = LabToYF_b[LL*2 ]; - ify = LabToYF_b[LL*2+1]; - - int adiv, bdiv; - //adiv = aa*BASE/500 - 128*BASE/500, bdiv = bb*BASE/200 - 128*BASE/200; - //approximations with reasonable precision - adiv = ((5*aa*53687 + (1 << 7)) >> 13) - 128*BASE/500; - bdiv = (( bb*41943 + (1 << 4)) >> 9) - 128*BASE/200+1; - - int ifxz[] = {ify + adiv, ify - bdiv}; - - for(int k = 0; k < 2; k++) - { - int& v = ifxz[k]; - v = abToXZ_b[v-minABvalue]; - } - xx = ifxz[0]; /* yy = yy */; zz = ifxz[1]; - - ro = CV_DESCALE(coeffs[0]*xx + coeffs[1]*yy + coeffs[2]*zz, shift); - go = CV_DESCALE(coeffs[3]*xx + coeffs[4]*yy + coeffs[5]*zz, shift); - bo = CV_DESCALE(coeffs[6]*xx + coeffs[7]*yy + coeffs[8]*zz, shift); - - ro = max(0, min((int)INV_GAMMA_TAB_SIZE-1, ro)); - go = max(0, min((int)INV_GAMMA_TAB_SIZE-1, go)); - bo = max(0, min((int)INV_GAMMA_TAB_SIZE-1, bo)); - - ro = tab[ro]; - go = tab[go]; - bo = tab[bo]; - - dst_row[x*cn ] = saturate_cast(bo); - dst_row[x*cn + 1] = saturate_cast(go); - dst_row[x*cn + 2] = saturate_cast(ro); - if(cn == 4) dst_row[x*cn + 3] = 255; - } - - return n; -} void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ) { @@ -1472,7 +1230,6 @@ protected: CV_ColorLuvTest::CV_ColorLuvTest() : CV_ColorCvtBaseTest( true, true, false ) { - initLabTabs(); INIT_FWD_INV_CODES( BGR2Luv, Luv2BGR ); } From f1e8aa70a01505939a6ded0fc75d5503bf146806 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Thu, 17 Aug 2017 02:14:42 +0300 Subject: [PATCH 10/14] added checksum-based test for Lab bit-exactness --- modules/imgproc/test/test_color.cpp | 320 ++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index f18853ae6d..4677b01684 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -2110,6 +2110,326 @@ TEST(Imgproc_ColorLab_Full, accuracy) } } + +static uint32_t adler32(Mat m) +{ + uint32_t s1 = 1, s2 = 0; + for(int y = 0; y < m.rows; y++) + { + uchar* py = m.ptr(y); + for(size_t x = 0; x < m.cols*m.elemSize(); x++) + { + s1 = (s1 + py[x]) % 65521; + s2 = (s1 + s2 ) % 65521; + } + } + return (s2 << 16) + s1; +} + + +// taken from color.cpp + +static ushort sRGBGammaTab_b[256], linearGammaTab_b[256]; +enum { inv_gamma_shift = 12, INV_GAMMA_TAB_SIZE = (1 << inv_gamma_shift) }; +static ushort sRGBInvGammaTab_b[INV_GAMMA_TAB_SIZE], linearInvGammaTab_b[INV_GAMMA_TAB_SIZE]; +#undef lab_shift +// #define lab_shift xyz_shift +#define lab_shift 12 +#define gamma_shift 3 +#define lab_shift2 (lab_shift + gamma_shift) +#define LAB_CBRT_TAB_SIZE_B (256*3/2*(1<> (n)) + +static ushort LabToYF_b[256*2]; +static const int minABvalue = -8145; +static int abToXZ_b[LAB_BASE*9/4]; + +static void initLabTabs() +{ + static bool initialized = false; + if(!initialized) + { + static const softfloat lthresh = softfloat(216) / softfloat(24389); // 0.008856f = (6/29)^3 + static const softfloat lscale = softfloat(841) / softfloat(108); // 7.787f = (29/3)^3/(29*4) + static const softfloat lbias = softfloat(16) / softfloat(116); + static const softfloat f255(255); + + static const softfloat intScale(255*(1 << gamma_shift)); + for(int i = 0; i < 256; i++) + { + softfloat x = softfloat(i)/f255; + sRGBGammaTab_b[i] = (ushort)(cvRound(intScale*applyGamma(x))); + linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift)); + } + static const softfloat invScale = softfloat::one()/softfloat((int)INV_GAMMA_TAB_SIZE); + for(int i = 0; i < INV_GAMMA_TAB_SIZE; i++) + { + softfloat x = invScale*softfloat(i); + sRGBInvGammaTab_b[i] = (ushort)(cvRound(f255*applyInvGamma(x))); + linearInvGammaTab_b[i] = (ushort)(cvTrunc(f255*x)); + } + + static const softfloat cbTabScale(softfloat::one()/(f255*(1 << gamma_shift))); + static const softfloat lshift2(1 << lab_shift2); + for(int i = 0; i < LAB_CBRT_TAB_SIZE_B; i++) + { + softfloat x = cbTabScale*softfloat(i); + LabCbrtTab_b[i] = (ushort)(cvRound(lshift2 * (x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x)))); + } + + //Lookup table for L to y and ify calculations + static const int BASE = (1 << 14); + for(int i = 0; i < 256; i++) + { + int y, ify; + //8 * 255.0 / 100.0 == 20.4 + if( i <= 20) + { + //yy = li / 903.3f; + //y = L*100/903.3f; 903.3f = (29/3)^3, 255 = 17*3*5 + y = cvRound(softfloat(i*BASE*20*9)/softfloat(17*29*29*29)); + //fy = 7.787f * yy + 16.0f / 116.0f; 7.787f = (29/3)^3/(29*4) + ify = cvRound(softfloat(BASE)*(softfloat(16)/softfloat(116) + softfloat(i*5)/softfloat(3*17*29))); + } + else + { + //fy = (li + 16.0f) / 116.0f; + softfloat fy = (softfloat(i*100*BASE)/softfloat(255*116) + + softfloat(16*BASE)/softfloat(116)); + ify = cvRound(fy); + //yy = fy * fy * fy; + y = cvRound(fy*fy*fy/softfloat(BASE*BASE)); + } + + LabToYF_b[i*2 ] = (ushort)y; // 2260 <= y <= BASE + LabToYF_b[i*2+1] = (ushort)ify; // 0 <= ify <= BASE + } + + //Lookup table for a,b to x,z conversion + for(int i = minABvalue; i < LAB_BASE*9/4+minABvalue; i++) + { + int v; + //6.f/29.f*BASE = 3389.730 + if(i <= 3390) + { + //fxz[k] = (fxz[k] - 16.0f / 116.0f) / 7.787f; + // 7.787f = (29/3)^3/(29*4) + v = i*108/841 - BASE*16/116*108/841; + } + else + { + //fxz[k] = fxz[k] * fxz[k] * fxz[k]; + v = i*i/BASE*i/BASE; + } + abToXZ_b[i-minABvalue] = v; // -1335 <= v <= 88231 + } + + initialized = true; + } +} + +static int row8uRGB2Lab(const uchar* src_row, uchar *dst_row, int n, int cn, int blue_idx, bool srgb) +{ + int coeffs[9]; + softdouble whitept[3] = {Xn, softdouble::one(), Zn}; + + static const softdouble lshift(1 << lab_shift); + for(int i = 0; i < 3; i++) + { + coeffs[i*3 + (blue_idx^2)] = cvRound(lshift*RGB2XYZ[i*3 ]/whitept[i]); + coeffs[i*3 + 1 ] = cvRound(lshift*RGB2XYZ[i*3+1]/whitept[i]); + coeffs[i*3 + (blue_idx )] = cvRound(lshift*RGB2XYZ[i*3+2]/whitept[i]); + } + + const int Lscale = (116*255+50)/100; + const int Lshift = -((16*255*(1 << lab_shift2) + 50)/100); + const ushort* tab = srgb ? sRGBGammaTab_b : linearGammaTab_b; + for (int x = 0; x < n; x++) + { + int R = src_row[x*cn + 0], + G = src_row[x*cn + 1], + B = src_row[x*cn + 2]; + R = tab[R], G = tab[G], B = tab[B]; + int fX = LabCbrtTab_b[CV_DESCALE(R*coeffs[0] + G*coeffs[1] + B*coeffs[2], lab_shift)]; + int fY = LabCbrtTab_b[CV_DESCALE(R*coeffs[3] + G*coeffs[4] + B*coeffs[5], lab_shift)]; + int fZ = LabCbrtTab_b[CV_DESCALE(R*coeffs[6] + G*coeffs[7] + B*coeffs[8], lab_shift)]; + + int L = CV_DESCALE( Lscale*fY + Lshift, lab_shift2 ); + int a = CV_DESCALE( 500*(fX - fY) + 128*(1 << lab_shift2), lab_shift2 ); + int b = CV_DESCALE( 200*(fY - fZ) + 128*(1 << lab_shift2), lab_shift2 ); + + dst_row[x*3 ] = saturate_cast(L); + dst_row[x*3 + 1] = saturate_cast(a); + dst_row[x*3 + 2] = saturate_cast(b); + } + + return n; +} + + +int row8uLab2RGB(const uchar* src_row, uchar *dst_row, int n, int cn, int blue_idx, bool srgb) +{ + static const int base_shift = 14; + static const int BASE = (1 << base_shift); + static const int shift = lab_shift+(base_shift-inv_gamma_shift); + + int coeffs[9]; + softdouble whitept[3] = {Xn, softdouble::one(), Zn}; + + static const softdouble lshift(1 << lab_shift); + for(int i = 0; i < 3; i++) + { + coeffs[i+(blue_idx )*3] = cvRound(lshift*XYZ2RGB[i ]*whitept[i]); + coeffs[i+ 1*3] = cvRound(lshift*XYZ2RGB[i+3]*whitept[i]); + coeffs[i+(blue_idx^2)*3] = cvRound(lshift*XYZ2RGB[i+6]*whitept[i]); + } + ushort* tab = srgb ? sRGBInvGammaTab_b : linearInvGammaTab_b; + + for(int x = 0; x < n; x++) + { + uchar LL = src_row[x*3 ]; + uchar aa = src_row[x*3 + 1]; + uchar bb = src_row[x*3 + 2]; + + int ro, go, bo, xx, yy, zz, ify; + + yy = LabToYF_b[LL*2 ]; + ify = LabToYF_b[LL*2+1]; + + int adiv, bdiv; + //adiv = aa*BASE/500 - 128*BASE/500, bdiv = bb*BASE/200 - 128*BASE/200; + //approximations with reasonable precision + adiv = ((5*aa*53687 + (1 << 7)) >> 13) - 128*BASE/500; + bdiv = (( bb*41943 + (1 << 4)) >> 9) - 128*BASE/200+1; + + int ifxz[] = {ify + adiv, ify - bdiv}; + + for(int k = 0; k < 2; k++) + { + int& v = ifxz[k]; + v = abToXZ_b[v-minABvalue]; + } + xx = ifxz[0]; /* yy = yy */; zz = ifxz[1]; + + ro = CV_DESCALE(coeffs[0]*xx + coeffs[1]*yy + coeffs[2]*zz, shift); + go = CV_DESCALE(coeffs[3]*xx + coeffs[4]*yy + coeffs[5]*zz, shift); + bo = CV_DESCALE(coeffs[6]*xx + coeffs[7]*yy + coeffs[8]*zz, shift); + + ro = max(0, min((int)INV_GAMMA_TAB_SIZE-1, ro)); + go = max(0, min((int)INV_GAMMA_TAB_SIZE-1, go)); + bo = max(0, min((int)INV_GAMMA_TAB_SIZE-1, bo)); + + ro = tab[ro]; + go = tab[go]; + bo = tab[bo]; + + dst_row[x*cn ] = saturate_cast(bo); + dst_row[x*cn + 1] = saturate_cast(go); + dst_row[x*cn + 2] = saturate_cast(ro); + if(cn == 4) dst_row[x*cn + 3] = 255; + } + + return n; +} + +int row8uLabChoose(const uchar* src_row, uchar *dst_row, int n, bool forward, int blue_idx, bool srgb) +{ + if(forward) + return row8uRGB2Lab(src_row, dst_row, n, 3, blue_idx, srgb); + else + return row8uLab2RGB(src_row, dst_row, n, 3, blue_idx, srgb); +} + + +TEST(Imgproc_ColorLab_Full, bitExactness) +{ + int codes[] = { CV_BGR2Lab, CV_RGB2Lab, CV_LBGR2Lab, CV_LRGB2Lab, + CV_Lab2BGR, CV_Lab2RGB, CV_Lab2LBGR, CV_Lab2LRGB}; + string names[] = { "CV_BGR2Lab", "CV_RGB2Lab", "CV_LBGR2Lab", "CV_LRGB2Lab", + "CV_Lab2BGR", "CV_Lab2RGB", "CV_Lab2LBGR", "CV_Lab2LRGB" }; + + // need to be recalculated each time we change Lab algorithms, RNG or test system + const int nIterations = 8; + uint32_t hashes[] = { + 0xca7d94c4, 0x34aeb79a, 0x7272c2cf, 0x62c2efed, 0x047cab77, 0x5e8dfb85, 0x10fed613, 0x34d2f4aa, + 0x048bea9a, 0xbbe20ef2, 0x3274e88f, 0x710e9272, 0x9fd6cd59, 0x69d67639, 0x04742095, 0x9ef2b60b, + 0x75b78f5b, 0x3fda9801, 0x374cc472, 0x3239e8ad, 0x94749b2d, 0x9362ac0c, 0xa4d7dd36, 0xe25ef694, + 0x51d1b01d, 0xb0f6e3f5, 0x2b72a228, 0xb7429fa0, 0x799ba6bd, 0x2141d3d2, 0xb4dde471, 0x813b6e0f, + 0x9c029161, 0xb51eb5ec, 0x460c3a09, 0x27724f63, 0xb446c9a8, 0x3adf1b61, 0xe6b0d30f, 0xd1078779, + 0xfaa7525b, 0x5b6ea158, 0xdf3511f7, 0xf01dc02d, 0x5c663841, 0xce611ed4, 0x758ad851, 0xa43c3a1c, + 0xed30f68c, 0xcb6babd9, 0xf38262b5, 0x608cb3db, 0x13425e5a, 0x6dc5fdc7, 0x9519090a, 0x87aa73d0, + 0x8e9bf980, 0x46b98728, 0x0064591c, 0x7e1efc9b, 0xf0ec2465, 0x89a75c8d, 0x0d162fa7, 0xffea7a2f, + }; + + RNG rng(0); + // blueIdx x srgb x direction + for(int c = 0; c < 8; c++) + { + int v = c; + int blueIdx = (v % 2 != 0) ? 2 : 0; v /=2; + bool srgb = (v % 2 == 0); v /= 2; + bool forward = (v % 2 == 0); + + for(int iter = 0; iter < nIterations; iter++) + { + Mat probe(256, 256, CV_8UC3), result; + rng.fill(probe, RNG::UNIFORM, 0, 255, true); + + cvtColor(probe, result, codes[c]); + + uint32_t h = adler32(result); + + if(h != hashes[c*nIterations + iter]) + { + initLabTabs(); + cvtest::TS* ts = cvtest::TS::ptr(); + + vector goldBuf(probe.cols*4); + uchar* goldRow = &goldBuf[0]; + bool next = true; + for(int y = 0; next && y < probe.rows; y++) + { + uchar* probeRow = probe.ptr(y); + uchar* resultRow = result.ptr(y); + row8uLabChoose(probeRow, goldRow, probe.cols, forward, blueIdx, srgb); + + for(int x = 0; next && x < probe.cols; x++) + { + uchar* px = probeRow + x*3; + uchar* gx = goldRow + x*3; + uchar* rx = resultRow + x*3; + if(gx[0] != rx[0] || gx[1] != rx[1] || gx[2] != rx[2]) + { + next = false; + ts->printf(cvtest::TS::SUMMARY, "Error in: (%d, %d)\n", x, y); + ts->printf(cvtest::TS::SUMMARY, "Conversion code: %s\n", names[c].c_str()); + ts->printf(cvtest::TS::SUMMARY, "Reference value: %d %d %d\n", gx[0], gx[1], gx[2]); + ts->printf(cvtest::TS::SUMMARY, "Actual value: %d %d %d\n", rx[0], rx[1], rx[2]); + ts->printf(cvtest::TS::SUMMARY, "Src value: %d %d %d\n", px[0], px[1], px[2]); + ts->printf(cvtest::TS::SUMMARY, "Size: (%d, %d)\n", probe.rows, probe.cols); + + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + ts->set_gtest_status(); + break; + } + } + } + if(next) + // this place should never be reached + throw std::runtime_error("Test system error: hash function mismatch when results are the same"); + } + } + } +} + static void test_Bayer2RGB_EdgeAware_8u(const Mat& src, Mat& dst, int code) { if (dst.empty()) From 92093a6e6cbce95f1196b8ae5c99fe603b6d31e6 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Thu, 17 Aug 2017 02:34:32 +0300 Subject: [PATCH 11/14] extra declarations removed --- modules/imgproc/test/test_color.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 4677b01684..1191578d2b 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -1044,8 +1044,6 @@ protected: double get_success_error_level( int test_case_idx, int i, int j ); void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n ); void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n ); - int convert_row_bgr2abc_8u_c3( const uchar *src_row, uchar *dst_row, int n, int cn ); - int convert_row_abc2bgr_8u_c3( const uchar *src_row, uchar *dst_row, int n, int cn ); }; From f6a165d10f507f5b538ef2c7128f170a0bf44c1c Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Thu, 17 Aug 2017 18:54:50 +0300 Subject: [PATCH 12/14] Lab test fix: stop at first mismatch --- modules/imgproc/test/test_color.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 1191578d2b..4a057144c7 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -2369,14 +2369,15 @@ TEST(Imgproc_ColorLab_Full, bitExactness) RNG rng(0); // blueIdx x srgb x direction - for(int c = 0; c < 8; c++) + bool next = true; + for(int c = 0; next && c < 8; c++) { int v = c; int blueIdx = (v % 2 != 0) ? 2 : 0; v /=2; bool srgb = (v % 2 == 0); v /= 2; bool forward = (v % 2 == 0); - for(int iter = 0; iter < nIterations; iter++) + for(int iter = 0; next && iter < nIterations; iter++) { Mat probe(256, 256, CV_8UC3), result; rng.fill(probe, RNG::UNIFORM, 0, 255, true); @@ -2392,7 +2393,6 @@ TEST(Imgproc_ColorLab_Full, bitExactness) vector goldBuf(probe.cols*4); uchar* goldRow = &goldBuf[0]; - bool next = true; for(int y = 0; next && y < probe.rows; y++) { uchar* probeRow = probe.ptr(y); From 2457bfc1e32e7261e2ff280dc1386cb055825061 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Fri, 25 Aug 2017 19:07:31 +0300 Subject: [PATCH 13/14] test info output improved --- modules/imgproc/test/test_color.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 4a057144c7..6cce5405b2 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -2385,11 +2385,11 @@ TEST(Imgproc_ColorLab_Full, bitExactness) cvtColor(probe, result, codes[c]); uint32_t h = adler32(result); + uint32_t goodHash = hashes[c*nIterations + iter]; - if(h != hashes[c*nIterations + iter]) + if(h != goodHash) { initLabTabs(); - cvtest::TS* ts = cvtest::TS::ptr(); vector goldBuf(probe.cols*4); uchar* goldRow = &goldBuf[0]; @@ -2407,15 +2407,18 @@ TEST(Imgproc_ColorLab_Full, bitExactness) if(gx[0] != rx[0] || gx[1] != rx[1] || gx[2] != rx[2]) { next = false; - ts->printf(cvtest::TS::SUMMARY, "Error in: (%d, %d)\n", x, y); - ts->printf(cvtest::TS::SUMMARY, "Conversion code: %s\n", names[c].c_str()); - ts->printf(cvtest::TS::SUMMARY, "Reference value: %d %d %d\n", gx[0], gx[1], gx[2]); - ts->printf(cvtest::TS::SUMMARY, "Actual value: %d %d %d\n", rx[0], rx[1], rx[2]); - ts->printf(cvtest::TS::SUMMARY, "Src value: %d %d %d\n", px[0], px[1], px[2]); - ts->printf(cvtest::TS::SUMMARY, "Size: (%d, %d)\n", probe.rows, probe.cols); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - ts->set_gtest_status(); + FAIL() << "Bad accuracy" << endl; + + FAIL() << "Conversion code: " << names[c] << endl; + FAIL() << "Iteration: " << iter << endl; + FAIL() << "Hash vs Correct hash: " << h << ", " << goodHash << endl; + FAIL() << "Error in: (" << x << ", " << y << ")" << endl; + FAIL() << "Reference value: " << gx[0] << " " << gx[1] << " " << gx[2] << endl; + FAIL() << "Actual value: " << rx[0] << " " << rx[1] << " " << rx[2] << endl; + FAIL() << "Src value: " << px[0] << " " << px[1] << " " << px[2] << endl; + FAIL() << "Size: (" << probe.rows << ", " << probe.cols << ")" << endl; + break; } } From 18ca3d1e6211b22b692fbad0f419067ab180ebda Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Tue, 12 Sep 2017 20:05:58 +0300 Subject: [PATCH 14/14] error message fixed --- modules/imgproc/test/test_color.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index 6cce5405b2..fb44aa7fa1 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -2408,16 +2408,15 @@ TEST(Imgproc_ColorLab_Full, bitExactness) { next = false; - FAIL() << "Bad accuracy" << endl; - - FAIL() << "Conversion code: " << names[c] << endl; - FAIL() << "Iteration: " << iter << endl; - FAIL() << "Hash vs Correct hash: " << h << ", " << goodHash << endl; - FAIL() << "Error in: (" << x << ", " << y << ")" << endl; - FAIL() << "Reference value: " << gx[0] << " " << gx[1] << " " << gx[2] << endl; - FAIL() << "Actual value: " << rx[0] << " " << rx[1] << " " << rx[2] << endl; - FAIL() << "Src value: " << px[0] << " " << px[1] << " " << px[2] << endl; - FAIL() << "Size: (" << probe.rows << ", " << probe.cols << ")" << endl; + FAIL() << "Bad accuracy" << endl + << "Conversion code: " << names[c] << endl + << "Iteration: " << iter << endl + << "Hash vs Correct hash: " << h << ", " << goodHash << endl + << "Error in: (" << x << ", " << y << ")" << endl + << "Reference value: " << gx[0] << " " << gx[1] << " " << gx[2] << endl + << "Actual value: " << rx[0] << " " << rx[1] << " " << rx[2] << endl + << "Src value: " << px[0] << " " << px[1] << " " << px[2] << endl + << "Size: (" << probe.rows << ", " << probe.cols << ")" << endl; break; }