TuttleOFX
1
|
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