TuttleOFX
1
|
00001 #include "ImageStatisticsPlugin.hpp" 00002 00003 #include <tuttle/plugin/global.hpp> 00004 #include <terry/globals.hpp> 00005 #include <tuttle/plugin/param/gilColor.hpp> 00006 #include <terry/typedefs.hpp> 00007 00008 #include <terry/numeric/operations.hpp> 00009 #include <terry/numeric/operations_assign.hpp> 00010 #include <terry/numeric/assign.hpp> 00011 #include <terry/numeric/assign_minmax.hpp> 00012 #include <terry/numeric/minmax.hpp> 00013 #include <terry/numeric/init.hpp> 00014 #include <terry/numeric/pow.hpp> 00015 #include <terry/numeric/sqrt.hpp> 00016 #include <boost/gil/extension/color/hsl.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 00023 /* 00024 namespace boost { 00025 namespace gil { 00026 00027 namespace details { 00028 00029 template <typename Colorspace, typename ChannelType> 00030 // models pixel concept 00031 struct without_alpha_channel_impl_t 00032 { 00033 typedef pixel<ChannelType, layout<Colorspace> > Pixel; 00034 typedef Pixel PixelR; 00035 00036 BOOST_STATIC_ASSERT( ( !contains_color<Pixel, alpha_t>::value ) ); 00037 00038 PixelR operator ()( const Pixel& src ) const 00039 { 00040 return src; 00041 } 00042 00043 }; 00044 00045 template <typename ChannelType> 00046 // models pixel concept 00047 struct without_alpha_channel_impl_t<rgba_t, ChannelType> 00048 { 00049 typedef rgba_t Colorspace; 00050 typedef pixel<ChannelType, layout<Colorspace> > Pixel; 00051 typedef pixel<ChannelType, layout<rgb_t> > PixelR; 00052 00053 BOOST_STATIC_ASSERT( ( contains_color<Pixel, alpha_t>::value ) ); 00054 00055 PixelR operator ()( const Pixel& src ) const 00056 { 00057 PixelR dst; 00058 00059 get_color( dst, red_t() ) = get_color( src, red_t() ); 00060 get_color( dst, green_t() ) = get_color( src, green_t() ); 00061 get_color( dst, blue_t() ) = get_color( src, blue_t() ); 00062 return dst; 00063 } 00064 00065 }; 00066 00067 } 00068 00069 template <typename Pixel> 00070 // models pixel concept 00071 struct without_alpha_channel_t 00072 { 00073 typedef typename channel_type<Pixel>::type ChannelType; 00074 typedef typename color_space_type<Pixel>::type Colorspace; 00075 00076 typedef details::without_alpha_channel_impl_t<Colorspace, ChannelType> Do; 00077 typedef typename Do::PixelR PixelR; 00078 00079 PixelR operator ()( const Pixel& src ) const 00080 { 00081 return Do() ( src ); 00082 } 00083 00084 }; 00085 00086 GIL_DEFINE_ALL_TYPEDEFS( 64, hsl ) 00087 GIL_DEFINE_ALL_TYPEDEFS( 64s, hsl ) 00088 GIL_DEFINE_ALL_TYPEDEFS( 64f, hsl ) 00089 00090 } 00091 } 00092 */ 00093 00094 namespace tuttle { 00095 namespace plugin { 00096 namespace imageStatistics { 00097 00098 template<typename T> 00099 T standard_deviation( const T v_sum, const T v_sum_p2, const std::size_t nb ) 00100 { 00101 using namespace boost::units; 00102 return std::sqrt( v_sum_p2 / nb - pow<2>( v_sum / nb ) ); 00103 } 00104 00105 template<typename Pixel> 00106 Pixel pixel_standard_deviation( const Pixel& v_sum, const Pixel& v_sum_p2, const std::size_t nb ) 00107 { 00108 using namespace boost::gil; 00109 Pixel res; 00110 for( int i = 0; i < num_channels<Pixel>::type::value; ++i ) 00111 { 00112 res[i] = standard_deviation( v_sum[i], v_sum_p2[i], nb ); 00113 } 00114 return res; 00115 } 00116 00117 /** 00118 * @brief In probability theory and statistics, kurtosis is a measure of the 00119 * "peakedness" of the probability distribution of a real-valued random variable. 00120 * 00121 * Higher kurtosis means more of the variance is the result of infrequent extreme deviations, 00122 * as opposed to frequent modestly sized deviations. 00123 */ 00124 template<typename T> 00125 T kurtosis( const T v_mean, const T v_standard_deviation, const T v_sum, const T v_sum_p2, const T v_sum_p3, const T v_sum_p4, const std::size_t nb ) 00126 { 00127 using namespace boost::units; 00128 return ( ( v_sum_p4 - 4.0 * v_mean * v_sum_p3 + 6.0 * pow<2>( v_mean ) * v_sum_p2 ) / nb - 3.0 * pow<4>( v_mean ) ) / 00129 ( pow<4>( v_standard_deviation ) ) - 3.0; 00130 } 00131 00132 template<typename Pixel> 00133 Pixel pixel_kurtosis( const Pixel& v_mean, const Pixel& v_standard_deviation, const Pixel& v_sum, const Pixel& v_sum_p2, const Pixel& v_sum_p3, const Pixel& v_sum_p4, const std::size_t nb ) 00134 { 00135 using namespace boost::gil; 00136 Pixel res; 00137 for( int i = 0; i < num_channels<Pixel>::type::value; ++i ) 00138 { 00139 res[i] = kurtosis( v_mean[i], v_standard_deviation[i], v_sum[i], v_sum_p2[i], v_sum_p3[i], v_sum_p4[i], nb ); 00140 } 00141 return res; 00142 } 00143 00144 /** 00145 * @brief In probability theory and statistics, skewness is a measure of the 00146 * asymmetry of the probability distribution of a real-valued random variable. 00147 * 00148 * A zero value indicates that the values are relatively evenly distributed 00149 * on both sides of the mean, typically but not necessarily implying 00150 * a symmetric distribution. 00151 */ 00152 template<typename T> 00153 T skewness( const T v_mean, const T v_standard_deviation, const T v_sum, const T v_sum_p2, const T v_sum_p3, const std::size_t nb ) 00154 { 00155 using namespace boost::units; 00156 return ( ( v_sum_p3 - 3.0 * v_mean * v_sum_p2 ) / nb + 2.0 * pow<3>( v_mean ) ) / pow<3>( v_standard_deviation ); 00157 } 00158 00159 template<typename Pixel> 00160 Pixel pixel_skewness( const Pixel& v_mean, const Pixel& v_standard_deviation, const Pixel& v_sum, const Pixel& v_sum_p2, const Pixel& v_sum_p3, const std::size_t nb ) 00161 { 00162 using namespace boost::gil; 00163 Pixel res; 00164 for( int i = 0; i < num_channels<Pixel>::type::value; ++i ) 00165 { 00166 res[i] = skewness( v_mean[i], v_standard_deviation[i], v_sum[i], v_sum_p2[i], v_sum_p3[i], nb ); 00167 } 00168 return res; 00169 } 00170 00171 template<class Pixel> 00172 struct OutputParams 00173 { 00174 OutputParams() 00175 { 00176 using namespace terry::numeric; 00177 pixel_zeros_t<Pixel>( )( _average ); 00178 pixel_zeros_t<Pixel>( )( _variance ); 00179 pixel_zeros_t<Pixel>( )( _channelMin ); 00180 pixel_zeros_t<Pixel>( )( _channelMax ); 00181 pixel_zeros_t<Pixel>( )( _luminosityMin ); 00182 pixel_zeros_t<Pixel>( )( _luminosityMax ); 00183 pixel_zeros_t<Pixel>( )( _kurtosis ); 00184 pixel_zeros_t<Pixel>( )( _skewness ); 00185 } 00186 00187 Pixel _average; 00188 Pixel _variance; 00189 Pixel _channelMin; 00190 Pixel _channelMax; 00191 Pixel _luminosityMin; 00192 Pixel _luminosityMax; 00193 Pixel _kurtosis; 00194 Pixel _skewness; 00195 }; 00196 00197 template<class View, typename CType = boost::gil::bits64f> 00198 struct ComputeOutputParams 00199 { 00200 00201 typedef typename View::value_type Pixel; 00202 typedef typename boost::gil::color_space_type<View>::type Colorspace; 00203 typedef boost::gil::pixel<typename boost::gil::channel_type<View>::type, boost::gil::layout<boost::gil::gray_t> > PixelGray; // grayscale pixel type (using the input channel_type) 00204 typedef boost::gil::pixel<CType, boost::gil::layout<Colorspace> > CPixel; // the pixel type use for computation (using input colorspace) 00205 00206 typedef OutputParams<CPixel> Output; 00207 00208 Output operator()( const View& image, ImageStatisticsPlugin& plugin ) 00209 { 00210 using namespace terry::numeric; 00211 OutputParams<CPixel> output; 00212 00213 const std::size_t nbPixels = image.width() * image.height(); 00214 00215 // declare values and init 00216 Pixel firstPixel = *image.begin(); // for initialization only 00217 PixelGray firstPixelGray; 00218 color_convert( firstPixel, firstPixelGray ); 00219 00220 Pixel channelMin = firstPixel; 00221 Pixel channelMax = firstPixel; 00222 Pixel luminosityMin = firstPixel; 00223 PixelGray luminosityMinGray = firstPixelGray; 00224 Pixel luminosityMax = firstPixel; 00225 PixelGray luminosityMaxGray = firstPixelGray; 00226 00227 CPixel sum; 00228 CPixel varianceSum; 00229 CPixel sum_p2; 00230 CPixel sum_p3; 00231 CPixel sum_p4; 00232 pixel_zeros_t<CPixel>( )( sum ); 00233 pixel_zeros_t<CPixel>( )( varianceSum ); 00234 pixel_zeros_t<CPixel>( )( sum_p2 ); 00235 pixel_zeros_t<CPixel>( )( sum_p3 ); 00236 pixel_zeros_t<CPixel>( )( sum_p4 ); 00237 00238 for( int y = 0; y < image.height(); ++y ) 00239 { 00240 typename View::x_iterator src_it = image.x_at( 0, y ); 00241 CPixel lineAverage; 00242 pixel_zeros_t<CPixel>( )( lineAverage ); 00243 00244 for( int x = 0; x < image.width(); ++x, ++src_it ) 00245 { 00246 CPixel pix; 00247 pixel_assigns_t<Pixel, CPixel>( )( * src_it, pix ); // pix = src_it; 00248 00249 CPixel pix_p2; 00250 CPixel pix_p3; 00251 CPixel pix_p4; 00252 00253 pixel_assigns_t<CPixel, CPixel>( )( pixel_pow_t<CPixel, 2>( )( pix ), pix_p2 ); // pix_p2 = pow<2>( pix ); 00254 pixel_assigns_t<CPixel, CPixel>( )( pixel_multiplies_t<CPixel, CPixel, CPixel>( )( pix, pix_p2 ), pix_p3 ); // pix_p3 = pix * pix_p2; 00255 pixel_assigns_t<CPixel, CPixel>( )( pixel_multiplies_t<CPixel, CPixel, CPixel>( )( pix_p2, pix_p2 ), pix_p4 ); // pix_p4 = pix_p2 * pix_p2; 00256 00257 pixel_plus_assign_t<CPixel, CPixel>( )( pix, sum ); // sum += pix; 00258 pixel_plus_assign_t<CPixel, CPixel>( )( pix_p2, sum_p2 ); // sum_p2 += pix_p2; 00259 pixel_plus_assign_t<CPixel, CPixel>( )( pix_p3, sum_p3 ); // sum_p3 += pix_p3; 00260 pixel_plus_assign_t<CPixel, CPixel>( )( pix_p4, sum_p4 ); // sum_p4 += pix_p4; 00261 00262 // search min for each channel 00263 pixel_assign_min_t<Pixel, Pixel>( )( * src_it, channelMin ); 00264 // search max for each channel 00265 pixel_assign_max_t<Pixel, Pixel>( )( * src_it, channelMax ); 00266 00267 PixelGray grayCurrentPixel; // current pixel in gray colorspace 00268 color_convert( * src_it, grayCurrentPixel ); 00269 00270 // search min luminosity 00271 if( get_color( grayCurrentPixel, gray_color_t() ) < get_color( luminosityMinGray, gray_color_t() ) ) 00272 { 00273 luminosityMin = *src_it; 00274 luminosityMinGray = grayCurrentPixel; 00275 } 00276 // search max luminosity 00277 if( get_color( grayCurrentPixel, gray_color_t() ) > get_color( luminosityMaxGray, gray_color_t() ) ) 00278 { 00279 luminosityMax = *src_it; 00280 luminosityMaxGray = grayCurrentPixel; 00281 } 00282 } 00283 } 00284 00285 output._channelMin = channelMin; 00286 output._channelMax = channelMax; 00287 output._luminosityMin = luminosityMin; 00288 output._luminosityMax = luminosityMax; 00289 00290 CPixel stdDeriv = pixel_standard_deviation( sum, sum_p2, nbPixels ); 00291 output._average = pixel_divides_scalar_t<CPixel, double>() ( sum, nbPixels ); 00292 00293 for( int y = 0; y < image.height(); ++y ) 00294 { 00295 typename View::x_iterator src_it = image.x_at( 0, y ); 00296 00297 for( int x = 0; x < image.width(); ++x, ++src_it ) 00298 { 00299 CPixel pix; 00300 pixel_assigns_t<Pixel, CPixel>( )( * src_it, pix ); // pix = src_it; 00301 00302 CPixel pix_diff = pix; 00303 CPixel pix_diff2; 00304 00305 pix_diff = pixel_minus_t<CPixel, CPixel, CPixel>( )( pix, output._average ); // pix_diff = (pix - mean) 00306 pix_diff2 = pixel_multiplies_t<CPixel, CPixel, CPixel>( )( pix_diff, pix_diff ); // pix_diff2 = (x - mean)*(x - mean) 00307 00308 pixel_plus_assign_t<CPixel, CPixel>( )( pix_diff2, varianceSum ); // varianceSum += pix_diff2; 00309 } 00310 } 00311 00312 CPixel varianceSquare = pixel_divides_scalar_t<CPixel, double>() ( varianceSum, nbPixels ); 00313 output._variance = pixel_sqrt_t<CPixel, Pixel>()( varianceSquare ); 00314 output._kurtosis = pixel_kurtosis( output._average, stdDeriv, sum, sum_p2, sum_p3, sum_p4, nbPixels ); 00315 output._skewness = pixel_skewness( output._average, stdDeriv, sum, sum_p2, sum_p3, nbPixels ); 00316 00317 return output; 00318 } 00319 00320 }; 00321 00322 00323 template <typename OutputParamsRGBA, typename OutputParamsHSL> 00324 void setOutputParams( const OutputParamsRGBA& outputParamsRGBA, const OutputParamsHSL& outputParamsHSL, const OfxTime time, ImageStatisticsPlugin& plugin ) 00325 { 00326 setRGBAParamValuesAtTime( *plugin._paramOutputAverage, time, outputParamsRGBA._average ); 00327 setRGBAParamValuesAtTime( *plugin._paramOutputVariance, time, outputParamsRGBA._variance ); 00328 // TUTTLE_LOG_VAR4( TUTTLE_INFO, outputParamsRGBA._average[0], outputParamsRGBA._average[1], outputParamsRGBA._average[2], outputParamsRGBA._average[3] ); 00329 setRGBAParamValuesAtTime( *plugin._paramOutputChannelMin, time, outputParamsRGBA._channelMin ); 00330 // TUTTLE_LOG_VAR4( TUTTLE_INFO, outputParamsRGBA._channelMin[0], outputParamsRGBA._channelMin[1], outputParamsRGBA._channelMin[2], outputParamsRGBA._channelMin[3] ); 00331 setRGBAParamValuesAtTime( *plugin._paramOutputChannelMax, time, outputParamsRGBA._channelMax ); 00332 setRGBAParamValuesAtTime( *plugin._paramOutputLuminosityMin, time, outputParamsRGBA._luminosityMin ); 00333 setRGBAParamValuesAtTime( *plugin._paramOutputLuminosityMax, time, outputParamsRGBA._luminosityMax ); 00334 setRGBAParamValuesAtTime( *plugin._paramOutputKurtosis, time, outputParamsRGBA._kurtosis ); 00335 setRGBAParamValuesAtTime( *plugin._paramOutputSkewness, time, outputParamsRGBA._skewness ); 00336 00337 set012ParamValuesAtTime( plugin._paramOutputAverageHSL, time, outputParamsHSL._average ); 00338 // TUTTLE_LOG_VAR4( TUTTLE_INFO, outputParamsHSL._average[0], outputParamsHSL._average[1], outputParamsHSL._average[2], outputParamsHSL._average[3] ); 00339 set012ParamValuesAtTime( *plugin._paramOutputChannelMinHSL, time, outputParamsHSL._channelMin ); 00340 // TUTTLE_LOG_VAR4( TUTTLE_INFO, outputParamsHSL._channelMin[0], outputParamsHSL._channelMin[1], outputParamsHSL._channelMin[2], outputParamsHSL._channelMin[3] ); 00341 set012ParamValuesAtTime( *plugin._paramOutputChannelMaxHSL, time, outputParamsHSL._channelMax ); 00342 set012ParamValuesAtTime( *plugin._paramOutputLuminosityMinHSL, time, outputParamsHSL._luminosityMin ); 00343 set012ParamValuesAtTime( *plugin._paramOutputLuminosityMaxHSL, time, outputParamsHSL._luminosityMax ); 00344 set012ParamValuesAtTime( *plugin._paramOutputKurtosisHSL, time, outputParamsHSL._kurtosis ); 00345 set012ParamValuesAtTime( *plugin._paramOutputSkewnessHSL, time, outputParamsHSL._skewness ); 00346 } 00347 00348 template<class View> 00349 ImageStatisticsProcess<View>::ImageStatisticsProcess( ImageStatisticsPlugin& instance ) 00350 : ImageGilFilterProcessor<View>( instance, eImageOrientationIndependant ) 00351 , _plugin( instance ) 00352 { 00353 this->setNoMultiThreading(); 00354 } 00355 00356 template<class View> 00357 void ImageStatisticsProcess<View>::setup( const OFX::RenderArguments& args ) 00358 { 00359 using namespace boost::gil; 00360 00361 ImageGilFilterProcessor<View>::setup( args ); 00362 _processParams = _plugin.getProcessParams( this->_srcPixelRod ); 00363 00364 View image = subimage_view( this->_srcView, 00365 _processParams._rect.x1, 00366 _processParams._rect.y1, 00367 _processParams._rect.x2 - _processParams._rect.x1, 00368 _processParams._rect.y2 - _processParams._rect.y1 ); 00369 00370 typedef ComputeOutputParams<View, boost::gil::bits64f> ComputeRGBA; 00371 typename ComputeRGBA::Output outputRGBA = ComputeRGBA() ( image, this->_plugin ); 00372 00373 typedef pixel<typename channel_type<View>::type, layout<hsl_t> > HSLPixel; 00374 typedef color_converted_view_type<View, HSLPixel> HSLConverter; 00375 typedef ComputeOutputParams<typename HSLConverter::type, boost::gil::bits64f> ComputeHSL; 00376 typename ComputeHSL::Output outputHSL = ComputeHSL() ( color_converted_view<HSLPixel>( image ), this->_plugin ); 00377 00378 setOutputParams( outputRGBA, outputHSL, args.time, this->_plugin ); 00379 00380 switch( _processParams._chooseOutput ) 00381 { 00382 case eParamChooseOutputSource: 00383 break; 00384 case eParamChooseOutputAverage: 00385 color_convert( outputRGBA._average, _outputPixel ); 00386 break; 00387 case eParamChooseOutputVariance: 00388 color_convert( outputRGBA._variance, _outputPixel ); 00389 break; 00390 case eParamChooseOutputChannelMin: 00391 color_convert( outputRGBA._channelMin, _outputPixel ); 00392 break; 00393 case eParamChooseOutputChannelMax: 00394 color_convert( outputRGBA._channelMax, _outputPixel ); 00395 break; 00396 case eParamChooseOutputLuminosityMin: 00397 color_convert( outputRGBA._luminosityMin, _outputPixel ); 00398 break; 00399 case eParamChooseOutputLuminosityMax: 00400 color_convert( outputRGBA._luminosityMax, _outputPixel ); 00401 break; 00402 } 00403 } 00404 00405 /** 00406 * @param[in] procWindowRoW Processing window in RoW 00407 */ 00408 template<class View> 00409 void ImageStatisticsProcess<View>::multiThreadProcessImages( const OfxRectI& procWindowRoW ) 00410 { 00411 using namespace boost::gil; 00412 OfxRectI procWindowOutput = this->translateRoWToOutputClipCoordinates( procWindowRoW ); 00413 const OfxPointI procWindowSize = { 00414 procWindowRoW.x2 - procWindowRoW.x1, 00415 procWindowRoW.y2 - procWindowRoW.y1 }; 00416 00417 if( _processParams._chooseOutput == eParamChooseOutputSource ) 00418 { 00419 for( int y = procWindowOutput.y1; 00420 y < procWindowOutput.y2; 00421 ++y ) 00422 { 00423 typename View::x_iterator src_it = this->_srcView.x_at( procWindowOutput.x1, y ); 00424 typename View::x_iterator dst_it = this->_dstView.x_at( procWindowOutput.x1, y ); 00425 for( int x = procWindowOutput.x1; 00426 x < procWindowOutput.x2; 00427 ++x, ++src_it, ++dst_it ) 00428 { 00429 ( *dst_it ) = ( *src_it ); 00430 } 00431 if( this->progressForward( procWindowSize.x ) ) 00432 return; 00433 } 00434 } 00435 else 00436 { 00437 for( int y = procWindowOutput.y1; 00438 y < procWindowOutput.y2; 00439 ++y ) 00440 { 00441 typename View::x_iterator dst_it = this->_dstView.x_at( procWindowOutput.x1, y ); 00442 for( int x = procWindowOutput.x1; 00443 x < procWindowOutput.x2; 00444 ++x, ++dst_it ) 00445 { 00446 ( *dst_it ) = _outputPixel; 00447 } 00448 if( this->progressForward( procWindowSize.x ) ) 00449 return; 00450 } 00451 } 00452 } 00453 00454 } 00455 } 00456 }