TuttleOFX
1
|
00001 #include <boost/math/constants/constants.hpp> 00002 00003 00004 namespace tuttle { 00005 namespace plugin { 00006 namespace anisotropicFilter { 00007 namespace diffusion { 00008 00009 static const float kEpsilon = 1e-5f; 00010 00011 template<class View> 00012 AnisotropicDiffusionProcess<View>::AnisotropicDiffusionProcess( AnisotropicDiffusionPlugin &instance ) 00013 : ImageGilFilterProcessor<View>( instance, eImageOrientationIndependant ) 00014 , _plugin( instance ) 00015 { 00016 _fast_approx = instance.fetchBooleanParam( kParamFastApproximation ); 00017 _amplitude = instance.fetchRGBParam( kParamAmplitude ); 00018 } 00019 00020 template<class View> 00021 void AnisotropicDiffusionProcess<View>::setup( const OFX::RenderArguments &args ) 00022 { 00023 // Fetch output image 00024 this->_dst.reset( _plugin._clipDst->fetchImage( args.time ) ); 00025 if( !this->_dst.get( ) ) 00026 BOOST_THROW_EXCEPTION( exception::ImageNotReady() << exception::user( "Output" ) ); 00027 _dBounds = this->_dst->getBounds(); 00028 00029 OFX::EBitDepth dstBitDepth = this->_dst->getPixelDepth( ); 00030 OFX::EPixelComponent dstComponents = this->_dst->getPixelComponents( ); 00031 00032 // Fetch main input image 00033 this->_src.reset( _plugin._clipSrc->fetchImage( args.time ) ); 00034 if( !this->_src.get( ) ) 00035 BOOST_THROW_EXCEPTION( exception::ImageNotReady() << exception::user( "Input" ) ); 00036 _upScaledSrcBounds = this->_src->getBounds(); 00037 00038 if( !_plugin._clipSrcTensors->isConnected( ) ) 00039 BOOST_THROW_EXCEPTION( exception::ImageNotConnected() << exception::user( "Tensor" ) ); 00040 00041 // Fetch input tensors image 00042 _srcTensor.reset( _plugin._clipSrcTensors->fetchImage( args.time ) ); 00043 if( !_srcTensor.get( ) ) 00044 BOOST_THROW_EXCEPTION( exception::ImageNotReady() << exception::user( "Tensors" ) ); 00045 00046 if( _srcTensor->getRowDistanceBytes( ) < 0 || 00047 this->_dst->getRowDistanceBytes( ) < 0 || 00048 this->_src->getRowDistanceBytes( ) != _srcTensor->getRowDistanceBytes( ) ) 00049 { 00050 BOOST_THROW_EXCEPTION( exception::InOutMismatch() << exception::user( "Incompatible row bytes !" ) ); 00051 } 00052 00053 // Make sure bit depths are same 00054 OFX::EBitDepth srcBitDepth = this->_src->getPixelDepth( ); 00055 OFX::EPixelComponent srcComponents = this->_src->getPixelComponents( ); 00056 00057 // See if they have the same bit depths 00058 if( srcBitDepth != dstBitDepth || srcComponents != dstComponents ) 00059 { 00060 BOOST_THROW_EXCEPTION( exception::BitDepthMismatch() ); 00061 } 00062 00063 // Build views 00064 _srcView = interleaved_view( _upScaledSrcBounds.x2 - _upScaledSrcBounds.x1, 00065 _upScaledSrcBounds.y2 - _upScaledSrcBounds.y1, 00066 static_cast<Pixel*>(this->_src->getPixelData()), 00067 this->_src->getRowDistanceBytes() ); 00068 00069 _srcTensorView = interleaved_view( _upScaledSrcBounds.x2 - _upScaledSrcBounds.x1, 00070 _upScaledSrcBounds.y2 - _upScaledSrcBounds.y1, 00071 static_cast<Pixel*>(_srcTensor->getPixelData()), 00072 _srcTensor->getRowDistanceBytes() ); 00073 00074 this->_dstView = interleaved_view( _dBounds.x2 - _dBounds.x1, 00075 _dBounds.y2 - _dBounds.y1, 00076 static_cast<Pixel*>(this->_dst->getPixelData()), 00077 this->_dst->getRowDistanceBytes() ); 00078 } 00079 00080 /** 00081 * @brief Function called by rendering thread each time a process must be done. 00082 * @param[in] procWindowRoW Processing window in RoW 00083 */ 00084 template<class View> 00085 void AnisotropicDiffusionProcess<View>::multiThreadProcessImages( const OfxRectI& procWindowRoW ) 00086 { 00087 using namespace boost::gil; 00088 int margin = _plugin.getMargin(); 00089 OfxRGBColourD amplitude_rgb = _amplitude->getValue(); 00090 region_t dregion; 00091 00092 // dest width and height 00093 dregion.dw = procWindowRoW.x2 - procWindowRoW.x1; 00094 dregion.dh = procWindowRoW.y2 - procWindowRoW.y1; 00095 00096 // Set upscaled source 00097 OfxRectI srcRect; 00098 srcRect.x1 = procWindowRoW.x1 - margin; 00099 srcRect.y1 = procWindowRoW.y1 - margin; 00100 srcRect.x2 = procWindowRoW.x2 + margin + 1; 00101 srcRect.y2 = procWindowRoW.y2 + margin + 1; 00102 00103 srcRect = rectanglesIntersection(srcRect, _upScaledSrcBounds); 00104 00105 // dest starting offset among x and y 00106 dregion.dox = procWindowRoW.x1 - srcRect.x1; 00107 dregion.doy = procWindowRoW.y1 - srcRect.y1; 00108 dregion.dw = dregion.dox + procWindowRoW.x2 - procWindowRoW.x1; 00109 dregion.dh = dregion.doy + procWindowRoW.y2 - procWindowRoW.y1; 00110 00111 // We want to work in the source roi space 00112 View src = subimage_view(_srcView, 00113 srcRect.x1 - _upScaledSrcBounds.x1, 00114 srcRect.y1 - _upScaledSrcBounds.y1, 00115 srcRect.x2 - srcRect.x1, 00116 srcRect.y2 - srcRect.y1); 00117 00118 View srct = subimage_view(_srcTensorView, 00119 srcRect.x1 - _upScaledSrcBounds.x1, 00120 srcRect.y1 - _upScaledSrcBounds.y1, 00121 srcRect.x2 - srcRect.x1, 00122 srcRect.y2 - srcRect.y1); 00123 00124 // We want to work in the source roi space 00125 View dst = subimage_view(this->_dstView, 00126 procWindowRoW.x1 - this->_renderArgs.renderWindow.x1 - dregion.dox, 00127 procWindowRoW.y1 - this->_renderArgs.renderWindow.y1 - dregion.doy, 00128 procWindowRoW.x2 - procWindowRoW.x1 + dregion.dw, 00129 procWindowRoW.y2 - procWindowRoW.y1 + dregion.dh); 00130 00131 blur_anisotropic( dst, src, srct, dregion, amplitude_rgb, _fast_approx->getValue() ); 00132 } 00133 00134 /** 00135 * @brief Function called to apply an anisotropic blur 00136 * 00137 * @param[out] dst Destination image view 00138 * @param[in] amplitude Amplitude of the anisotropic blur 00139 * @param dl spatial discretization. 00140 * @param da angular discretization. 00141 * @param gauss_prec precision of the gaussian function. 00142 * @param fast_approx Tell to use the fast approximation or not. 00143 * 00144 * @return Result view of the blurring process 00145 */ 00146 template<class View> 00147 void AnisotropicDiffusionProcess<View>::blur_anisotropic( View &dst, View &src, View &G, const region_t & dregion, 00148 const OfxRGBColourD& amplitude, const bool fast_approx /*=true*/, 00149 const float dl /* =0.8f */, const float da /* = 30.0f */, 00150 const float gauss_prec /*=2.0f*/ ) 00151 { 00152 using namespace boost::gil; 00153 using namespace terry; 00154 00155 typedef typename View::x_iterator dIterator; 00156 typedef typename channel_type<View>::type pix_t; 00157 typedef typename channel_type<View>::type dpix_t; 00158 00159 if( amplitude.r > 0.0f || amplitude.g > 0.0f || amplitude.b > 0.0f ) 00160 { 00161 if( src.num_channels( ) < 3 || dst.num_channels( ) < 3 ) 00162 { 00163 TUTTLE_LOG_ERROR( "blur_anisotropic() : Specified tensor field (" << (int)G.width( ) << ", " << (int)G.height( ) << ", " << (int)G.num_channels( ) << ") is not valid." ); 00164 return; 00165 } 00166 00167 if( src.dimensions( ) != G.dimensions( ) ) 00168 { 00169 TUTTLE_LOG_ERROR( "blur_anisotropic() : Incompatible dimensions." ); 00170 return; 00171 } 00172 00173 // Create a float compatible view 00174 typedef pixel<bits32f, devicen_layout_t< num_channels<View>::value > > comp_pixel_t; 00175 typedef image<comp_pixel_t, false, std::allocator<unsigned char> > comp_image_t; 00176 typedef typename view_type_from_pixel<comp_pixel_t>::type comp_view_t; 00177 typedef typename comp_view_t::x_iterator tmp_iterator; 00178 00179 comp_image_t itmp( src.dimensions( ) ); 00180 comp_view_t tmpv( view( itmp ) ); 00181 00182 const float amplitude_tab[] = { amplitude.r, amplitude.g, amplitude.b }; 00183 const float sqrt2amplitude_r = std::sqrt( 2.0f * amplitude.r ); 00184 const float sqrt2amplitude_g = std::sqrt( 2.0f * amplitude.g ); 00185 const float sqrt2amplitude_b = std::sqrt( 2.0f * amplitude.b ); 00186 const unsigned int 00187 dx1 = src.width( ) - 1, 00188 dy1 = src.height( ) - 1, 00189 w = src.width( ), 00190 h = src.height( ), 00191 nc = std::min( ( int ) src.num_channels( ), 00192 std::min( 3, ( int ) dst.num_channels( ) ) ); 00193 00194 std::vector<float> sW( w * h * nc ); 00195 float *pW = NULL; 00196 std::vector<float> tmp( nc ); 00197 int N = 0; 00198 00199 this->progressBegin( ( int ) ( ( 360.0f - ( 360 % ( int ) da ) / 2.0f ) / da ) * h, "PDE Denoiser algorithm in progress" ); 00200 fill_black( tmpv/*subimage_view(tmpv, dregion.dox, dregion.doy, dregion.dw, dregion.dh)*/ ); 00201 00202 // 2D version of the algorithm 00203 for( float theta = ( 360 % ( int ) da ) / 2.0f; 00204 theta < 360.0f; ( theta += da ), ++N ) 00205 { 00206 const float 00207 thetar = theta * boost::math::constants::pi<float>() / 180.0f, 00208 vx = std::cos( thetar ), 00209 vy = std::sin( thetar ); 00210 pW = &sW[0]; 00211 for( unsigned int yg = 0; yg < h; ++yg ) 00212 { 00213 dIterator iterG = G.row_begin( yg ); 00214 for( unsigned int xg = 0; xg < w; ++xg ) 00215 { 00216 const float 00217 a = ( *iterG )[0], 00218 b = ( *iterG )[1], 00219 c = ( *iterG )[2]; 00220 const float 00221 u = a * vx + b*vy, 00222 v = b * vx + c*vy, 00223 n = std::sqrt( kEpsilon + u * u + v * v ); 00224 float dln = 0.0f; 00225 if( n > 0.0f ) 00226 dln = dl / n; 00227 00228 pW[0] = u * dln; 00229 pW[1] = v * dln; 00230 pW[2] = n; 00231 pW += nc; 00232 iterG++; 00233 } 00234 } 00235 pW = &sW[0]; 00236 for( unsigned int y = 0; y < h; ++y ) 00237 { 00238 tmp_iterator tmp_it = tmpv.row_begin( y ); 00239 dIterator view_it = src.row_begin( y ); 00240 for( unsigned int x = 0; x < w; ++x ) 00241 { 00242 memset( &tmp[0], 0, sizeof(float ) * nc ); 00243 const float 00244 cu = pW[0], 00245 cv = pW[1], 00246 n = pW[2], 00247 fsigma_r = sqrt2amplitude_r * n, 00248 length_r = gauss_prec * fsigma_r, 00249 fsigma2_r = 2.0f * fsigma_r * fsigma_r, 00250 fsigma_g = sqrt2amplitude_g * n, 00251 length_g = gauss_prec * fsigma_g, 00252 fsigma2_g = 2.0f * fsigma_g * fsigma_g, 00253 fsigma_b = sqrt2amplitude_b * n, 00254 length_b = gauss_prec * fsigma_b, 00255 fsigma2_b = 2.0f * fsigma_b * fsigma_b; 00256 00257 const float lrgba[] = { length_r, length_g, length_b, 00258 length_r + length_g + length_b / 3.0f }; 00259 00260 const float srgba[] = { fsigma2_r, fsigma2_g, fsigma2_b, 00261 fsigma2_r + fsigma2_g + fsigma2_b / 3.0f }; 00262 00263 float S, pu, pv, X, Y; 00264 pW += nc; 00265 if( n == 0.0f ) 00266 { 00267 for( unsigned int i = 0; i < nc; ++i ) 00268 ( *tmp_it )[i] += ( *view_it )[i]; 00269 } 00270 else 00271 { 00272 // Nearest-neighbor interpolation for 2D images 00273 if( fast_approx ) 00274 { 00275 for( unsigned int i = 0; i < nc; ++i ) 00276 { 00277 X = ( float ) x; 00278 Y = ( float ) y; 00279 S = 0.0f; 00280 pu = cu; 00281 pv = cv; 00282 if( amplitude_tab[i] > 0 ) 00283 for( float l = 0.0f; l < lrgba[i] && 00284 X >= 0.0f && 00285 X <= dx1 && 00286 Y >= 0.0f && 00287 Y <= dy1; l += dl ) 00288 { 00289 const int 00290 cx = ( int ) ( X + 0.5f ), 00291 cy = ( int ) ( Y + 0.5f ); 00292 float 00293 u = sW[( cy * w + cx ) * nc + 0], 00294 v = sW[( cy * w + cx ) * nc + 1]; 00295 if( ( pu * u + pv * v ) < 0.0f ) 00296 { 00297 u = -u; 00298 v = -v; 00299 } 00300 tmp[i] += src( cx, cy )[i]; 00301 ++S; 00302 X += ( pu = u ); 00303 Y += ( pv = v ); 00304 } 00305 if( S > 0 ) 00306 ( *tmp_it )[i] += ( dpix_t ) ( tmp[i] / S ); 00307 else 00308 (*tmp_it )[i] += ( *view_it )[i]; 00309 } 00310 } 00311 else 00312 { 00313 for( unsigned int i = 0; i < nc; ++i ) 00314 { 00315 X = ( float ) x; 00316 Y = ( float ) y; 00317 S = 0.0f; 00318 pu = cu; 00319 pv = cv; 00320 if( amplitude_tab[i] > 0.0f ) 00321 for( float l = 0.0f; l < lrgba[i] && 00322 X >= 0.0f && 00323 X <= dx1 && 00324 Y >= 0.0f && 00325 Y <= dy1; l += dl ) 00326 { 00327 const int 00328 cx = ( int ) ( X + 0.5f ), 00329 cy = ( int ) ( Y + 0.5f ); 00330 float 00331 u = sW[( cy * w + cx ) * nc + 0], 00332 v = sW[( cy * w + cx ) * nc + 1]; 00333 if( ( pu * u + pv * v ) < 0.0f ) 00334 { 00335 u = -u; 00336 v = -v; 00337 } 00338 const float coef = std::exp( -l * l / srgba[i] ); 00339 tmp[i] += coef * src( cx, cy )[i]; 00340 S += coef; 00341 X += ( pu = u ); 00342 Y += ( pv = v ); 00343 } 00344 if( S > 0 ) 00345 ( *tmp_it )[i] += ( dpix_t ) ( tmp[i] / S ); 00346 else 00347 (*tmp_it )[i] += ( *view_it )[i]; 00348 } 00349 } 00350 } 00351 ++view_it; 00352 ++tmp_it; 00353 } 00354 if( this->progressForward( w ) ) 00355 return; 00356 } 00357 } 00358 00359 for( unsigned int y = dregion.doy; y < dregion.dh; ++y ) 00360 { 00361 tmp_iterator t_iter = tmpv.row_begin( y ); 00362 dIterator d_iter = dst.row_begin( y ); 00363 t_iter += dregion.dox; 00364 d_iter += dregion.dox; 00365 00366 for( unsigned int x = dregion.dox; x < dregion.dw; ++x ) 00367 { 00368 for( unsigned int c = 0; c < nc; ++c ) 00369 { 00370 ( *d_iter )[c] = ( dpix_t ) ( ( *t_iter )[c] / N ); 00371 } 00372 ++d_iter; 00373 ++t_iter; 00374 } 00375 } 00376 } 00377 else 00378 { 00379 copy_pixels( subimage_view(src, dregion.dox, dregion.doy, dregion.dw, dregion.dh), 00380 subimage_view(dst, dregion.dox, dregion.doy, dregion.dw, dregion.dh) ); 00381 } 00382 } 00383 00384 } 00385 } 00386 } 00387 }