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