TuttleOFX  1
DiffProcess.tcc
Go to the documentation of this file.
00001 #include "DiffPlugin.hpp"
00002 
00003 #include <climits>
00004 
00005 #include <tuttle/plugin/numeric/rectOp.hpp>
00006 #include <terry/globals.hpp>
00007 #include <terry/basic_colors.hpp>
00008 
00009 namespace tuttle {
00010 namespace plugin {
00011 namespace quality {
00012 
00013 template<class View>
00014 DiffProcess<View>::DiffProcess( DiffPlugin& instance )
00015         : ImageGilProcessor<View>( instance, eImageOrientationIndependant )
00016         , _plugin( instance )
00017 {
00018         this->setNoMultiThreading();
00019 }
00020 
00021 template<class View>
00022 void DiffProcess<View>::setup( const OFX::RenderArguments& args )
00023 {
00024         ImageGilProcessor<View>::setup( args );
00025 
00026         _params = _plugin.getProcessParams( );
00027 
00028         // sources view
00029         // clip A
00030         _srcA.reset( _plugin._clipSrcA->fetchImage( args.time ) );
00031         if( !_srcA.get() )
00032                 BOOST_THROW_EXCEPTION( exception::ImageNotReady() );
00033         if( _srcA->getRowDistanceBytes() == 0 )
00034                 BOOST_THROW_EXCEPTION( exception::WrongRowBytes() );
00035         
00036         if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00037         {
00038                 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00039                 _srcPixelRodA   = _plugin._clipSrcA->getPixelRod( args.time, args.renderScale );
00040         }
00041         else
00042         {
00043                 _srcPixelRodA = _srcA->getRegionOfDefinition();
00044         }
00045         this->_srcViewA = this->getView( _srcA.get(), _srcPixelRodA );
00046 
00047         // clip B
00048         _srcB.reset( _plugin._clipSrcB->fetchImage( args.time ) );
00049         if( !_srcB.get() )
00050                 BOOST_THROW_EXCEPTION( exception::ImageNotReady() );
00051         if( _srcB->getRowDistanceBytes() == 0 )
00052                 BOOST_THROW_EXCEPTION( exception::WrongRowBytes() );
00053         
00054         if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00055         {
00056                 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00057                 _srcPixelRodB   = _plugin._clipSrcB->getPixelRod( args.time, args.renderScale );
00058         }
00059         else
00060         {
00061                 _srcPixelRodB = _srcB->getRegionOfDefinition();
00062         }
00063         this->_srcViewB = this->getView( _srcB.get(), _srcPixelRodB );
00064 
00065         // Make sure bit depths are the same
00066         if( _srcA->getPixelDepth() != this->_dst->getPixelDepth() ||
00067             _srcB->getPixelDepth() != this->_dst->getPixelDepth() )
00068         {
00069                 BOOST_THROW_EXCEPTION( exception::BitDepthMismatch()
00070                                         << exception::user( "Diff: bit depth mismatch" ) );
00071         }
00072         if( _srcA->getPixelComponents() != this->_dst->getPixelComponents() ||
00073             _srcB->getPixelComponents() != this->_dst->getPixelComponents() )
00074         {
00075                 BOOST_THROW_EXCEPTION( exception::InputMismatch()
00076                                         << exception::user( "Diff: components mismatch" ) );
00077         }
00078 
00079 }
00080 
00081 /**
00082  * @brief Function called by rendering thread each time a process must be done.
00083  * @param[in] procWindowRoW  Processing window in RoW
00084  */
00085 template<class View>
00086 void DiffProcess<View>::multiThreadProcessImages( const OfxRectI& procWindowRoW )
00087 {
00088         using namespace boost::gil;
00089         OfxPointI procWindowSize = {
00090                 procWindowRoW.x2 - procWindowRoW.x1,
00091                 procWindowRoW.y2 - procWindowRoW.y1
00092         };
00093         View srcViewA = subimage_view( this->_srcViewA,
00094                                        procWindowRoW.x1 - _srcPixelRodA.x1,
00095                                        procWindowRoW.y1 - _srcPixelRodA.y1,
00096                                        procWindowSize.x,
00097                                        procWindowSize.y );
00098         View srcViewB = subimage_view( this->_srcViewB,
00099                                        procWindowRoW.x1 - _srcPixelRodB.x1,
00100                                        procWindowRoW.y1 - _srcPixelRodB.y1,
00101                                        procWindowSize.x,
00102                                        procWindowSize.y );
00103         View dstView = subimage_view( this->_dstView,
00104                                       procWindowRoW.x1 - this->_dstPixelRod.x1,
00105                                       procWindowRoW.y1 - this->_dstPixelRod.y1,
00106                                       procWindowSize.x,
00107                                       procWindowSize.y );
00108 
00109         rgba32f_pixel_t paramRgbaValue (0,0,0,0);
00110 
00111         switch( _params.measureFunction )
00112         {
00113                 case eMeasureFunctionMSE:
00114                         color_convert( mse( srcViewA, srcViewB, dstView ), paramRgbaValue );
00115                         break;
00116                 case eMeasureFunctionPSNR:
00117                         color_convert( psnr( srcViewA, srcViewB, dstView ), paramRgbaValue );
00118                         break;
00119                 case eMeasureFunctionSSIM: break;
00120         }
00121 
00122         _plugin._qualityMesure->setValueAtTime( this->_renderArgs.time,
00123                                                 get_color( paramRgbaValue, red_t() ),
00124                                                 get_color( paramRgbaValue, green_t() ),
00125                                                 get_color( paramRgbaValue, blue_t() ),
00126                                                 get_color( paramRgbaValue, alpha_t() )
00127                                                 );
00128 }
00129 
00130 
00131 template<class View>
00132 template<class SView>
00133 boost::gil::pixel<boost::gil::bits32f, boost::gil::layout<typename boost::gil::color_space_type<SView>::type> >
00134 DiffProcess<View>::psnr( const SView& v1, const SView& v2, const SView& dst )
00135 {
00136         typedef boost::gil::pixel<boost::gil::bits32f, boost::gil::layout<typename boost::gil::color_space_type<SView>::type> > Pixel32F;
00137         typedef typename boost::gil::channel_type<Pixel32F>::type Value32F;
00138         typedef typename boost::gil::channel_type<typename SView::value_type>::type SValueType;
00139 
00140         size_t d      = (size_t)( std::pow( 2.0, sizeof( SValueType ) * 8.0 ) ) - 1;
00141         size_t d2     = d * d;
00142         Pixel32F psnr = mse( v1, v2, dst, true );
00143         for( int i = 0; i < boost::gil::num_channels<Pixel32F>::type::value; ++i )
00144         {
00145                 float p = (float)d2 -( (float)d2 / psnr[i] );
00146                 if( p > std::numeric_limits<float>::epsilon( ) )
00147                         psnr[i] = Value32F( 10.0 * std::log10( p ) );
00148                 else
00149                         psnr[i] = 0;
00150 
00151         }
00152         return psnr;
00153 }
00154 
00155 template<class View>
00156 template<class SView>
00157 boost::gil::pixel<boost::gil::bits32f, boost::gil::layout<typename boost::gil::color_space_type<SView>::type> >
00158 DiffProcess<View>::mse( const SView& v1, const SView& v2, const SView& dst, bool outputIsPsnr )
00159 {
00160         using namespace terry;
00161         typedef boost::gil::pixel<boost::gil::bits32f, boost::gil::layout<typename boost::gil::color_space_type<SView>::type> > Pixel32F;
00162         typedef typename boost::gil::channel_type<Pixel32F>::type Value32F;
00163         typedef typename boost::gil::channel_type<typename SView::value_type>::type SValueType;
00164 
00165         Pixel32F veqm = get_black<Pixel32F>();
00166         int w         = v1.width();
00167         int h         = v1.height();
00168 
00169         size_t d      = (size_t)( std::pow( 2.0, sizeof( SValueType ) * 8.0 ) ) - 1;
00170         size_t d2     = d * d;
00171 
00172         for( typename SView::y_coord_t y = 0; y < h; ++y )
00173         {
00174                 typename SView::x_iterator itA = v1.row_begin( y );
00175                 typename SView::x_iterator itB = v2.row_begin( y );
00176                 typename SView::x_iterator itD = dst.row_begin( y );
00177 
00178                 for( typename SView::x_coord_t x = 0; x < w; ++x )
00179                 {
00180                         for( int i = 0; i < boost::gil::num_channels<Pixel32F>::type::value; ++i )
00181                         {
00182                                 Value32F diff = ( Value32F ) std::abs( double( ( *itA )[i] - ( *itB )[i]) );
00183 
00184                                 if( outputIsPsnr )
00185                                 {
00186                                         float p = (float)d2 / diff;
00187                                         if( p > std::numeric_limits<float>::epsilon( ) )
00188                                                 ( *itD )[i] = Value32F( 10.0 * std::log10( p ) );
00189                                         else
00190                                                 ( *itD )[i] = 0;
00191                                 }
00192                                 else
00193                                 {
00194                                         ( *itD )[i] = diff;
00195                                 }
00196                                 veqm[i]    += diff * diff;
00197                         }
00198                         ++itA;
00199                         ++itB;
00200                         ++itD;
00201                 }
00202                 this->progressUpdate( (float)(y+1)/h );
00203         }
00204 
00205         for( int i = 0; i < boost::gil::num_channels<Pixel32F>::type::value; ++i )
00206         {
00207                 veqm[i] /= ( w * h );
00208         }
00209         return veqm;
00210 }
00211 
00212 }
00213 }
00214 }