TuttleOFX  1
ImageStatisticsProcess.tcc
Go to the documentation of this file.
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 }