TuttleOFX
1
|
00001 #include "PushPixelPlugin.hpp" 00002 #include "PushPixelDefinitions.hpp" 00003 #include "PushPixelProcess.hpp" 00004 00005 #include <tuttle/plugin/exceptions.hpp> 00006 #include <tuttle/plugin/memory/OfxAllocator.hpp> 00007 #include <tuttle/common/math/rectOp.hpp> 00008 00009 #include <terry/globals.hpp> 00010 #include <terry/basic_colors.hpp> 00011 #include <terry/channel_view.hpp> 00012 #include <terry/filter/motionVectors.hpp> 00013 #include <terry/filter/convolve.hpp> 00014 #include <terry/sampler/all.hpp> 00015 00016 #include <boost/gil/utilities.hpp> 00017 #include <boost/mpl/if.hpp> 00018 #include <boost/type_traits/is_floating_point.hpp> 00019 00020 namespace tuttle { 00021 namespace plugin { 00022 namespace pushPixel { 00023 00024 template<class View> 00025 PushPixelProcess<View>::PushPixelProcess( PushPixelPlugin &effect ) 00026 : ImageGilFilterProcessor<View>( effect, eImageOrientationIndependant ) 00027 , _plugin( effect ) 00028 { 00029 _clipMask = effect.fetchClip( kClipMask ); 00030 _clipMaskConnected = this->_clipMask->isConnected(); 00031 } 00032 00033 template <class View> 00034 void PushPixelProcess<View>::setup( const OFX::RenderArguments& args ) 00035 { 00036 ImageGilFilterProcessor<View>::setup( args ); 00037 00038 // mask view 00039 if( _clipMaskConnected ) 00040 { 00041 this->_mask.reset( _clipMask->fetchImage( args.time ) ); 00042 if( !this->_mask.get( ) ) 00043 BOOST_THROW_EXCEPTION( exception::ImageNotReady() ); 00044 if( this->_mask->getRowDistanceBytes( ) <= 0 ) 00045 BOOST_THROW_EXCEPTION( exception::WrongRowBytes() ); 00046 this->_maskView = this->getView( this->_mask.get(), _clipMask->getPixelRod(args.time, args.renderScale) ); 00047 00048 if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" ) 00049 { 00050 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds 00051 _maskPixelRod = _clipMask->getPixelRod( args.time, args.renderScale ); 00052 } 00053 else 00054 { 00055 _maskPixelRod = _mask->getRegionOfDefinition(); 00056 } 00057 } 00058 00059 _params = _plugin.getProcessParams( args.renderScale ); 00060 00061 /* 00062 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_renderArgs.fieldToRender ); 00063 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_renderArgs.renderScale ); 00064 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_renderArgs.renderWindow ); 00065 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_renderArgs.time ); 00066 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_srcPixelRod ); 00067 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_dstPixelRod ); 00068 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_srcView.width(), this->_srcView.height() ); 00069 TUTTLE_LOG_VAR( TUTTLE_INFO, this->_dstView.width(), this->_dstView.height() ); 00070 */ 00071 } 00072 00073 /** 00074 * @brief Function called by rendering thread each time a process must be done. 00075 * @param[in] procWindowRoW Processing window 00076 */ 00077 template<class View> 00078 void PushPixelProcess<View>::multiThreadProcessImages( const OfxRectI& procWindowRoW ) 00079 { 00080 using namespace boost::gil; 00081 using namespace terry; 00082 using namespace terry::filter; 00083 00084 typedef View MaskView; /// @todo add a MaskView template parameter... 00085 00086 const OfxRectI procWindowOutput = this->translateRoWToOutputClipCoordinates( procWindowRoW ); 00087 const OfxPointI procWindowSize = { 00088 procWindowRoW.x2 - procWindowRoW.x1, 00089 procWindowRoW.y2 - procWindowRoW.y1 00090 }; 00091 00092 // use View channel type if floating point else use bit32f 00093 typedef typename channel_mapping_type<View>::type Channel; 00094 typedef typename boost::mpl::if_< boost::is_floating_point<Channel>, 00095 Channel, 00096 bits32f>::type ChannelFloat; 00097 typedef pixel<ChannelFloat, gray_layout_t> PixelGray; 00098 typedef image<PixelGray, false> ImageGray; 00099 typedef typename ImageGray::view_t ViewGray; 00100 00101 OfxRectI usedMaskPixelRod; // the mask RoD (depending on the clip mask used) 00102 if( _clipMaskConnected ) 00103 usedMaskPixelRod = this->_maskPixelRod; 00104 else 00105 usedMaskPixelRod = this->_srcPixelRod; 00106 00107 const OfxRectI imgGradientWin = rectanglesIntersection( procWindowRoW, usedMaskPixelRod ); 00108 const bool partialGradient = (imgGradientWin != procWindowRoW); // if true we can only calculate a gradient for a subpart of the procWindow 00109 if( _params._output == eParamOutputMotionVectors && partialGradient ) 00110 { 00111 // fill the procWindow in black 00112 View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1, 00113 procWindowSize.x, 00114 procWindowSize.y ); 00115 Pixel fillingColor = get_black( this->_srcView ); 00116 boost::gil::fill_pixels( dst, fillingColor ); 00117 } 00118 // if gradient window is empty there is nothing to do 00119 // so simply copy source clip 00120 if( (imgGradientWin.y2 - imgGradientWin.y1 == 0) || 00121 (imgGradientWin.x2 - imgGradientWin.x1 == 0) ) 00122 { 00123 switch( _params._output ) 00124 { 00125 case eParamOutputMotionVectors: 00126 { 00127 BOOST_ASSERT( partialGradient == true ); 00128 // the output is already fill in black 00129 return; 00130 } 00131 case eParamOutputPushPixel: 00132 { 00133 const OfxRectI procWindowSrc = translateRegion( procWindowRoW, this->_srcPixelRod ); 00134 00135 View src = subimage_view( this->_srcView, procWindowSrc.x1, procWindowSrc.y1, 00136 procWindowSize.x, 00137 procWindowSize.y ); 00138 View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1, 00139 procWindowSize.x, 00140 procWindowSize.y ); 00141 copy_pixels( src, dst ); 00142 return; 00143 } 00144 } 00145 } 00146 00147 const OfxPointI imgGradientSize = { imgGradientWin.x2 - imgGradientWin.x1, 00148 imgGradientWin.y2 - imgGradientWin.y1 }; 00149 ImageGray xGradientImage( imgGradientSize.x, imgGradientSize.y ); 00150 ImageGray yGradientImage( imgGradientSize.x, imgGradientSize.y ); 00151 ViewGray xGradientView = view(xGradientImage); 00152 ViewGray yGradientView = view(yGradientImage); 00153 Point proc_mask_tl( imgGradientWin.x1 - usedMaskPixelRod.x1, imgGradientWin.y1 - usedMaskPixelRod.y1 ); 00154 00155 /* 00156 TUTTLE_LOG_VAR( TUTTLE_INFO, usedMaskPixelRod ); 00157 TUTTLE_LOG_VAR( TUTTLE_INFO, imgGradientWin ); 00158 TUTTLE_LOG_VAR( TUTTLE_INFO, procWindowRoW ); 00159 TUTTLE_LOG_VAR( TUTTLE_INFO, partialGradient ); 00160 TUTTLE_LOG_VAR( TUTTLE_INFO, procWindowOutput ); 00161 TUTTLE_LOG_VAR( TUTTLE_INFO, procWindowSize ); 00162 TUTTLE_LOG_VAR( TUTTLE_INFO, imgGradientSize ); 00163 TUTTLE_LOG_VAR( TUTTLE_INFO, proc_mask_tl ); 00164 */ 00165 00166 // compute motion vectors 00167 if( _clipMaskConnected ) 00168 { 00169 // choose the mask channel to use 00170 // alpha_t if the image contains an alpha channel like rgba 00171 // gray_t if grayscale image 00172 typedef typename MaskView::value_type MaskPixel; 00173 typedef typename boost::mpl::if_<contains_color<MaskPixel, alpha_t>, 00174 alpha_t, 00175 gray_t>::type MaskColorChannel; 00176 typedef channel_view_type<MaskColorChannel, MaskView> KthChannelView; 00177 typename KthChannelView::type channelMaskView = KthChannelView::make(this->_maskView); // gray or alpha channel 00178 00179 if( correlateMotionVectors<OfxAllocator, ViewGray, typename KthChannelView::type, Point, Scalar>( xGradientView, yGradientView, channelMaskView, proc_mask_tl, _params._kernelGaussianDerivative, _params._kernelGaussian, _params._boundary_option, this->getOfxProgress() ) ) 00180 return; 00181 } 00182 else 00183 { 00184 typedef channel_view_type<alpha_t, View> KthChannelView; 00185 typename KthChannelView::type channelMaskView = KthChannelView::make(this->_srcView); // alpha channel 00186 00187 if( correlateMotionVectors<OfxAllocator, ViewGray, typename KthChannelView::type, Point, Scalar>( xGradientView, yGradientView, channelMaskView, proc_mask_tl, _params._kernelGaussianDerivative, _params._kernelGaussian, _params._boundary_option, this->getOfxProgress() ) ) 00188 return; 00189 } 00190 00191 if( modifyVectors( xGradientView, yGradientView, this->_params._angle, _params._intensity, this->getOfxProgress() ) ) 00192 return; 00193 00194 // apply motion vectors 00195 switch( _params._output ) 00196 { 00197 case eParamOutputMotionVectors: 00198 { 00199 const OfxRectI gradientRodInSrc = translateRegion( imgGradientWin, this->_srcPixelRod ); 00200 // subdst is the part of dst for which we have motion vectors information 00201 View subdst = subimage_view( this->_dstView, 00202 gradientRodInSrc.x1, gradientRodInSrc.y1, 00203 imgGradientSize.x, imgGradientSize.y ); 00204 copy_and_convert_pixels( xGradientView, kth_channel_view<0>(subdst) ); // put x vectors 00205 if( this->progressForward( procWindowSize.y * 0.5 ) ) 00206 return; 00207 copy_and_convert_pixels( yGradientView, kth_channel_view<1>(subdst) ); // put y vectors 00208 if( this->progressForward( procWindowSize.y * 0.5 ) ) 00209 return; 00210 return; 00211 } 00212 case eParamOutputPushPixel: 00213 { 00214 switch( _params._interpolation ) 00215 { 00216 /// @todo add all interpolation methods 00217 case eParamInterpolationNearest: 00218 motionvectors_resample_pixels<sampler::nearest_neighbor_sampler>( 00219 this->_srcView, this->_srcPixelRod, 00220 xGradientView, yGradientView, imgGradientWin, 00221 this->_dstView, this->_dstPixelRod, 00222 procWindowRoW, 00223 sampler::eParamFilterOutBlack, 00224 this->getOfxProgress() ); 00225 return; 00226 case eParamInterpolationBilinear: 00227 motionvectors_resample_pixels<sampler::bilinear_sampler>( 00228 this->_srcView, this->_srcPixelRod, 00229 xGradientView, yGradientView, imgGradientWin, 00230 this->_dstView, this->_dstPixelRod, 00231 procWindowRoW, 00232 sampler::eParamFilterOutBlack, 00233 this->getOfxProgress() ); 00234 return; 00235 } 00236 TUTTLE_LOG_ERROR( "Interpolation method not recognize." ); 00237 return; 00238 } 00239 } 00240 } 00241 00242 } 00243 } 00244 }