#pragma once /*------------------------------------ Iwa_BokehRefFx Apply an off-focus effect to a single source image. The amount of bokeh is specified by user input reference image, which represents the depth by brightness of pixels. It considers characteristics of films (which is known as Hurter–Driffield curves) or human eye's perception (which is known as Weber–Fechner law). For filtering process I used KissFFT, an FFT library by Mark Borgerding, distributed with a 3-clause BSD-style license. ------------------------------------*/ #ifndef IWA_BOKEH_REF_H #define IWA_BOKEH_REF_H #include "stdfx.h" #include "tfxparam.h" #include #include #include "tools/kiss_fftnd.h" struct float4 { float x, y, z, w; }; //------------------------------------ class BokehRefThread : public QThread { int m_channel; volatile bool m_finished; kiss_fft_cpx* m_fftcpx_channel_before; kiss_fft_cpx* m_fftcpx_channel; kiss_fft_cpx* m_fftcpx_alpha; kiss_fft_cpx* m_fftcpx_iris; float4* m_result_buff; kiss_fftnd_cfg m_kissfft_plan_fwd, m_kissfft_plan_bkwd; TDimensionI m_dim; bool m_isTerminated; public: BokehRefThread(int channel, kiss_fft_cpx* fftcpx_channel_before, kiss_fft_cpx* fftcpx_channel, kiss_fft_cpx* fftcpx_alpha, kiss_fft_cpx* fftcpx_iris, float4* result_buff, kiss_fftnd_cfg kissfft_plan_fwd, kiss_fftnd_cfg kissfft_plan_bkwd, TDimensionI& dim); void run() override; bool isFinished() { return m_finished; } void terminateThread() { m_isTerminated = true; } }; //------------------------------------ class Iwa_BokehRefFx : public TStandardRasterFx { FX_PLUGIN_DECLARATION(Iwa_BokehRefFx) protected: TRasterFxPort m_iris; // iris image TRasterFxPort m_source; // source image TRasterFxPort m_depth; // depth reference image TDoubleParamP m_onFocusDistance; // Focus Distance (0-1) TDoubleParamP m_bokehAmount; // The maximum bokeh size. The size of bokeh at // the layer separated by 1.0 from the focal // position TDoubleParamP m_hardness; // Film gamma TIntParamP m_distancePrecision; // Separation of depth image TBoolParamP m_fillGap; // Toggles whether to extend pixels behind the front // layers in order to fill gap // It should be ON for normal backgrounds and should be OFF for the layer // which is // "floating" from the lower layers such as dust, snowflake or spider-web. TBoolParamP m_doMedian; // (Effective only when the Fill Gap option is ON) // Toggles whether to use Median Filter for extending the pixels. // Get the pixel size of bokehAmount ( referenced ino_blur.cpp ) float getBokehPixelAmount(const double frame, const TAffine affine); // normalize the source raster image to 0-1 and set to dstMem // returns true if the source is (seems to be) premultiplied template bool setSourceRaster(const RASTER srcRas, float4* dstMem, TDimensionI dim); // normalize brightness of the depth reference image to unsigned char // and store into dstMem template void setDepthRaster(const RASTER srcRas, unsigned char* dstMem, TDimensionI dim); template void setDepthRasterGray(const RASTER srcRas, unsigned char* dstMem, TDimensionI dim); // create the depth index map void defineSegemntDepth(const unsigned char* indexMap_main, const unsigned char* indexMap_sub, const float* mainSub_ratio, const unsigned char* depth_buff, const TDimensionI& dimOut, const double frame, QVector& segmentDepth_main, QVector& segmentDepth_sub); // set the result template void setOutputRaster(float4* srcMem, const RASTER dstRas, TDimensionI dim, TDimensionI margin); // obtain iris size from the depth value float calcIrisSize(const float depth, const float bokehPixelAmount, const double onFocusDistance); // resize/invert the iris according to the size ratio // normalize the brightness // resize to the output size void convertIris(const float irisSize, const TRectD& irisBBox, const TTile& irisTile, const TDimensionI& enlargedDim, kiss_fft_cpx* fftcpx_iris_before); // convert source image value rgb -> exposure void convertRGBToExposure(const float4* source_buff, int size, float filmGamma, bool sourceIsPremultiplied); // generate the segment layer source at the current depth // considering fillGap and doMedian options void retrieveLayer(const float4* source_buff, const float4* segment_layer_buff, const unsigned char* indexMap_mainSub, int index, int lx, int ly, bool fillGap, bool doMedian, int margin); // apply single median filter void doSingleMedian(const float4* source_buff, const float4* segment_layer_buff, const unsigned char* indexMap_mainSub, int index, int lx, int ly, const unsigned char* generation_buff, int curGen); // normal-composite the layer as is, without filtering void compositeAsIs(const float4* segment_layer_buff, const float4* result_buff_mainSub, int size); // retrieve segment layer image for each channel void retrieveChannel(const float4* segment_layer_buff, // src kiss_fft_cpx* fftcpx_r_before, // dst kiss_fft_cpx* fftcpx_g_before, // dst kiss_fft_cpx* fftcpx_b_before, // dst kiss_fft_cpx* fftcpx_a_before, // dst int size); // multiply filter on channel void multiplyFilter(kiss_fft_cpx* fftcpx_channel, // dst kiss_fft_cpx* fftcpx_iris, // filter int size); // normal comosite the alpha channel void compositeAlpha(const float4* result_buff, // dst const kiss_fft_cpx* fftcpx_alpha, // alpha int lx, int ly); // interpolate main and sub exposures // convert exposure -> RGB (0-1) // set to the result void interpolateExposureAndConvertToRGB( const float4* result_main_buff, // result1 const float4* result_sub_buff, // result2 const float* mainSub_ratio, // ratio float filmGamma, const float4* source_buff, // dst int size); public: Iwa_BokehRefFx(); void doCompute(TTile& tile, double frame, const TRenderSettings& settings) override; bool doGetBBox(double frame, TRectD& bBox, const TRenderSettings& info) override; bool canHandle(const TRenderSettings& info, double frame) override; void doCompute_CPU(const double frame, const TRenderSettings& settings, float bokehPixelAmount, float maxIrisSize, int margin, TDimensionI& dimOut, float4* source_buff, unsigned char* indexMap_main, unsigned char* indexMap_sub, float* mainSub_ratio, QVector& segmentDepth_main, QVector& segmentDepth_sub, TTile& irisTile, TRectD& irisBBox, bool sourceIsPremultiplied); }; #endif