TuttleOFX  1
ImageTensors.hpp
Go to the documentation of this file.
00001 /**
00002  * @brief This file provides a class for tensors image generation.
00003  */
00004 
00005 #ifndef _TUTTLE_PLUGIN_IMAGE_TENSORS_HPP_
00006 #define _TUTTLE_PLUGIN_IMAGE_TENSORS_HPP_
00007 
00008 #include "filters/edgeDetect.hpp"
00009 #include "filters/blurFilters.hpp"
00010 
00011 #include <tuttle/plugin/IProgress.hpp>
00012 #include <terry/math.hpp>
00013 
00014 #include <boost/gil/gil_all.hpp>
00015 
00016 #include <vector>
00017 #include <string>
00018 #include <float.h>
00019 
00020 namespace tuttle {
00021 namespace imageUtils {
00022 
00023 /**
00024  * @brief Structure handling anisotropic gradient parameters.
00025  */
00026 template <typename TensorView>
00027 struct tensor_t
00028 {
00029 
00030     tensor_t( )
00031         : dox(0), doy(0), dw(0), dh(0), algorithm( false ), stAlgo( 0 ),
00032         alpha( 0.0 ), sigma( 0.0 ), sharpness( 0.0 ),
00033         anisotropy( 0.0 ), geom_fact( 0.0 ), threshold( 0.0 ) { };
00034         int dox, doy, dw, dh;
00035         bool algorithm; ///< Generation algorithm
00036         int  stAlgo; ///< Structure tensors algorithm
00037         double alpha; ///< Pre-blurring (noise scale)
00038         double sigma; ///< Post-blurring
00039         double sharpness; ///< Contour preservation
00040         double anisotropy; ///< Anisotropic filtering
00041         double geom_fact; ///< Geometry factor
00042         double threshold; ///< Thresholding quantization factor
00043 };
00044 
00045 /** 
00046  * @brief Class used to render tensors
00047  */
00048 template <class View>
00049 class ImageTensors : public View
00050 {
00051 private:
00052     template <class D_ITER>
00053     void compute_vectors( D_ITER & iter_dst, const std::vector<double> & val,
00054                           const std::vector<double> & vec,
00055                           const double power1, const double power2 );
00056 
00057 public:
00058 
00059     typedef enum
00060     {
00061         eAnisotGradient
00062     } E_TensorsAlgorithm;
00063 
00064     ImageTensors( const View & view ) : View( view ) { }
00065 
00066     ImageTensors( int &width, int &height, typename View::xy_locator loc ) : View( width, height, loc ) { }
00067 
00068     virtual ~ImageTensors( ) { }
00069     //@todo: add other constructors
00070 
00071     void process( const View & srcView, E_TensorsAlgorithm tensAlgo,
00072                                   tuttle::plugin::IProgress *progress, void *args );
00073         void anisotropic_gradient( const View & src, tuttle::plugin::IProgress *progress, tensor_t<View> *args );
00074 };
00075 
00076 /**
00077  * @brief Process rendering
00078  *
00079  * @param[in]       srcView   source view
00080  * @param[in]       progress  tensor algorithm
00081  * @param[in]       args      algorithm parameters
00082  * 
00083  */
00084 template <class View>
00085 void ImageTensors<View>::process( const View & srcView, E_TensorsAlgorithm tensAlgo,
00086                                                                   tuttle::plugin::IProgress *progress, void *args )
00087 {
00088     switch( tensAlgo )
00089     {
00090         case eAnisotGradient:
00091             anisotropic_gradient( srcView, progress, ( tensor_t<View>* )args );
00092             break;
00093     }
00094     progress->progressEnd( );
00095 }
00096 
00097 template <class View>
00098 template <class D_ITER>
00099 void ImageTensors<View>::compute_vectors( D_ITER & iter_dst, const std::vector<double> & val,
00100                                           const std::vector<double> & vec,
00101                                           const double power1, const double power2 )
00102 {
00103     typedef typename bgil::channel_type<D_ITER>::type pix_t;
00104     const double
00105         // The more is l1 high, the more smoothing will be performed along
00106         // the edges direction (because n1 << n2).
00107         // Otherwise, the smoothing will be performed
00108         // along all directions (because n1 =~ n2).
00109         l1 = val[1],
00110         l2 = val[0],
00111         // (ux,uy) is orthogonal to (vx, vy)
00112         vx = vec[0],
00113         vy = vec[1],
00114         ux = vec[2],
00115         uy = vec[3],
00116         // n1, n2 set the strengths of the desired smoothing factor along
00117         // respectives directions.
00118         n1 = std::pow( 1.0 + l1 + l2, -power1 ),
00119         n2 = std::pow( 1.0 + l1 + l2, -power2 ),
00120         // Gaussian kernel strength for horizontal smoothing
00121         c0 = n1 * ux * ux + n2 * vx*vx,
00122         // Gaussian kernel strength for diagonal smoothing
00123         c1 = n1 * ux * uy + n2 * vx*vy,
00124         // Gaussian kernel strength for vertical smoothing
00125         c2 = n1 * uy * uy + n2 * vy*vy;
00126     /*
00127         c0 = c0 < 0.0 ? 0.0 : (c0 > 1.0 ? 1.0 : c0);
00128         c1 = c1 < 0.0 ? 0.0 : (c1 > 1.0 ? 1.0 : c1);
00129         c2 = c2 < 0.0 ? 0.0 : (c2 > 1.0 ? 1.0 : c2);
00130      */
00131     // Compute final values
00132     ( *iter_dst )[0] = pix_t( c0 );
00133     ( *iter_dst )[1] = pix_t( c1 );
00134     ( *iter_dst )[2] = pix_t( c2 );
00135 }
00136 
00137 /**
00138  * @brief Anisotropic gradient
00139  *
00140  * @param[in]       srcView   source view
00141  * @param[in]       args      algorithm parameters
00142  * 
00143  */
00144 template <class View>
00145 void ImageTensors<View>::anisotropic_gradient( const View & src, tuttle::plugin::IProgress *progress, tensor_t<View> *args )
00146 {
00147     using namespace boost::gil;
00148     using namespace terry;
00149     float alpha = ( float ) args->alpha;
00150     float sigma = ( float ) args->sigma;
00151     float sharpness = ( float ) args->sharpness;
00152     float anisotropy = ( float ) args->anisotropy;
00153     float geom_factor = ( float ) args->geom_fact;
00154     float threshold = ( float ) args->threshold / 1000.0f;
00155     int stAlgo = args->stAlgo;
00156     bool tensorAlgo = args->algorithm;
00157 
00158     if( anisotropy < 0.0f || anisotropy > 1.0f )
00159                 return;
00160     if( alpha < 0.0f )
00161                 return;
00162         if( sigma < 0.0f )
00163                 return;
00164         if( sharpness < 0.0f )
00165                 return;
00166 
00167         // Float type to process computation with reals
00168     typedef typename image_from_view<View>::type image_t;
00169     typedef typename View::x_iterator iterator;
00170     typedef typename View::x_iterator tmp_iterator;
00171     typedef typename View::locator locator;
00172 
00173     const double nsharpness = std::max( ( double ) sharpness, 1e-5 ),
00174             power1 = 0.5 * nsharpness,
00175             power2 = power1 / ( 1e-7 + 1.0 - anisotropy );
00176     std::vector<double> val( 2 );
00177     std::vector<double> vec( 4 );
00178     View & dst = *( View* )this;
00179 
00180     progress->progressBegin( 5 * 25, "Tensors generator algorithm in progress" );
00181         image_t tmp1( src.dimensions() );
00182         image_t tmp2( src.dimensions() );
00183     View tmpv1( view( tmp1 ) );
00184     View tmpv2( view( tmp2 ) );
00185     // Apply pre-blur (attenuate noise effect in tensors map)
00186     dericheFilter( src, tmpv1, alpha );
00187     if( progress->progressForward( 25 ) )
00188         return;
00189 
00190     // Multiply or normalize tensors to amplify image geometry
00191     if( geom_factor > 0.0f )
00192         multiply( tmpv1, tmpv2, geom_factor );
00193     else if( geom_factor < 0.0f )
00194         normalize( tmpv1, tmpv2, 0.0f, -geom_factor );
00195     else
00196         copy_pixels( tmpv1, tmpv2 );
00197     if( progress->progressForward( 25 ) )
00198         return;
00199 
00200     // Compute structure tensors
00201     switch( stAlgo )
00202     {
00203         case 0:
00204             // Using precise forward backward finite differences
00205             simple_structure_tensor( tmpv2, tmpv1, threshold );
00206             break;
00207         case 1:
00208             // Using harris edges detection
00209             harris( tmpv2, tmpv1, threshold );
00210             break;
00211         case 2:
00212             // Using canny-deriche edges detection
00213             dericheFilter( tmpv2, tmpv1, alpha, 1, threshold );
00214             break;
00215         default:
00216                         return;
00217             break;
00218     }
00219 
00220     if( progress->progressForward( 25 ) )
00221         return;
00222 
00223     // Apply a post-blur to attenuate the texture
00224     // of the gradient image
00225     dericheFilter( tmpv1, tmpv2, sigma );
00226     if( progress->progressForward( 25 ) )
00227         return;
00228 
00229     // Compute diffusion tensors (gaussian kernel directions map).
00230     if( tensorAlgo == false )
00231     {
00232         const float step = 25 / (args->dh - args->doy);
00233                 for( int y = args->doy; y < args->dh; ++y )
00234         {
00235             iterator src_it = tmpv2.row_begin( y );
00236                         iterator dst_it = this->row_begin( y - args->doy );
00237             if( progress->progressForward( ( int ) step ) )
00238                 return;
00239                         src_it += args->dox;
00240                         for( int x = args->dox; x < args->dw; ++x )
00241             {
00242                 // eigen value and vectors gives strength and direction of
00243                 // the vectors for the gaussian kernel.
00244                 symmetric_eigen( src_it, val, vec );
00245                                 // Compute vector directions:
00246                                 // r = along X, g = along diagonal, b = along y
00247                                 compute_vectors( dst_it, val, vec, power1, power2 );
00248                                 dst_it++;
00249                                 src_it++;
00250             }
00251         }
00252     }
00253     else
00254     {
00255         copy_pixels( subimage_view(tmpv2, args->dox, args->doy, dst.width(), dst.height()), dst );
00256     }
00257 }
00258 
00259 }
00260 }
00261 
00262 #endif