@@ -2110,6 +2110,326 @@ TEST(Imgproc_ColorLab_Full, accuracy)
2110
2110
}
2111
2111
}
2112
2112
2113
+
2114
+ static uint32_t adler32 (Mat m)
2115
+ {
2116
+ uint32_t s1 = 1 , s2 = 0 ;
2117
+ for (int y = 0 ; y < m.rows ; y++)
2118
+ {
2119
+ uchar* py = m.ptr (y);
2120
+ for (size_t x = 0 ; x < m.cols *m.elemSize (); x++)
2121
+ {
2122
+ s1 = (s1 + py[x]) % 65521 ;
2123
+ s2 = (s1 + s2 ) % 65521 ;
2124
+ }
2125
+ }
2126
+ return (s2 << 16 ) + s1;
2127
+ }
2128
+
2129
+
2130
+ // taken from color.cpp
2131
+
2132
+ static ushort sRGBGammaTab_b [256 ], linearGammaTab_b[256 ];
2133
+ enum { inv_gamma_shift = 12 , INV_GAMMA_TAB_SIZE = (1 << inv_gamma_shift) };
2134
+ static ushort sRGBInvGammaTab_b [INV_GAMMA_TAB_SIZE], linearInvGammaTab_b[INV_GAMMA_TAB_SIZE];
2135
+ #undef lab_shift
2136
+ // #define lab_shift xyz_shift
2137
+ #define lab_shift 12
2138
+ #define gamma_shift 3
2139
+ #define lab_shift2 (lab_shift + gamma_shift)
2140
+ #define LAB_CBRT_TAB_SIZE_B (256 *3 /2 *(1 <<gamma_shift))
2141
+ static ushort LabCbrtTab_b[LAB_CBRT_TAB_SIZE_B];
2142
+
2143
+ enum
2144
+ {
2145
+ lab_base_shift = 14 ,
2146
+ LAB_BASE = (1 << lab_base_shift),
2147
+ };
2148
+
2149
+ #define CV_DESCALE (x,n ) (((x) + (1 << ((n)-1 ))) >> (n))
2150
+
2151
+ static ushort LabToYF_b[256 *2 ];
2152
+ static const int minABvalue = -8145 ;
2153
+ static int abToXZ_b[LAB_BASE*9 /4 ];
2154
+
2155
+ static void initLabTabs ()
2156
+ {
2157
+ static bool initialized = false ;
2158
+ if (!initialized)
2159
+ {
2160
+ static const softfloat lthresh = softfloat (216 ) / softfloat (24389 ); // 0.008856f = (6/29)^3
2161
+ static const softfloat lscale = softfloat (841 ) / softfloat (108 ); // 7.787f = (29/3)^3/(29*4)
2162
+ static const softfloat lbias = softfloat (16 ) / softfloat (116 );
2163
+ static const softfloat f255 (255 );
2164
+
2165
+ static const softfloat intScale (255 *(1 << gamma_shift));
2166
+ for (int i = 0 ; i < 256 ; i++)
2167
+ {
2168
+ softfloat x = softfloat (i)/f255;
2169
+ sRGBGammaTab_b [i] = (ushort)(cvRound (intScale*applyGamma (x)));
2170
+ linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift));
2171
+ }
2172
+ static const softfloat invScale = softfloat::one ()/softfloat ((int )INV_GAMMA_TAB_SIZE);
2173
+ for (int i = 0 ; i < INV_GAMMA_TAB_SIZE; i++)
2174
+ {
2175
+ softfloat x = invScale*softfloat (i);
2176
+ sRGBInvGammaTab_b [i] = (ushort)(cvRound (f255*applyInvGamma (x)));
2177
+ linearInvGammaTab_b[i] = (ushort)(cvTrunc (f255*x));
2178
+ }
2179
+
2180
+ static const softfloat cbTabScale (softfloat::one ()/(f255*(1 << gamma_shift)));
2181
+ static const softfloat lshift2 (1 << lab_shift2);
2182
+ for (int i = 0 ; i < LAB_CBRT_TAB_SIZE_B; i++)
2183
+ {
2184
+ softfloat x = cbTabScale*softfloat (i);
2185
+ LabCbrtTab_b[i] = (ushort)(cvRound (lshift2 * (x < lthresh ? mulAdd (x, lscale, lbias) : cbrt (x))));
2186
+ }
2187
+
2188
+ // Lookup table for L to y and ify calculations
2189
+ static const int BASE = (1 << 14 );
2190
+ for (int i = 0 ; i < 256 ; i++)
2191
+ {
2192
+ int y, ify;
2193
+ // 8 * 255.0 / 100.0 == 20.4
2194
+ if ( i <= 20 )
2195
+ {
2196
+ // yy = li / 903.3f;
2197
+ // y = L*100/903.3f; 903.3f = (29/3)^3, 255 = 17*3*5
2198
+ y = cvRound (softfloat (i*BASE*20 *9 )/softfloat (17 *29 *29 *29 ));
2199
+ // fy = 7.787f * yy + 16.0f / 116.0f; 7.787f = (29/3)^3/(29*4)
2200
+ ify = cvRound (softfloat (BASE)*(softfloat (16 )/softfloat (116 ) + softfloat (i*5 )/softfloat (3 *17 *29 )));
2201
+ }
2202
+ else
2203
+ {
2204
+ // fy = (li + 16.0f) / 116.0f;
2205
+ softfloat fy = (softfloat (i*100 *BASE)/softfloat (255 *116 ) +
2206
+ softfloat (16 *BASE)/softfloat (116 ));
2207
+ ify = cvRound (fy);
2208
+ // yy = fy * fy * fy;
2209
+ y = cvRound (fy*fy*fy/softfloat (BASE*BASE));
2210
+ }
2211
+
2212
+ LabToYF_b[i*2 ] = (ushort)y; // 2260 <= y <= BASE
2213
+ LabToYF_b[i*2 +1 ] = (ushort)ify; // 0 <= ify <= BASE
2214
+ }
2215
+
2216
+ // Lookup table for a,b to x,z conversion
2217
+ for (int i = minABvalue; i < LAB_BASE*9 /4 +minABvalue; i++)
2218
+ {
2219
+ int v;
2220
+ // 6.f/29.f*BASE = 3389.730
2221
+ if (i <= 3390 )
2222
+ {
2223
+ // fxz[k] = (fxz[k] - 16.0f / 116.0f) / 7.787f;
2224
+ // 7.787f = (29/3)^3/(29*4)
2225
+ v = i*108 /841 - BASE*16 /116 *108 /841 ;
2226
+ }
2227
+ else
2228
+ {
2229
+ // fxz[k] = fxz[k] * fxz[k] * fxz[k];
2230
+ v = i*i/BASE*i/BASE;
2231
+ }
2232
+ abToXZ_b[i-minABvalue] = v; // -1335 <= v <= 88231
2233
+ }
2234
+
2235
+ initialized = true ;
2236
+ }
2237
+ }
2238
+
2239
+ static int row8uRGB2Lab (const uchar* src_row, uchar *dst_row, int n, int cn, int blue_idx, bool srgb)
2240
+ {
2241
+ int coeffs[9 ];
2242
+ softdouble whitept[3 ] = {Xn, softdouble::one (), Zn};
2243
+
2244
+ static const softdouble lshift (1 << lab_shift);
2245
+ for (int i = 0 ; i < 3 ; i++)
2246
+ {
2247
+ coeffs[i*3 + (blue_idx^2 )] = cvRound (lshift*RGB2XYZ[i*3 ]/whitept[i]);
2248
+ coeffs[i*3 + 1 ] = cvRound (lshift*RGB2XYZ[i*3 +1 ]/whitept[i]);
2249
+ coeffs[i*3 + (blue_idx )] = cvRound (lshift*RGB2XYZ[i*3 +2 ]/whitept[i]);
2250
+ }
2251
+
2252
+ const int Lscale = (116 *255 +50 )/100 ;
2253
+ const int Lshift = -((16 *255 *(1 << lab_shift2) + 50 )/100 );
2254
+ const ushort* tab = srgb ? sRGBGammaTab_b : linearGammaTab_b;
2255
+ for (int x = 0 ; x < n; x++)
2256
+ {
2257
+ int R = src_row[x*cn + 0 ],
2258
+ G = src_row[x*cn + 1 ],
2259
+ B = src_row[x*cn + 2 ];
2260
+ R = tab[R], G = tab[G], B = tab[B];
2261
+ int fX = LabCbrtTab_b[CV_DESCALE (R*coeffs[0 ] + G*coeffs[1 ] + B*coeffs[2 ], lab_shift)];
2262
+ int fY = LabCbrtTab_b[CV_DESCALE (R*coeffs[3 ] + G*coeffs[4 ] + B*coeffs[5 ], lab_shift)];
2263
+ int fZ = LabCbrtTab_b[CV_DESCALE (R*coeffs[6 ] + G*coeffs[7 ] + B*coeffs[8 ], lab_shift)];
2264
+
2265
+ int L = CV_DESCALE ( Lscale*fY + Lshift, lab_shift2 );
2266
+ int a = CV_DESCALE ( 500 *(fX - fY ) + 128 *(1 << lab_shift2), lab_shift2 );
2267
+ int b = CV_DESCALE ( 200 *(fY - fZ ) + 128 *(1 << lab_shift2), lab_shift2 );
2268
+
2269
+ dst_row[x*3 ] = saturate_cast<uchar>(L);
2270
+ dst_row[x*3 + 1 ] = saturate_cast<uchar>(a);
2271
+ dst_row[x*3 + 2 ] = saturate_cast<uchar>(b);
2272
+ }
2273
+
2274
+ return n;
2275
+ }
2276
+
2277
+
2278
+ int row8uLab2RGB (const uchar* src_row, uchar *dst_row, int n, int cn, int blue_idx, bool srgb)
2279
+ {
2280
+ static const int base_shift = 14 ;
2281
+ static const int BASE = (1 << base_shift);
2282
+ static const int shift = lab_shift+(base_shift-inv_gamma_shift);
2283
+
2284
+ int coeffs[9 ];
2285
+ softdouble whitept[3 ] = {Xn, softdouble::one (), Zn};
2286
+
2287
+ static const softdouble lshift (1 << lab_shift);
2288
+ for (int i = 0 ; i < 3 ; i++)
2289
+ {
2290
+ coeffs[i+(blue_idx )*3 ] = cvRound (lshift*XYZ2RGB[i ]*whitept[i]);
2291
+ coeffs[i+ 1 *3 ] = cvRound (lshift*XYZ2RGB[i+3 ]*whitept[i]);
2292
+ coeffs[i+(blue_idx^2 )*3 ] = cvRound (lshift*XYZ2RGB[i+6 ]*whitept[i]);
2293
+ }
2294
+ ushort* tab = srgb ? sRGBInvGammaTab_b : linearInvGammaTab_b;
2295
+
2296
+ for (int x = 0 ; x < n; x++)
2297
+ {
2298
+ uchar LL = src_row[x*3 ];
2299
+ uchar aa = src_row[x*3 + 1 ];
2300
+ uchar bb = src_row[x*3 + 2 ];
2301
+
2302
+ int ro, go, bo, xx, yy, zz, ify;
2303
+
2304
+ yy = LabToYF_b[LL*2 ];
2305
+ ify = LabToYF_b[LL*2 +1 ];
2306
+
2307
+ int adiv, bdiv;
2308
+ // adiv = aa*BASE/500 - 128*BASE/500, bdiv = bb*BASE/200 - 128*BASE/200;
2309
+ // approximations with reasonable precision
2310
+ adiv = ((5 *aa*53687 + (1 << 7 )) >> 13 ) - 128 *BASE/500 ;
2311
+ bdiv = (( bb*41943 + (1 << 4 )) >> 9 ) - 128 *BASE/200 +1 ;
2312
+
2313
+ int ifxz[] = {ify + adiv, ify - bdiv};
2314
+
2315
+ for (int k = 0 ; k < 2 ; k++)
2316
+ {
2317
+ int & v = ifxz[k];
2318
+ v = abToXZ_b[v-minABvalue];
2319
+ }
2320
+ xx = ifxz[0 ]; /* yy = yy */ ; zz = ifxz[1 ];
2321
+
2322
+ ro = CV_DESCALE (coeffs[0 ]*xx + coeffs[1 ]*yy + coeffs[2 ]*zz, shift);
2323
+ go = CV_DESCALE (coeffs[3 ]*xx + coeffs[4 ]*yy + coeffs[5 ]*zz, shift);
2324
+ bo = CV_DESCALE (coeffs[6 ]*xx + coeffs[7 ]*yy + coeffs[8 ]*zz, shift);
2325
+
2326
+ ro = max (0 , min ((int )INV_GAMMA_TAB_SIZE-1 , ro));
2327
+ go = max (0 , min ((int )INV_GAMMA_TAB_SIZE-1 , go));
2328
+ bo = max (0 , min ((int )INV_GAMMA_TAB_SIZE-1 , bo));
2329
+
2330
+ ro = tab[ro];
2331
+ go = tab[go];
2332
+ bo = tab[bo];
2333
+
2334
+ dst_row[x*cn ] = saturate_cast<uchar>(bo);
2335
+ dst_row[x*cn + 1 ] = saturate_cast<uchar>(go);
2336
+ dst_row[x*cn + 2 ] = saturate_cast<uchar>(ro);
2337
+ if (cn == 4 ) dst_row[x*cn + 3 ] = 255 ;
2338
+ }
2339
+
2340
+ return n;
2341
+ }
2342
+
2343
+ int row8uLabChoose (const uchar* src_row, uchar *dst_row, int n, bool forward, int blue_idx, bool srgb)
2344
+ {
2345
+ if (forward)
2346
+ return row8uRGB2Lab (src_row, dst_row, n, 3 , blue_idx, srgb);
2347
+ else
2348
+ return row8uLab2RGB (src_row, dst_row, n, 3 , blue_idx, srgb);
2349
+ }
2350
+
2351
+
2352
+ TEST (Imgproc_ColorLab_Full, bitExactness)
2353
+ {
2354
+ int codes[] = { CV_BGR2Lab, CV_RGB2Lab, CV_LBGR2Lab, CV_LRGB2Lab,
2355
+ CV_Lab2BGR, CV_Lab2RGB, CV_Lab2LBGR, CV_Lab2LRGB};
2356
+ string names[] = { " CV_BGR2Lab" , " CV_RGB2Lab" , " CV_LBGR2Lab" , " CV_LRGB2Lab" ,
2357
+ " CV_Lab2BGR" , " CV_Lab2RGB" , " CV_Lab2LBGR" , " CV_Lab2LRGB" };
2358
+
2359
+ // need to be recalculated each time we change Lab algorithms, RNG or test system
2360
+ const int nIterations = 8 ;
2361
+ uint32_t hashes[] = {
2362
+ 0xca7d94c4 , 0x34aeb79a , 0x7272c2cf , 0x62c2efed , 0x047cab77 , 0x5e8dfb85 , 0x10fed613 , 0x34d2f4aa ,
2363
+ 0x048bea9a , 0xbbe20ef2 , 0x3274e88f , 0x710e9272 , 0x9fd6cd59 , 0x69d67639 , 0x04742095 , 0x9ef2b60b ,
2364
+ 0x75b78f5b , 0x3fda9801 , 0x374cc472 , 0x3239e8ad , 0x94749b2d , 0x9362ac0c , 0xa4d7dd36 , 0xe25ef694 ,
2365
+ 0x51d1b01d , 0xb0f6e3f5 , 0x2b72a228 , 0xb7429fa0 , 0x799ba6bd , 0x2141d3d2 , 0xb4dde471 , 0x813b6e0f ,
2366
+ 0x9c029161 , 0xb51eb5ec , 0x460c3a09 , 0x27724f63 , 0xb446c9a8 , 0x3adf1b61 , 0xe6b0d30f , 0xd1078779 ,
2367
+ 0xfaa7525b , 0x5b6ea158 , 0xdf3511f7 , 0xf01dc02d , 0x5c663841 , 0xce611ed4 , 0x758ad851 , 0xa43c3a1c ,
2368
+ 0xed30f68c , 0xcb6babd9 , 0xf38262b5 , 0x608cb3db , 0x13425e5a , 0x6dc5fdc7 , 0x9519090a , 0x87aa73d0 ,
2369
+ 0x8e9bf980 , 0x46b98728 , 0x0064591c , 0x7e1efc9b , 0xf0ec2465 , 0x89a75c8d , 0x0d162fa7 , 0xffea7a2f ,
2370
+ };
2371
+
2372
+ RNG rng (0 );
2373
+ // blueIdx x srgb x direction
2374
+ for (int c = 0 ; c < 8 ; c++)
2375
+ {
2376
+ int v = c;
2377
+ int blueIdx = (v % 2 != 0 ) ? 2 : 0 ; v /=2 ;
2378
+ bool srgb = (v % 2 == 0 ); v /= 2 ;
2379
+ bool forward = (v % 2 == 0 );
2380
+
2381
+ for (int iter = 0 ; iter < nIterations; iter++)
2382
+ {
2383
+ Mat probe (256 , 256 , CV_8UC3), result;
2384
+ rng.fill (probe, RNG::UNIFORM, 0 , 255 , true );
2385
+
2386
+ cvtColor (probe, result, codes[c]);
2387
+
2388
+ uint32_t h = adler32 (result);
2389
+
2390
+ if (h != hashes[c*nIterations + iter])
2391
+ {
2392
+ initLabTabs ();
2393
+ cvtest::TS* ts = cvtest::TS::ptr ();
2394
+
2395
+ vector<uchar> goldBuf (probe.cols *4 );
2396
+ uchar* goldRow = &goldBuf[0 ];
2397
+ bool next = true ;
2398
+ for (int y = 0 ; next && y < probe.rows ; y++)
2399
+ {
2400
+ uchar* probeRow = probe.ptr (y);
2401
+ uchar* resultRow = result.ptr (y);
2402
+ row8uLabChoose (probeRow, goldRow, probe.cols , forward, blueIdx, srgb);
2403
+
2404
+ for (int x = 0 ; next && x < probe.cols ; x++)
2405
+ {
2406
+ uchar* px = probeRow + x*3 ;
2407
+ uchar* gx = goldRow + x*3 ;
2408
+ uchar* rx = resultRow + x*3 ;
2409
+ if (gx[0 ] != rx[0 ] || gx[1 ] != rx[1 ] || gx[2 ] != rx[2 ])
2410
+ {
2411
+ next = false ;
2412
+ ts->printf (cvtest::TS::SUMMARY, " Error in: (%d, %d)\n " , x, y);
2413
+ ts->printf (cvtest::TS::SUMMARY, " Conversion code: %s\n " , names[c].c_str ());
2414
+ ts->printf (cvtest::TS::SUMMARY, " Reference value: %d %d %d\n " , gx[0 ], gx[1 ], gx[2 ]);
2415
+ ts->printf (cvtest::TS::SUMMARY, " Actual value: %d %d %d\n " , rx[0 ], rx[1 ], rx[2 ]);
2416
+ ts->printf (cvtest::TS::SUMMARY, " Src value: %d %d %d\n " , px[0 ], px[1 ], px[2 ]);
2417
+ ts->printf (cvtest::TS::SUMMARY, " Size: (%d, %d)\n " , probe.rows , probe.cols );
2418
+
2419
+ ts->set_failed_test_info (cvtest::TS::FAIL_BAD_ACCURACY);
2420
+ ts->set_gtest_status ();
2421
+ break ;
2422
+ }
2423
+ }
2424
+ }
2425
+ if (next)
2426
+ // this place should never be reached
2427
+ throw std::runtime_error (" Test system error: hash function mismatch when results are the same" );
2428
+ }
2429
+ }
2430
+ }
2431
+ }
2432
+
2113
2433
static void test_Bayer2RGB_EdgeAware_8u (const Mat& src, Mat& dst, int code)
2114
2434
{
2115
2435
if (dst.empty ())
0 commit comments