TuttleOFX  1
ColorTransferProcess.tcc
Go to the documentation of this file.
00001 #include "ColorTransferProcess.hpp"
00002 #include "ColorTransferPlugin.hpp"
00003 
00004 #include <tuttle/plugin/global.hpp>
00005 #include <tuttle/plugin/exceptions.hpp>
00006 #include <tuttle/plugin/param/gilColor.hpp>
00007 
00008 #include <terry/color/transfer.hpp>
00009 #include <terry/algorithm/transform_pixels_progress.hpp>
00010 #include <terry/numeric/operations.hpp>
00011 #include <terry/numeric/assign.hpp>
00012 #include <terry/numeric/init.hpp>
00013 #include <terry/numeric/assign.hpp>
00014 #include <terry/numeric/sqrt.hpp>
00015 #include <terry/numeric/operations_assign.hpp>
00016 #include <terry/globals.hpp>
00017 
00018 #include <boost/units/pow.hpp>
00019 #include <boost/mpl/vector.hpp>
00020 #include <boost/mpl/erase.hpp>
00021 #include <boost/mpl/find.hpp>
00022 #include <boost/mpl/if.hpp>
00023 #include <boost/static_assert.hpp>
00024 
00025 #include <boost/numeric/ublas/vector.hpp>
00026 
00027 namespace tuttle {
00028 namespace plugin {
00029 namespace colorTransfer {
00030 
00031 using namespace terry;
00032 using namespace terry::color;
00033 using namespace terry::color::transfer;
00034 using namespace terry::numeric;
00035 
00036 template< typename Pixel, typename CPixel >
00037 CPixel getPixel( const Pixel& p, const EColorspace eColorspace )
00038 {
00039         switch( eColorspace )
00040         {
00041                 case eColorspaceNone:
00042                 {
00043                         CPixel cp;
00044                         pixel_assigns_t<Pixel,CPixel>()( p, cp );
00045                         return cp;
00046                 }
00047                 case eColorspaceLMS:
00048                 {
00049                         return pixel_rgb_to_lms_t<Pixel, CPixel>()( p );
00050                 }
00051                 case eColorspaceLab:
00052                 {
00053                         return pixel_rgb_to_lab_t<Pixel, CPixel>()( p );
00054                 }
00055         }
00056         BOOST_ASSERT(false);
00057         CPixel cp;
00058         pixel_assigns_t<Pixel,CPixel>()( p, cp );
00059         return cp;
00060 }
00061 
00062 template< typename Pixel, typename CPixel >
00063 CPixel setPixel( const Pixel& p, const EColorspace eColorspace )
00064 {
00065         switch( eColorspace )
00066         {
00067                 case eColorspaceNone:
00068                 {
00069                         CPixel cp;
00070                         pixel_assigns_t<CPixel,Pixel>()( p, cp );
00071                         return cp;
00072                 }
00073                 case eColorspaceLMS:
00074                 {
00075                         return pixel_lms_to_rgb_t<Pixel, CPixel>()( p );
00076                 }
00077                 case eColorspaceLab:
00078                 {
00079                         return pixel_lab_to_rgb_t<Pixel, CPixel>()( p );
00080                 }
00081         }
00082         BOOST_ASSERT(false);
00083         CPixel cp;
00084         pixel_assigns_t<Pixel,CPixel>()( p, cp );
00085         return cp;
00086 }
00087 
00088 template<class View>
00089 struct ColorParams
00090 {
00091         typedef typename View::value_type Pixel;
00092         Pixel _srcAverage, _dstAverage, _deviationRatio;
00093         EColorspace _eColorspace;
00094         
00095         ColorParams( const Pixel& srcAverage, const Pixel& dstAverage, const Pixel& deviationRatio, const EColorspace eColorspace )
00096         {
00097                 pixel_assigns_t<Pixel, Pixel>()( deviationRatio, _deviationRatio );
00098                 pixel_assigns_t<Pixel, Pixel>()( srcAverage, _srcAverage );
00099                 pixel_assigns_t<Pixel, Pixel>()( dstAverage, _dstAverage );
00100                 _eColorspace = eColorspace;
00101 
00102 //              TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( _deviationRatio, red_t() ) );
00103 //              TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( _srcAverage, red_t() ) );
00104 //              TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( _dstAverage, red_t() ) );
00105         }
00106 
00107         Pixel operator( )(const Pixel& p ) const
00108         {
00109 //              Pixel p2; pixel_assigns_t<Pixel, Pixel > ( )( p, p2 );
00110                 // RGB to LAB
00111                 Pixel p2 = getPixel<Pixel, Pixel>( p, _eColorspace );
00112 
00113                 pixel_minus_assign_t<Pixel, Pixel > ( )( _srcAverage, p2 );
00114                 p2 = pixel_multiplies_t<Pixel, Pixel, Pixel > ( )( _deviationRatio, p2 );
00115                 pixel_plus_assign_t<Pixel, Pixel > ( )( _dstAverage, p2 );
00116 
00117                 // LAB to RGB
00118                 return setPixel<Pixel, Pixel>( p2, _eColorspace );
00119         }
00120 };
00121 
00122 
00123 template<class View>
00124 ColorTransferProcess<View>::ColorTransferProcess( ColorTransferPlugin &effect )
00125 : ImageGilFilterProcessor<View>( effect, eImageOrientationIndependant )
00126 , _plugin( effect )
00127 {
00128 }
00129 
00130 template<class View>
00131 void ColorTransferProcess<View>::computeAverage( const View& image, Pixel& average, Pixel& deviation, const EColorspace& eColorspace )
00132 {
00133         typedef typename color_space_type<View>::type Colorspace;
00134         typedef pixel<boost::gil::bits64f, layout<Colorspace> > CPixel;
00135         CPixel sumAverage, sumDeviation;
00136         pixel_zeros_t<CPixel>()( sumAverage );
00137         pixel_zeros_t<CPixel>()( sumDeviation );
00138 //      const std::size_t nbPixels = image.width( ) * image.height( );
00139 
00140         // Average
00141         CPixel cAverage;
00142         pixel_zeros_t<CPixel>()( cAverage );
00143         for( std::ssize_t y = 0; y < image.height( ); ++y )
00144         {
00145                 CPixel sumAverageLine;
00146                 pixel_zeros_t<CPixel>()( sumAverageLine );
00147                 typename View::x_iterator src_it = image.x_at( 0, y );
00148                 for( std::ssize_t x = 0; x < image.width( ); ++x, ++src_it )
00149                 {
00150 //                      CPixel pix; pixel_assigns_t<Pixel, CPixel>()( * src_it, pix );
00151                         CPixel pix = getPixel<Pixel, CPixel>( *src_it, eColorspace );
00152                         pixel_plus_assign_t<CPixel, CPixel>()( pix, sumAverageLine );
00153                 }
00154                 pixel_divides_scalar_assign_t<double, CPixel>()( image.width(), sumAverageLine );
00155                 pixel_plus_assign_t<CPixel>()( sumAverageLine, cAverage );
00156         }
00157         pixel_divides_scalar_assign_t<double, CPixel>()( image.height(), cAverage );
00158         pixel_assigns_t<CPixel, Pixel>()( cAverage, average );
00159 //      average = pixel_divides_scalar_t<CPixel, double, Pixel>()( sumAverage, nbPixels );
00160 
00161         CPixel cDeviation;
00162         pixel_zeros_t<CPixel>()( cDeviation );
00163         // Standard deviation
00164         for( std::ssize_t y = 0; y < image.height( ); ++y )
00165         {
00166                 CPixel sumDeviationLine;
00167                 pixel_zeros_t<CPixel>()( sumDeviationLine );
00168                 typename View::x_iterator src_it = image.x_at( 0, y );
00169                 for( std::ssize_t x = 0; x < image.width(); ++x, ++src_it )
00170                 {
00171 //                      CPixel pix; pixel_assigns_t<Pixel, CPixel>()( *src_it, pix );
00172                         CPixel pix = getPixel<Pixel, CPixel>( *src_it, eColorspace );
00173                         pixel_minus_assign_t<CPixel>()( cAverage, pix );
00174                         pix = pixel_pow_t<CPixel, 2>()( pix );
00175                         pixel_plus_assign_t<CPixel>()( pix, sumDeviationLine );
00176                 }
00177                 pixel_plus_assign_t<CPixel>()(
00178                                 pixel_divides_scalar_t<CPixel, double>()( sumDeviationLine, image.width() ),
00179                                 cDeviation
00180                         );
00181         }
00182         pixel_divides_scalar_assign_t<double, CPixel>()( image.height(), cDeviation );
00183         deviation = pixel_sqrt_t<CPixel,Pixel>()( cDeviation );
00184 }
00185 
00186 template<class View>
00187 void ColorTransferProcess<View>::setup( const OFX::RenderArguments& args )
00188 {
00189         ImageGilFilterProcessor<View>::setup( args ); // Call parent class setup
00190         _params = _plugin.getProcessParams( args.renderScale ); // Retrieve plugin parameters
00191 
00192         // srcRef initialization
00193         if( _plugin._clipSrcRef->isConnected() )
00194         {
00195                 this->_srcRef.reset( _plugin._clipSrcRef->fetchImage( args.time ) );
00196                 if( !this->_srcRef.get( ) )
00197                 {
00198                         BOOST_THROW_EXCEPTION( exception::ImageNotReady( ) );
00199                 }
00200                 if( this->_srcRef->getRowDistanceBytes( ) == 0 )
00201                 {
00202                         BOOST_THROW_EXCEPTION( exception::WrongRowBytes( ) );
00203                 }
00204                 
00205                 if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00206                 {
00207                         // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00208                         _srcRefPixelRod   = _plugin._clipSrcRef->getPixelRod( args.time, args.renderScale );
00209                 }
00210                 else
00211                 {
00212                         _srcRefPixelRod = _srcRef->getRegionOfDefinition();
00213                 }
00214                 this->_srcRefView = this->getView( this->_srcRef.get(), _srcRefPixelRod );
00215         }
00216         else
00217         {
00218                 this->_srcRefPixelRod = this->_srcPixelRod;
00219                 this->_srcRefView     = this->_srcView;
00220         }
00221 
00222         // dstRef initialization
00223         this->_dstRef.reset( _plugin._clipDstRef->fetchImage( args.time ) );
00224         if( !this->_dstRef.get( ) )
00225         {
00226                 BOOST_THROW_EXCEPTION( exception::ImageNotReady( ) );
00227         }
00228         if( this->_dstRef->getRowDistanceBytes( ) == 0 )
00229         {
00230                 BOOST_THROW_EXCEPTION( exception::WrongRowBytes( ) );
00231         }
00232         
00233         if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00234         {
00235                 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00236                 _dstRefPixelRod   = _plugin._clipDstRef->getPixelRod( args.time, args.renderScale );
00237         }
00238         else
00239         {
00240                 _dstRefPixelRod = _dstRef->getRegionOfDefinition();
00241         }
00242         this->_dstRefView = this->getView( this->_dstRef.get( ), _dstRefPixelRod );
00243 
00244         // analyse srcRef and dstRef
00245         Pixel srcRefDeviation, dstRefDeviation;
00246         computeAverage( this->_srcRefView, _srcRefAverage, srcRefDeviation, _params._colorspace );
00247         computeAverage( this->_dstRefView, _dstRefAverage, dstRefDeviation, _params._colorspace );
00248         //TUTTLE_LOG_VAR4( TUTTLE_INFO, _srcRefAverage[0], _srcRefDeviation[0], _dstRefAverage[0], _dstRefDeviation[0]);
00249         
00250         TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( dstRefDeviation, red_t() ) );
00251         TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( dstRefDeviation, green_t() ) );
00252         TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( dstRefDeviation, blue_t() ) );
00253         TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( srcRefDeviation, red_t() ) );
00254         TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( srcRefDeviation, green_t() ) );
00255         TUTTLE_TLOG_VAR( TUTTLE_INFO, get_color( srcRefDeviation, blue_t() ) );
00256 
00257         // now analyse the differences
00258         pixel_zeros_t<Pixel>()( _deviationRatio );
00259         _deviationRatio = pixel_divides_t<Pixel, Pixel, Pixel>()( dstRefDeviation, srcRefDeviation );
00260 
00261         // modify the values with coefficient parameters
00262         if( _params._averageCoef != 1.0 )
00263         {
00264                 // recompute the dstAverage weighted by the averageCoef
00265                 // - scale the vector from srcAverage to dstAverage
00266                 Pixel vecAverage = pixel_minus_t<Pixel, Pixel, Pixel>()( _dstRefAverage, _srcRefAverage );
00267                 pixel_multiplies_scalar_assign_t<double, Pixel>()( _params._averageCoef, vecAverage );
00268                 // - apply this new vector to srcAverage
00269                 _dstRefAverage = pixel_plus_t<Pixel, Pixel, Pixel>()( _srcRefAverage, vecAverage );
00270         }
00271         if( _params._dynamicCoef != 1.0 )
00272         {
00273                 // scale the deviationRatio, centered around 1, with the dynamicCoef
00274                 Pixel ones;
00275                 pixel_ones_t<Pixel>()( ones );
00276                 pixel_minus_assign_t<Pixel, Pixel>()( ones, _deviationRatio );
00277                 pixel_multiplies_scalar_assign_t<double, Pixel>()( _params._dynamicCoef, _deviationRatio );
00278                 pixel_plus_assign_t<Pixel, Pixel>()( ones, _deviationRatio );
00279         }
00280 }
00281 
00282 /**
00283  * @brief Function called by rendering thread each time a process must be done.
00284  * @param[in] procWindowRoW  Processing window
00285  */
00286 template<class View>
00287 void ColorTransferProcess<View>::multiThreadProcessImages( const OfxRectI& procWindowRoW )
00288 {
00289         using namespace terry;
00290         const OfxRectI procWindowOutput = this->translateRoWToOutputClipCoordinates( procWindowRoW );
00291         const OfxRectI procWindowSrc = translateRegion( procWindowRoW, this->_srcPixelRod );
00292         OfxPointI procWindowSize = { procWindowRoW.x2 - procWindowRoW.x1,
00293                                                                  procWindowRoW.y2 - procWindowRoW.y1 };
00294         View src = subimage_view( this->_srcView, procWindowSrc.x1, procWindowSrc.y1,
00295                                                           procWindowSize.x, procWindowSize.y );
00296         View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1,
00297                                                           procWindowSize.x, procWindowSize.y );
00298 
00299         // fill dst: modify src using analyse of srcRef and dstRef differences
00300         terry::algorithm::transform_pixels_progress( src, dst, ColorParams<View>( _srcRefAverage, _dstRefAverage, _deviationRatio, _params._colorspace ), *this );
00301 }
00302 
00303 }
00304 }
00305 }
00306 
00307