Skip to content

Commit 375ec5e

Browse files
committed
bit-exact tests written for Lab
1 parent 758f914 commit 375ec5e

File tree

1 file changed

+255
-8
lines changed

1 file changed

+255
-8
lines changed

modules/imgproc/test/test_color.cpp

Lines changed: 255 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ class CV_ColorCvtBaseTest : public cvtest::ArrayTest
7070

7171
// called from default implementation of convert_backward
7272
// for cases of bit-exact functions
73-
virtual int convert_row_abc2bgr_8u_c3( const uchar* src_row, uchar* dst_row, int n );
73+
virtual int convert_row_abc2bgr_8u_c3( const uchar* src_row, uchar* dst_row, int n, int cn );
7474

7575
// called from default implementation of convert_forward
7676
// for cases of bit-exact functions
77-
virtual int convert_row_bgr2abc_8u_c3(const uchar *src_row, uchar *dst_row, int n );
77+
virtual int convert_row_bgr2abc_8u_c3(const uchar *src_row, uchar *dst_row, int n, int cn);
7878

7979
const char* fwd_code_str;
8080
const char* inv_code_str;
@@ -237,7 +237,7 @@ void CV_ColorCvtBaseTest::convert_forward( const Mat& src, Mat& dst )
237237
const uchar* src_row = src.ptr(i);
238238
uchar* dst_row = dst.ptr(i);
239239

240-
int processed = convert_row_bgr2abc_8u_c3( src_row, dst_row, cols );
240+
int processed = convert_row_bgr2abc_8u_c3( src_row, dst_row, cols, cn );
241241
if(processed != cols)
242242
{
243243
for( j = 0; j < cols; j++ )
@@ -313,14 +313,14 @@ void CV_ColorCvtBaseTest::convert_row_abc2bgr_32f_c3( const float* /*src_row*/,
313313

314314

315315
int CV_ColorCvtBaseTest::convert_row_abc2bgr_8u_c3(const uchar * /*src_row*/,
316-
uchar * /*dst_row*/, int /*n*/ )
316+
uchar * /*dst_row*/, int /*n*/, int /*cn*/ )
317317
{
318318
return 0;
319319
}
320320

321321

322-
int CV_ColorCvtBaseTest::convert_row_bgr2abc_8u_c3( const uchar* /*src_row*/,
323-
uchar* /*dst_row*/, int /*n*/ )
322+
int CV_ColorCvtBaseTest::convert_row_bgr2abc_8u_c3(const uchar* /*src_row*/,
323+
uchar* /*dst_row*/, int /*n*/ , int /*cn*/)
324324
{
325325
return 0;
326326
}
@@ -349,7 +349,7 @@ void CV_ColorCvtBaseTest::convert_backward( const Mat& src, const Mat& dst, Mat&
349349
const uchar* src_row = dst.ptr(i);
350350
uchar* dst_row = dst2.ptr(i);
351351

352-
int processed = convert_row_abc2bgr_8u_c3(src_row, dst_row, dst_cols);
352+
int processed = convert_row_abc2bgr_8u_c3(src_row, dst_row, dst_cols, cn);
353353

354354
if(processed != dst_cols)
355355
{
@@ -1027,6 +1027,57 @@ void CV_ColorXYZTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* d
10271027

10281028

10291029
//// rgb <=> L*a*b*
1030+
1031+
// taken from color.cpp
1032+
1033+
static ushort sRGBGammaTab_b[256], linearGammaTab_b[256];
1034+
enum { inv_gamma_shift = 12, INV_GAMMA_TAB_SIZE = (1 << inv_gamma_shift) };
1035+
static ushort sRGBInvGammaTab_b[INV_GAMMA_TAB_SIZE], linearInvGammaTab_b[INV_GAMMA_TAB_SIZE];
1036+
#undef lab_shift
1037+
// #define lab_shift xyz_shift
1038+
#define lab_shift 12
1039+
#define gamma_shift 3
1040+
#define lab_shift2 (lab_shift + gamma_shift)
1041+
#define LAB_CBRT_TAB_SIZE_B (256*3/2*(1<<gamma_shift))
1042+
static ushort LabCbrtTab_b[LAB_CBRT_TAB_SIZE_B];
1043+
1044+
enum
1045+
{
1046+
lab_base_shift = 14,
1047+
LAB_BASE = (1 << lab_base_shift),
1048+
};
1049+
1050+
#define CV_DESCALE(x,n) (((x) + (1 << ((n)-1))) >> (n))
1051+
1052+
static ushort LabToYF_b[256*2];
1053+
static const int minABvalue = -8145;
1054+
static int abToXZ_b[LAB_BASE*9/4];
1055+
1056+
//all constants should be presented through integers to keep bit-exactness
1057+
static const softdouble gammaThreshold = softdouble(809)/softdouble(20000); // 0.04045
1058+
static const softdouble gammaInvThreshold = softdouble(7827)/softdouble(2500000); // 0.0031308
1059+
static const softdouble gammaLowScale = softdouble(323)/softdouble(25); // 12.92
1060+
static const softdouble gammaPower = softdouble(12)/softdouble(5); // 2.4
1061+
static const softdouble gammaXshift = softdouble(11)/softdouble(200); // 0.055
1062+
1063+
static inline softfloat applyGamma(softfloat x)
1064+
{
1065+
//return x <= 0.04045f ? x*(1.f/12.92f) : (float)std::pow((double)(x + 0.055)*(1./1.055), 2.4);
1066+
softdouble xd = x;
1067+
return (xd <= gammaThreshold ?
1068+
xd/gammaLowScale :
1069+
pow((xd + gammaXshift)/(softdouble::one()+gammaXshift), gammaPower));
1070+
}
1071+
1072+
static inline softfloat applyInvGamma(softfloat x)
1073+
{
1074+
//return x <= 0.0031308 ? x*12.92f : (float)(1.055*std::pow((double)x, 1./2.4) - 0.055);
1075+
softdouble xd = x;
1076+
return (xd <= gammaInvThreshold ?
1077+
xd*gammaLowScale :
1078+
pow(xd, softdouble::one()/gammaPower)*(softdouble::one()+gammaXshift) - gammaXshift);
1079+
}
1080+
10301081
static inline float applyGamma(float x)
10311082
{
10321083
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)
10371088
return x <= 0.0031308 ? x*12.92f : (float)(1.055*std::pow((double)x, 1./2.4) - 0.055);
10381089
}
10391090

1091+
static void initLabTabs()
1092+
{
1093+
static bool initialized = false;
1094+
if(!initialized)
1095+
{
1096+
static const softfloat lthresh = softfloat(216) / softfloat(24389); // 0.008856f = (6/29)^3
1097+
static const softfloat lscale = softfloat(841) / softfloat(108); // 7.787f = (29/3)^3/(29*4)
1098+
static const softfloat lbias = softfloat(16) / softfloat(116);
1099+
static const softfloat f255(255);
1100+
1101+
static const softfloat intScale(255*(1 << gamma_shift));
1102+
for(int i = 0; i < 256; i++)
1103+
{
1104+
softfloat x = softfloat(i)/f255;
1105+
sRGBGammaTab_b[i] = (ushort)(cvRound(intScale*applyGamma(x)));
1106+
linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift));
1107+
}
1108+
static const softfloat invScale = softfloat::one()/softfloat((int)INV_GAMMA_TAB_SIZE);
1109+
for(int i = 0; i < INV_GAMMA_TAB_SIZE; i++)
1110+
{
1111+
softfloat x = invScale*softfloat(i);
1112+
sRGBInvGammaTab_b[i] = (ushort)(cvRound(f255*applyInvGamma(x)));
1113+
linearInvGammaTab_b[i] = (ushort)(cvTrunc(f255*x));
1114+
}
1115+
1116+
static const softfloat cbTabScale(softfloat::one()/(f255*(1 << gamma_shift)));
1117+
static const softfloat lshift2(1 << lab_shift2);
1118+
for(int i = 0; i < LAB_CBRT_TAB_SIZE_B; i++)
1119+
{
1120+
softfloat x = cbTabScale*softfloat(i);
1121+
LabCbrtTab_b[i] = (ushort)(cvRound(lshift2 * (x < lthresh ? mulAdd(x, lscale, lbias) : cbrt(x))));
1122+
}
1123+
1124+
//Lookup table for L to y and ify calculations
1125+
static const int BASE = (1 << 14);
1126+
for(int i = 0; i < 256; i++)
1127+
{
1128+
int y, ify;
1129+
//8 * 255.0 / 100.0 == 20.4
1130+
if( i <= 20)
1131+
{
1132+
//yy = li / 903.3f;
1133+
//y = L*100/903.3f; 903.3f = (29/3)^3, 255 = 17*3*5
1134+
y = cvRound(softfloat(i*BASE*20*9)/softfloat(17*29*29*29));
1135+
//fy = 7.787f * yy + 16.0f / 116.0f; 7.787f = (29/3)^3/(29*4)
1136+
ify = cvRound(softfloat(BASE)*(softfloat(16)/softfloat(116) + softfloat(i*5)/softfloat(3*17*29)));
1137+
}
1138+
else
1139+
{
1140+
//fy = (li + 16.0f) / 116.0f;
1141+
softfloat fy = (softfloat(i*100*BASE)/softfloat(255*116) +
1142+
softfloat(16*BASE)/softfloat(116));
1143+
ify = cvRound(fy);
1144+
//yy = fy * fy * fy;
1145+
y = cvRound(fy*fy*fy/softfloat(BASE*BASE));
1146+
}
1147+
1148+
LabToYF_b[i*2 ] = (ushort)y; // 2260 <= y <= BASE
1149+
LabToYF_b[i*2+1] = (ushort)ify; // 0 <= ify <= BASE
1150+
}
1151+
1152+
//Lookup table for a,b to x,z conversion
1153+
for(int i = minABvalue; i < LAB_BASE*9/4+minABvalue; i++)
1154+
{
1155+
int v;
1156+
//6.f/29.f*BASE = 3389.730
1157+
if(i <= 3390)
1158+
{
1159+
//fxz[k] = (fxz[k] - 16.0f / 116.0f) / 7.787f;
1160+
// 7.787f = (29/3)^3/(29*4)
1161+
v = i*108/841 - BASE*16/116*108/841;
1162+
}
1163+
else
1164+
{
1165+
//fxz[k] = fxz[k] * fxz[k] * fxz[k];
1166+
v = i*i/BASE*i/BASE;
1167+
}
1168+
abToXZ_b[i-minABvalue] = v; // -1335 <= v <= 88231
1169+
}
1170+
1171+
initialized = true;
1172+
}
1173+
}
1174+
10401175
class CV_ColorLabTest : public CV_ColorCvtBaseTest
10411176
{
10421177
public:
@@ -1046,11 +1181,14 @@ class CV_ColorLabTest : public CV_ColorCvtBaseTest
10461181
double get_success_error_level( int test_case_idx, int i, int j );
10471182
void convert_row_bgr2abc_32f_c3( const float* src_row, float* dst_row, int n );
10481183
void convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n );
1184+
int convert_row_bgr2abc_8u_c3( const uchar *src_row, uchar *dst_row, int n, int cn );
1185+
int convert_row_abc2bgr_8u_c3( const uchar *src_row, uchar *dst_row, int n, int cn );
10491186
};
10501187

10511188

10521189
CV_ColorLabTest::CV_ColorLabTest() : CV_ColorCvtBaseTest( true, true, false )
10531190
{
1191+
initLabTabs();
10541192
INIT_FWD_INV_CODES( BGR2Lab, Lab2BGR );
10551193
}
10561194

@@ -1079,7 +1217,50 @@ void CV_ColorLabTest::get_test_array_types_and_sizes( int test_case_idx, vector<
10791217
double CV_ColorLabTest::get_success_error_level( int /*test_case_idx*/, int i, int j )
10801218
{
10811219
int depth = test_mat[i][j].depth();
1082-
return depth == CV_8U ? 16 : depth == CV_16U ? 32 : 1e-3;
1220+
// j == 0 is for forward code, j == 1 is for inverse code
1221+
return (depth == CV_8U) ? 0 :
1222+
//16u is forbidden
1223+
//(depth == CV_16U) ? 32 :
1224+
srgb ? ((j == 0) ? 0.4 : 0.0055) : 1e-3;
1225+
}
1226+
1227+
1228+
int CV_ColorLabTest::convert_row_bgr2abc_8u_c3(const uchar* src_row, uchar *dst_row, int n, int cn)
1229+
{
1230+
int coeffs[9];
1231+
softdouble whitept[3] = {Xn, softdouble::one(), Zn};
1232+
1233+
static const softdouble lshift(1 << lab_shift);
1234+
for(int i = 0; i < 3; i++)
1235+
{
1236+
coeffs[i*3 + (blue_idx^2)] = cvRound(lshift*RGB2XYZ[i*3 ]/whitept[i]);
1237+
coeffs[i*3 + 1 ] = cvRound(lshift*RGB2XYZ[i*3+1]/whitept[i]);
1238+
coeffs[i*3 + (blue_idx )] = cvRound(lshift*RGB2XYZ[i*3+2]/whitept[i]);
1239+
}
1240+
1241+
const int Lscale = (116*255+50)/100;
1242+
const int Lshift = -((16*255*(1 << lab_shift2) + 50)/100);
1243+
const ushort* tab = srgb ? sRGBGammaTab_b : linearGammaTab_b;
1244+
for (int x = 0; x < n; x++)
1245+
{
1246+
int R = src_row[x*cn + 0],
1247+
G = src_row[x*cn + 1],
1248+
B = src_row[x*cn + 2];
1249+
R = tab[R], G = tab[G], B = tab[B];
1250+
int fX = LabCbrtTab_b[CV_DESCALE(R*coeffs[0] + G*coeffs[1] + B*coeffs[2], lab_shift)];
1251+
int fY = LabCbrtTab_b[CV_DESCALE(R*coeffs[3] + G*coeffs[4] + B*coeffs[5], lab_shift)];
1252+
int fZ = LabCbrtTab_b[CV_DESCALE(R*coeffs[6] + G*coeffs[7] + B*coeffs[8], lab_shift)];
1253+
1254+
int L = CV_DESCALE( Lscale*fY + Lshift, lab_shift2 );
1255+
int a = CV_DESCALE( 500*(fX - fY) + 128*(1 << lab_shift2), lab_shift2 );
1256+
int b = CV_DESCALE( 200*(fY - fZ) + 128*(1 << lab_shift2), lab_shift2 );
1257+
1258+
dst_row[x*3 ] = saturate_cast<uchar>(L);
1259+
dst_row[x*3 + 1] = saturate_cast<uchar>(a);
1260+
dst_row[x*3 + 2] = saturate_cast<uchar>(b);
1261+
}
1262+
1263+
return n;
10831264
}
10841265

10851266

@@ -1137,6 +1318,71 @@ void CV_ColorLabTest::convert_row_bgr2abc_32f_c3(const float* src_row, float* ds
11371318
}
11381319
}
11391320

1321+
int CV_ColorLabTest::convert_row_abc2bgr_8u_c3(const uchar* src_row, uchar *dst_row, int n, int cn)
1322+
{
1323+
static const int base_shift = 14;
1324+
static const int BASE = (1 << base_shift);
1325+
static const int shift = lab_shift+(base_shift-inv_gamma_shift);
1326+
1327+
int coeffs[9];
1328+
softdouble whitept[3] = {Xn, softdouble::one(), Zn};
1329+
1330+
static const softdouble lshift(1 << lab_shift);
1331+
for(int i = 0; i < 3; i++)
1332+
{
1333+
coeffs[i+(blue_idx )*3] = cvRound(lshift*XYZ2RGB[i ]*whitept[i]);
1334+
coeffs[i+ 1*3] = cvRound(lshift*XYZ2RGB[i+3]*whitept[i]);
1335+
coeffs[i+(blue_idx^2)*3] = cvRound(lshift*XYZ2RGB[i+6]*whitept[i]);
1336+
}
1337+
ushort* tab = srgb ? sRGBInvGammaTab_b : linearInvGammaTab_b;
1338+
1339+
for(int x = 0; x < n; x++)
1340+
{
1341+
uchar LL = src_row[x*3 ];
1342+
uchar aa = src_row[x*3 + 1];
1343+
uchar bb = src_row[x*3 + 2];
1344+
1345+
int ro, go, bo, xx, yy, zz, ify;
1346+
1347+
yy = LabToYF_b[LL*2 ];
1348+
ify = LabToYF_b[LL*2+1];
1349+
1350+
int adiv, bdiv;
1351+
//adiv = aa*BASE/500 - 128*BASE/500, bdiv = bb*BASE/200 - 128*BASE/200;
1352+
//approximations with reasonable precision
1353+
adiv = ((5*aa*53687 + (1 << 7)) >> 13) - 128*BASE/500;
1354+
bdiv = (( bb*41943 + (1 << 4)) >> 9) - 128*BASE/200+1;
1355+
1356+
int ifxz[] = {ify + adiv, ify - bdiv};
1357+
1358+
for(int k = 0; k < 2; k++)
1359+
{
1360+
int& v = ifxz[k];
1361+
v = abToXZ_b[v-minABvalue];
1362+
}
1363+
xx = ifxz[0]; /* yy = yy */; zz = ifxz[1];
1364+
1365+
ro = CV_DESCALE(coeffs[0]*xx + coeffs[1]*yy + coeffs[2]*zz, shift);
1366+
go = CV_DESCALE(coeffs[3]*xx + coeffs[4]*yy + coeffs[5]*zz, shift);
1367+
bo = CV_DESCALE(coeffs[6]*xx + coeffs[7]*yy + coeffs[8]*zz, shift);
1368+
1369+
ro = max(0, min((int)INV_GAMMA_TAB_SIZE-1, ro));
1370+
go = max(0, min((int)INV_GAMMA_TAB_SIZE-1, go));
1371+
bo = max(0, min((int)INV_GAMMA_TAB_SIZE-1, bo));
1372+
1373+
ro = tab[ro];
1374+
go = tab[go];
1375+
bo = tab[bo];
1376+
1377+
dst_row[x*cn ] = saturate_cast<uchar>(bo);
1378+
dst_row[x*cn + 1] = saturate_cast<uchar>(go);
1379+
dst_row[x*cn + 2] = saturate_cast<uchar>(ro);
1380+
if(cn == 4) dst_row[x*cn + 3] = 255;
1381+
}
1382+
1383+
return n;
1384+
}
1385+
11401386
void CV_ColorLabTest::convert_row_abc2bgr_32f_c3( const float* src_row, float* dst_row, int n )
11411387
{
11421388
int depth = test_mat[INPUT][0].depth();
@@ -1226,6 +1472,7 @@ class CV_ColorLuvTest : public CV_ColorCvtBaseTest
12261472

12271473
CV_ColorLuvTest::CV_ColorLuvTest() : CV_ColorCvtBaseTest( true, true, false )
12281474
{
1475+
initLabTabs();
12291476
INIT_FWD_INV_CODES( BGR2Luv, Luv2BGR );
12301477
}
12311478

0 commit comments

Comments
 (0)