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