void tst_QColor::convertTo() { QColor color(Qt::black); QColor rgb = color.convertTo(QColor::Rgb); QVERIFY(rgb.spec() == QColor::Rgb); QColor hsv = color.convertTo(QColor::Hsv); QVERIFY(hsv.spec() == QColor::Hsv); QColor cmyk = color.convertTo(QColor::Cmyk); QVERIFY(cmyk.spec() == QColor::Cmyk); QColor hsl = color.convertTo(QColor::Hsl); QVERIFY(hsl.spec() == QColor::Hsl); QColor invalid = color.convertTo(QColor::Invalid); QVERIFY(invalid.spec() == QColor::Invalid); DEPENDS_ON(toRgb()); DEPENDS_ON(toHsv()); DEPENDS_ON(toCmyk()); DEPENDS_ON(toHsl()); }
void Pixel::convertColorModel(ColorModel model) { // When converting between models other then RGB, first convert to RGB then to other model if (this->model != RGB) toRgb(); if (model == RGB) return; // Nothing to do int R = c[0], G = c[1], B = c[2]; double X,Y,Z,L_lab,a_lab,b_lab; // used in several conversions if (model == YUV) { // YUV formula /*double Y, U, V; Y = 0.299*R + 0.587*G + 0.114*B; U = 0.492*(B-Y); V = 0.877*(R-Y); // Ranges are now Ye[0,1], Ue[-0.436,0.436], Ve[-0.615,0.615] // Convert to byte: Y = toByte(Y*256); U = toByte((U+0.436)*256 / (0.436*2)); V = toByte((V+0.615)*256 / (0.615*2));*/ // The following is equivalent and comes from JPEG standard int Y = toByte ( 0.299 * R + 0.587 * G + 0.114 * B ); int U = toByte ( 128 - 0.168736 * R - 0.331264 * G + 0.5 * B ); int V = toByte ( 128 + 0.5 * R - 0.418688 * G - 0.081312 * B ); c[0] = Y; c[1] = U; c[2] = V; return; } if (model == YIQ) { int Y = toByte ( 0.299 * R + 0.587 * G + 0.114 * B ); int I = toByte ( (152 + 0.596 * R - 0.275 * G - 0.321 * B) / 304 * 255 ); int Q = toByte ( (133 + 0.212 * R - 0.523 * G + 0.311 * B) / 266 * 255 ); c[0] = Y; c[1] = I; c[2] = Q; return; } if (model == HSV) { int H, S, V; QColor color(R, G, B); color.getHsv(&H, &S, &V); H = (H*256)/360; // Rescale to 0-255 if (H==256) H=255; c[0] = H; c[1] = S; c[2] = V; return; } if (model == HSL) { int H, S, L; QColor color(R, G, B); color.getHsl(&H, &S, &L); H = (H*256)/360; // Rescale to 0-255 if (H==256) H=255; c[0] = H; c[1] = S; c[2] = L; return; } if (model == HSI) { int H, S, I; QColor color(R, G, B); color.getHsl(&H, &S, &I); H = (H*256)/360; // Rescale to 0-255 if (H==256) H=255; I = (R+G+B)/765; // 765 = 3*255 c[0] = H; c[1] = S; c[2] = I; return; } if (model == IHLS) { // More perceptually uniform variant of HSL color space // See: Hanbury and Serra "A 3D-polar coordinate colour representation suitable for image analysis" (2002) int H, S, L; QColor color(R, G, B); color.getHsl(&H, &S, &L); H = (H*256)/360; // Rescale to 0-255 if (H==256) H=255; int M=qMax(R,qMax(G,B)); int m=qMin(R,qMin(G,B)); S = M-m; c[0] = H; c[1] = S; c[2] = L; return; } if (model == HMMD) { // Color model used in MPEG-7 standard // See: Manjunath et al. "Color and texture descriptors" (2001) int H, S, V; QColor color(R, G, B); color.getHsv(&H, &S, &V); H = (H*256)/360; // Rescale to 0-255 if (H==256) H=255; int M=qMax(R,qMax(G,B)); int m=qMin(R,qMin(G,B)); int D=M-m; c[0] = H; c[1] = M; c[2] = m; c[3] = D; return; } if (model == HY) { int H, S, V; QColor color(R, G, B); color.getHsv(&H, &S, &V); H = (H*256)/360; // Rescale to 0-255 if (H==256) H=255; int Y = toByte ( 0.299 * R + 0.587 * G + 0.114 * B ); c[0] = H; c[1] = Y; return; } if (model == XYZ || model == LAB || model == LUV || model == LCH) { // Convert RGB to range [0,1] double r(double(R)/255); double g(double(G)/255); double b(double(B)/255); // We assume that color is sRGB with decoding gamma of 2.2, as customary // Linearize sRGB if (r<0.04045) r=r/12.92; else r=pow((r+0.055)/1.055, 2.4); if (g<0.04045) g=g/12.92; else g=pow((g+0.055)/1.055, 2.4); if (b<0.04045) b=b/12.92; else b=pow((b+0.055)/1.055, 2.4); // Convert sRGB to XYZ using transformation matrix X = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b; Y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b; Z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b; } if (model == XYZ) { c[0] = toByte(X*255); c[1] = toByte(Y*255); c[2] = toByte(Z*255); return; } if (model == LAB || model == LCH) { // Reference illuminant D65 white point double Xn = 0.95047; double Yn = 1; double Zn = 1.08883; double D = pow( 6.0 / 29.0, 3 ); double Xfn( X / Xn ), Yfn( Y / Yn ), Zfn( Z / Zn ); if ( Xfn > D ) Xfn = pow(Xfn,1.0/3.0); else Xfn = ( 1.0 / 3.0 ) * pow( (29.0 / 6.0), 2) * Xfn + ( 4.0 / 29.0 ); if ( Yfn > D ) Yfn = pow(Yfn,1.0/3.0); else Yfn = ( 1.0 / 3.0 ) * pow( (29.0 / 6.0), 2) * Yfn + ( 4.0 / 29.0 ); if ( Zfn > D ) Zfn = pow(Zfn,1.0/3.0); else Zfn = ( 1.0 / 3.0 ) * pow( (29.0 / 6.0), 2) * Zfn + ( 4.0 / 29.0 ); L_lab = 116 * Yfn - 16; a_lab = 500 * (Xfn - Yfn); b_lab = 200 * (Yfn - Zfn); } if (model == LAB) { // Convert to byte c[0] = toByte( L_lab * 255 / 100 ); // Actual gamut of RGB in CIE Lab space is [0,100], [-86,98], [-107,94]; c[1] = toByte( (a_lab + 86) * 255 / (86 + 98) ); c[2] = toByte( (b_lab + 107) * 255 / (107 + 94) ); return; } if (model == LCH) { // a and b are in range [-127,128] a_lab = (a_lab+127) / 255; b_lab = (b_lab+127) / 255; double H = atan2(b_lab, a_lab); double C = sqrt(a_lab*a_lab + b_lab*b_lab); c[0] = toByte( L_lab * 255 / 100 ); // H is in range [0.092, 1.37] c[1] = toByte( (H - 0.092) * 199 ); // C is in range [0.533, 1.11] c[2] = toByte( (C - 0.533) * 440 ); return; } if (model == LUV) { // Reference illuminant D65 white point double Xn = 0.95047; double Yn = 1; double Zn = 1.08883; double D = pow( 6.0 / 29.0, 3 ); double Yfn( Y / Yn ); if ( Yfn > D ) Yfn = pow(Yfn,1.0/3.0); else Yfn = ( 1.0 / 3.0 ) * pow( (29.0 / 6.0), 2) * Yfn + ( 4.0 / 29.0 ); double Xfn((4*X) / (X + 15*Y + 3*Z)); double Zfn((9*Y) / (X + 15*Y + 3*Z)); double Xref((4*Xn) / (Xn + 15*Yn + 3*Zn)); double Zref((9*Yn) / (Xn + 15*Yn + 3*Zn)); double L = 116 * Yfn - 16; double u = 13 * L * ( Xfn - Xref ); double v = 13 * L * ( Zfn - Zref ); // Convert to byte c[0] = toByte( L*255 / 100 ); c[1] = toByte( u + 127 ); c[2] = toByte( v + 127 ); return; } if (model == L1L2L3) { // See: Gevers and Smeulders "Color-based object recognition" (1999) int d1 = (R-G)*(R-G); int d2 = (R-B)*(R-B); int d3 = (G-B)*(G-B); double l1 = double(d1) / (d1+d2+d3); double l2 = double(d2) / (d1+d2+d3); double l3 = double(d3) / (d1+d2+d3); c[0] = toByte(l1); c[1] = toByte(l2); c[2] = toByte(l3); } }