TuttleOFX  1
matrix.hpp
Go to the documentation of this file.
00001 #ifndef _TERRY_COLOR_MATRIX_HPP_
00002 #define _TERRY_COLOR_MATRIX_HPP_
00003 
00004 #include <terry/math/Matrix.hpp>
00005 
00006 #include <boost/math/constants/constants.hpp>
00007 
00008 namespace terry {
00009 namespace color {
00010 
00011 // http://www.graficaobscura.com/matrix/index.html
00012 
00013 static const double s_redLum = 0.3086;
00014 static const double s_greenLum = 0.6094;
00015 static const double s_blueLum = 0.0820;
00016 
00017 
00018 /**
00019  * @brief Make a color scale matrix.
00020  */
00021 template<class Matrix5x5>
00022 Matrix5x5 scaleMatrix(
00023         const typename Matrix5x5::value_type rScale,
00024         const typename Matrix5x5::value_type gScale,
00025         const typename Matrix5x5::value_type bScale,
00026         const typename Matrix5x5::value_type aScale )
00027 {
00028         Matrix5x5 matrix(5, 5);
00029     
00030     matrix(0, 0) = rScale;
00031     matrix(0, 1) = 0.0;
00032     matrix(0, 2) = 0.0;
00033     matrix(0, 3) = 0.0;
00034     matrix(0, 4) = 0.0;
00035 
00036     matrix(1, 0) = 0.0;
00037     matrix(1, 1) = gScale;
00038     matrix(1, 2) = 0.0;
00039     matrix(1, 3) = 0.0;
00040     matrix(1, 4) = 0.0;
00041 
00042     matrix(2, 0) = 0.0;
00043     matrix(2, 1) = 0.0;
00044     matrix(2, 2) = bScale;
00045     matrix(2, 3) = 0.0;
00046     matrix(2, 4) = 0.0;
00047 
00048     matrix(3, 0) = 0.0;
00049     matrix(3, 1) = 0.0;
00050     matrix(3, 2) = 0.0;
00051     matrix(3, 3) = aScale;
00052     matrix(3, 4) = 0.0;
00053 
00054     matrix(4, 0) = 0.0;
00055     matrix(4, 1) = 0.0;
00056     matrix(4, 2) = 0.0;
00057     matrix(4, 3) = 0.0;
00058     matrix(4, 4) = 1.0;
00059         
00060         return matrix;
00061 }
00062 
00063 /**
00064  * @brief Make a contrast matrix.
00065  */
00066 template<class Matrix5x5>
00067 Matrix5x5 contrastMatrix(
00068         const typename Matrix5x5::value_type rScale,
00069         const typename Matrix5x5::value_type gScale,
00070         const typename Matrix5x5::value_type bScale,
00071         const typename Matrix5x5::value_type aScale )
00072 {
00073         Matrix5x5 matrix(5, 5);
00074     
00075     matrix(0, 0) = rScale;
00076     matrix(0, 1) = 0.0;
00077     matrix(0, 2) = 0.0;
00078     matrix(0, 3) = 0.0;
00079     matrix(0, 4) = 0.0;
00080 
00081     matrix(1, 0) = 0.0;
00082     matrix(1, 1) = gScale;
00083     matrix(1, 2) = 0.0;
00084     matrix(1, 3) = 0.0;
00085     matrix(1, 4) = 0.0;
00086 
00087     matrix(2, 0) = 0.0;
00088     matrix(2, 1) = 0.0;
00089     matrix(2, 2) = bScale;
00090     matrix(2, 3) = 0.0;
00091     matrix(2, 4) = 0.0;
00092 
00093     matrix(3, 0) = 0.0;
00094     matrix(3, 1) = 0.0;
00095     matrix(3, 2) = 0.0;
00096     matrix(3, 3) = 0.0;
00097     matrix(3, 4) = 0.0;
00098 
00099     matrix(4, 0) = (1.0 - rScale) * 0.5;
00100     matrix(4, 1) = (1.0 - gScale) * 0.5;
00101     matrix(4, 2) = (1.0 - bScale) * 0.5;
00102     matrix(4, 3) = (1.0 - aScale) * 0.5;
00103     matrix(4, 4) = 1.0;
00104         
00105         return matrix;
00106 }
00107 
00108 /**
00109  * @brief Make a saturation matrix.
00110  */
00111 template<class Matrix5x5>
00112 Matrix5x5 saturationMatrix( const typename Matrix5x5::value_type saturation )
00113 {
00114         typedef typename Matrix5x5::value_type T;
00115         
00116         const T a = (1.0-saturation) * s_redLum + saturation;
00117         const T b = (1.0-saturation) * s_redLum;
00118         const T c = (1.0-saturation) * s_redLum;
00119         const T d = (1.0-saturation) * s_greenLum;
00120         const T e = (1.0-saturation) * s_greenLum + saturation;
00121         const T f = (1.0-saturation) * s_greenLum;
00122         const T g = (1.0-saturation) * s_blueLum;
00123         const T h = (1.0-saturation) * s_blueLum;
00124         const T i = (1.0-saturation) * s_blueLum + saturation;
00125         
00126         Matrix5x5 matrix(5, 5);
00127         
00128         matrix(0, 0) = a;
00129         matrix(0, 1) = b;
00130         matrix(0, 2) = c;
00131         matrix(0, 3) = 0.0;
00132         matrix(0, 4) = 0.0;
00133 
00134         matrix(1, 0) = d;
00135         matrix(1, 1) = e;
00136         matrix(1, 2) = f;
00137         matrix(1, 3) = 0.0;
00138         matrix(1, 4) = 0.0;
00139 
00140         matrix(2, 0) = g;
00141         matrix(2, 1) = h;
00142         matrix(2, 2) = i;
00143         matrix(2, 3) = 0.0;
00144         matrix(2, 4) = 0.0;
00145 
00146         matrix(3, 0) = 0.0;
00147         matrix(3, 1) = 0.0;
00148         matrix(3, 2) = 0.0;
00149         matrix(3, 3) = 1.0; // no saturation on alpha channel
00150         matrix(3, 4) = 0.0;
00151 
00152         matrix(4, 0) = 0.0;
00153         matrix(4, 1) = 0.0;
00154         matrix(4, 2) = 0.0;
00155         matrix(4, 3) = 0.0;
00156         matrix(4, 4) = 1.0;
00157 
00158         return matrix;
00159 }
00160 
00161 /**
00162  * @brief Offset r, g, and b.
00163  */
00164 template<class Matrix5x5>
00165 Matrix5x5 offsetMatrix(
00166                 const typename Matrix5x5::value_type rOffset,
00167                 const typename Matrix5x5::value_type gOffset,
00168                 const typename Matrix5x5::value_type bOffset,
00169                 const typename Matrix5x5::value_type aOffset )
00170 {
00171         Matrix5x5 matrix(5, 5);
00172 
00173     matrix(0, 0) = 1.0;
00174     matrix(0, 1) = 0.0;
00175     matrix(0, 2) = 0.0;
00176     matrix(0, 3) = 0.0;
00177     matrix(0, 4) = 0.0;
00178 
00179     matrix(1, 0) = 0.0;
00180     matrix(1, 1) = 1.0;
00181     matrix(1, 2) = 0.0;
00182     matrix(1, 3) = 0.0;
00183     matrix(1, 4) = 0.0;
00184 
00185     matrix(2, 0) = 0.0;
00186     matrix(2, 1) = 0.0;
00187     matrix(2, 2) = 1.0;
00188     matrix(2, 3) = 0.0;
00189     matrix(2, 4) = 0.0;
00190 
00191     matrix(3, 0) = 0.0;
00192     matrix(3, 1) = 0.0;
00193     matrix(3, 2) = 0.0;
00194     matrix(3, 3) = 1.0;
00195     matrix(3, 4) = 0.0;
00196 
00197     matrix(4, 0) = rOffset;
00198     matrix(4, 1) = gOffset;
00199     matrix(4, 2) = bOffset;
00200     matrix(4, 3) = aOffset;
00201     matrix(4, 4) = 1.0;
00202         
00203         return matrix;
00204 }
00205 
00206 /**
00207  * @brief rotate about the x (red) axis.
00208  */
00209 template<class Matrix5x5>
00210 Matrix5x5 xRotateMatrix(
00211         const typename Matrix5x5::value_type rs,
00212         const typename Matrix5x5::value_type rc )
00213 {
00214         Matrix5x5 matrix(5, 5);
00215 
00216         matrix(0, 0) = 1.0;
00217         matrix(0, 1) = 0.0;
00218         matrix(0, 2) = 0.0;
00219         matrix(0, 3) = 0.0;
00220         matrix(0, 4) = 0.0;
00221 
00222         matrix(1, 0) = 0.0;
00223         matrix(1, 1) = rc;
00224         matrix(1, 2) = rs;
00225         matrix(1, 3) = 0.0;
00226         matrix(1, 4) = 0.0;
00227 
00228         matrix(2, 0) = 0.0;
00229         matrix(2, 1) = -rs;
00230         matrix(2, 2) = rc;
00231         matrix(2, 3) = 0.0;
00232         matrix(2, 4) = 0.0;
00233 
00234         matrix(3, 0) = 0.0;
00235         matrix(3, 1) = 0.0;
00236         matrix(3, 2) = 0.0;
00237         matrix(3, 3) = 1.0;
00238         matrix(3, 4) = 0.0;
00239 
00240         matrix(4, 0) = 0.0;
00241         matrix(4, 1) = 0.0;
00242         matrix(4, 2) = 0.0;
00243         matrix(4, 3) = 0.0;
00244         matrix(4, 4) = 1.0;
00245 
00246         return matrix;
00247 }
00248 
00249 /**
00250  * @brief Rotate about the y (green) axis.
00251  */
00252 template<class Matrix5x5>
00253 Matrix5x5 yRotateMatrix(
00254         const typename Matrix5x5::value_type rs,
00255         const typename Matrix5x5::value_type rc )
00256 {
00257     Matrix5x5 matrix(5, 5);
00258         
00259     matrix(0, 0) = rc;
00260     matrix(0, 1) = 0.0;
00261     matrix(0, 2) = -rs;
00262     matrix(0, 3) = 0.0;
00263     matrix(0, 4) = 0.0;
00264 
00265     matrix(1, 0) = 0.0;
00266     matrix(1, 1) = 1.0;
00267     matrix(1, 2) = 0.0;
00268     matrix(1, 3) = 0.0;
00269     matrix(1, 4) = 0.0;
00270 
00271     matrix(2, 0) = rs;
00272     matrix(2, 1) = 0.0;
00273     matrix(2, 2) = rc;
00274     matrix(2, 3) = 0.0;
00275     matrix(2, 4) = 0.0;
00276 
00277     matrix(3, 0) = 0.0;
00278     matrix(3, 1) = 0.0;
00279     matrix(3, 2) = 0.0;
00280     matrix(3, 3) = 1.0;
00281     matrix(3, 4) = 0.0;
00282 
00283     matrix(4, 0) = 0.0;
00284     matrix(4, 1) = 0.0;
00285     matrix(4, 2) = 0.0;
00286     matrix(4, 3) = 0.0;
00287     matrix(4, 4) = 1.0;
00288 
00289         return matrix;
00290 }
00291 
00292 /**
00293  *      @brief Rotate about the z (blue) axis.
00294  */
00295 template<class Matrix5x5>
00296 Matrix5x5 zRotateMatrix(
00297         const typename Matrix5x5::value_type rs,
00298         const typename Matrix5x5::value_type rc )
00299 {
00300     Matrix5x5 matrix(5, 5);
00301 
00302     matrix(0, 0) = rc;
00303     matrix(0, 1) = rs;
00304     matrix(0, 2) = 0.0;
00305     matrix(0, 3) = 0.0;
00306     matrix(0, 4) = 0.0;
00307 
00308     matrix(1, 0) = -rs;
00309     matrix(1, 1) = rc;
00310     matrix(1, 2) = 0.0;
00311     matrix(1, 3) = 0.0;
00312     matrix(1, 4) = 0.0;
00313 
00314     matrix(2, 0) = 0.0;
00315     matrix(2, 1) = 0.0;
00316     matrix(2, 2) = 1.0;
00317     matrix(2, 3) = 0.0;
00318     matrix(2, 4) = 0.0;
00319 
00320     matrix(3, 0) = 0.0;
00321     matrix(3, 1) = 0.0;
00322     matrix(3, 2) = 0.0;
00323     matrix(3, 3) = 1.0;
00324     matrix(3, 4) = 0.0;
00325 
00326     matrix(4, 0) = 0.0;
00327     matrix(4, 1) = 0.0;
00328     matrix(4, 2) = 0.0;
00329     matrix(4, 3) = 0.0;
00330     matrix(4, 4) = 1.0;
00331         
00332         return matrix;
00333 }
00334 
00335 /**
00336  * @brief Shear z using x and y.
00337  */
00338 template<class Matrix5x5>
00339 Matrix5x5 zShearMatrix(
00340         const typename Matrix5x5::value_type dx,
00341         const typename Matrix5x5::value_type dy )
00342 {
00343     Matrix5x5 matrix(5, 5);
00344 
00345     matrix(0, 0) = 1.0;
00346     matrix(0, 1) = 0.0;
00347     matrix(0, 2) = dx;
00348     matrix(0, 3) = 0.0;
00349     matrix(0, 4) = 0.0;
00350 
00351     matrix(1, 0) = 0.0;
00352     matrix(1, 1) = 1.0;
00353     matrix(1, 2) = dy;
00354     matrix(1, 3) = 0.0;
00355     matrix(1, 4) = 0.0;
00356 
00357     matrix(2, 0) = 0.0;
00358     matrix(2, 1) = 0.0;
00359     matrix(2, 2) = 1.0;
00360     matrix(2, 3) = 0.0;
00361     matrix(2, 4) = 0.0;
00362 
00363     matrix(3, 0) = 0.0;
00364     matrix(3, 1) = 0.0;
00365     matrix(3, 2) = 0.0;
00366     matrix(3, 3) = 1.0;
00367     matrix(3, 4) = 0.0;
00368 
00369     matrix(4, 0) = 0.0;
00370     matrix(4, 1) = 0.0;
00371     matrix(4, 2) = 0.0;
00372     matrix(4, 3) = 0.0;
00373     matrix(4, 4) = 1.0;
00374         
00375         return matrix;
00376 }
00377 
00378 /**
00379  * @brief Simple hue rotation. This changes luminance.
00380  */
00381 template<class Matrix5x5>
00382 Matrix5x5 simpleHueRotateMatrix( const typename Matrix5x5::value_type rotate )
00383 {
00384         typedef typename Matrix5x5::value_type T;
00385         using boost::numeric::ublas::prec_prod;
00386 
00387         Matrix5x5 matrix = boost::numeric::ublas::identity_matrix<T>( 5 );
00388 
00389         /* rotate the grey vector into positive Z */
00390         static const T xmag = sqrt(2.0);
00391         static const T xrs = 1.0/xmag;
00392         static const T xrc = 1.0/xmag;
00393         matrix = prec_prod( matrix,
00394                 xRotateMatrix<Matrix5x5>( xrs, xrc )
00395                 );
00396 
00397         static const T ymag = sqrt(3.0);
00398         static const T yrs = -1.0/ymag;
00399         static const T yrc = sqrt(2.0)/ymag;
00400         
00401         matrix = prec_prod( matrix,
00402                 yRotateMatrix<Matrix5x5>( yrs, yrc )
00403                 );
00404 
00405         /* rotate the hue */
00406         const T zrs = std::sin( rotate * boost::math::constants::pi<T>() / 180.0 );
00407         const T zrc = std::cos( rotate * boost::math::constants::pi<T>() / 180.0 );
00408         matrix = prec_prod( matrix,
00409                 zRotateMatrix( zrs, zrc )
00410                 );
00411 
00412         /* rotate the grey vector back into place */
00413         matrix = prec_prod( matrix,
00414                 yRotateMatrix<Matrix5x5>( -yrs, yrc )
00415                 );
00416         matrix = prec_prod( matrix,
00417                 xRotateMatrix<Matrix5x5>( -xrs, xrc )
00418                 );
00419         
00420         return matrix;
00421 }
00422 
00423 
00424 /**
00425  * @brief Simple hue rotation. This changes luminance.
00426  */
00427 template<class Matrix5x5>
00428 void xformpnt(
00429         const Matrix5x5& matrix,
00430         const typename Matrix5x5::value_type x,
00431         const typename Matrix5x5::value_type y,
00432         const typename Matrix5x5::value_type z,
00433               typename Matrix5x5::value_type* tx,
00434               typename Matrix5x5::value_type* ty,
00435               typename Matrix5x5::value_type* tz )
00436 {
00437         *tx = x*matrix(0, 0) + y*matrix(1, 0) + z*matrix(2, 0) + matrix(4, 0);
00438         *ty = x*matrix(0, 1) + y*matrix(1, 1) + z*matrix(2, 1) + matrix(4, 1);
00439         *tz = x*matrix(0, 2) + y*matrix(1, 2) + z*matrix(2, 2) + matrix(4, 2);
00440 }
00441 
00442 /**
00443  * @brief rotate the hue, while maintaining luminance.
00444  */
00445 template<class Matrix5x5>
00446 Matrix5x5 hueRotateMatrix( const typename Matrix5x5::value_type rotate )
00447 {
00448         typedef typename Matrix5x5::value_type T;
00449         using boost::numeric::ublas::prec_prod;
00450         
00451         Matrix5x5 matrix = boost::numeric::ublas::identity_matrix<T>( 5 );
00452 
00453         // rotate the grey vector into positive Z
00454         const T xmag = std::sqrt(2.0);
00455         const T xrs = 1.0/xmag;
00456         const T xrc = 1.0/xmag;
00457         matrix = prec_prod( matrix,
00458                 xRotateMatrix<Matrix5x5>( xrs, xrc ) );
00459 
00460         const T ymag = std::sqrt(3.0);
00461         const T yrs = -1.0/ymag;
00462         const T yrc = std::sqrt(2.0)/ymag;
00463         matrix = prec_prod( matrix,
00464                 yRotateMatrix<Matrix5x5>( yrs, yrc ) );
00465 
00466         T lx;
00467         T ly;
00468         T lz;
00469 
00470         // shear the space to make the luminance plane horizontal
00471         xformpnt( matrix, s_redLum, s_greenLum, s_blueLum, &lx, &ly, &lz );
00472 
00473         BOOST_ASSERT( lz != 0 );
00474         const T zsx = lx/lz;
00475         const T zsy = ly/lz;
00476         matrix = prec_prod( matrix,
00477                 zShearMatrix<Matrix5x5>( zsx, zsy )
00478                 );
00479 
00480         // rotate the hue
00481         const T zrs = std::sin(rotate * boost::math::constants::pi<T>() / 180.0);
00482         const T zrc = std::cos(rotate * boost::math::constants::pi<T>() / 180.0);
00483         matrix = prec_prod( matrix,
00484                 zRotateMatrix<Matrix5x5>( zrs, zrc )
00485                 );
00486 
00487         // unshear the space to put the luminance plane back
00488         matrix = prec_prod( matrix,
00489                 zShearMatrix<Matrix5x5>( -zsx, -zsy )
00490                 );
00491 
00492         // rotate the grey vector back into place
00493         matrix = prec_prod( matrix,
00494                 yRotateMatrix<Matrix5x5>( -yrs, yrc )
00495                 );
00496         matrix = prec_prod( matrix,
00497                 xRotateMatrix<Matrix5x5>( -xrs, xrc )
00498                 );
00499 
00500         return matrix;
00501 }
00502 
00503 
00504 }
00505 }
00506 
00507 #endif
00508