|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
#include "iwa_bokeh_util.h"
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
#include "trop.h"
|
|
shun-iwasawa |
1b1839 |
#include <array></array>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
#include <qset></qset>
|
|
shun-iwasawa |
1b1839 |
#include <qmap></qmap>
|
|
shun-iwasawa |
1b1839 |
#include <qreadwritelock></qreadwritelock>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
namespace {
|
|
shun-iwasawa |
1b1839 |
QReadWriteLock lock;
|
|
shun-iwasawa |
1b1839 |
QMutex mutex;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// modify fft coordinate to normal
|
|
shun-iwasawa |
1b1839 |
inline int getCoord(int index, int lx, int ly) {
|
|
shun-iwasawa |
1b1839 |
int i = index % lx;
|
|
shun-iwasawa |
1b1839 |
int j = index / lx;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int cx = i - lx / 2;
|
|
shun-iwasawa |
1b1839 |
int cy = j - ly / 2;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (cx < 0) cx += lx;
|
|
shun-iwasawa |
1b1839 |
if (cy < 0) cy += ly;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
return cy * lx + cx;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// RGB value <--> Exposure
|
|
shun-iwasawa |
1b1839 |
inline double valueToExposure(double value, double filmGamma) {
|
|
shun-iwasawa |
1b1839 |
double logVal = (value - 0.5) / filmGamma;
|
|
shun-iwasawa |
1b1839 |
return std::pow(10.0, logVal);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
inline double exposureToValue(double exposure, double filmGamma) {
|
|
shun-iwasawa |
1b1839 |
return std::log10(exposure) * filmGamma + 0.5;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
inline double clamp01(double val) {
|
|
shun-iwasawa |
1b1839 |
return (val < 0.0) ? 0.0 : ((val > 1.0) ? 1.0 : val);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
template <typename t=""></typename>
|
|
shun-iwasawa |
1b1839 |
TRasterGR8P allocateRasterAndLock(T** buf, TDimensionI dim) {
|
|
shun-iwasawa |
1b1839 |
TRasterGR8P ras(dim.lx * sizeof(T), dim.ly);
|
|
shun-iwasawa |
1b1839 |
ras->lock();
|
|
shun-iwasawa |
1b1839 |
*buf = (T*)ras->getRawData();
|
|
shun-iwasawa |
1b1839 |
return ras;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// release all registered raster memories and free all fft plans
|
|
shun-iwasawa |
1b1839 |
void releaseAllRastersAndPlans(QList<trastergr8p>& rasterList,</trastergr8p>
|
|
shun-iwasawa |
1b1839 |
QList<kiss_fftnd_cfg>& planList) {</kiss_fftnd_cfg>
|
|
shun-iwasawa |
1b1839 |
for (int r = 0; r < rasterList.size(); r++) rasterList.at(r)->unlock();
|
|
shun-iwasawa |
1b1839 |
for (int p = 0; p < planList.size(); p++) kiss_fft_free(planList.at(p));
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
} // namespace
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// thread for computing bokeh of each channel
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
BokehUtils::MyThread::MyThread(Channel channel, TRasterP layerTileRas,
|
|
shun-iwasawa |
1b1839 |
double4* result, double* alpha_bokeh,
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* kissfft_comp_iris,
|
|
shun-iwasawa |
1b1839 |
double layerHardness, double masterHardness,
|
|
shun-iwasawa |
1b1839 |
bool doLightenComp)
|
|
shun-iwasawa |
1b1839 |
: m_channel(channel)
|
|
shun-iwasawa |
1b1839 |
, m_layerTileRas(layerTileRas)
|
|
shun-iwasawa |
1b1839 |
, m_result(result)
|
|
shun-iwasawa |
1b1839 |
, m_alpha_bokeh(alpha_bokeh)
|
|
shun-iwasawa |
1b1839 |
, m_kissfft_comp_iris(kissfft_comp_iris)
|
|
shun-iwasawa |
1b1839 |
, m_layerHardness(layerHardness)
|
|
shun-iwasawa |
1b1839 |
, m_masterHardness(masterHardness)
|
|
shun-iwasawa |
1b1839 |
, m_finished(false)
|
|
shun-iwasawa |
1b1839 |
, m_kissfft_comp_in(0)
|
|
shun-iwasawa |
1b1839 |
, m_kissfft_comp_out(0)
|
|
shun-iwasawa |
1b1839 |
, m_isTerminated(false)
|
|
shun-iwasawa |
1b1839 |
, m_doLightenComp(doLightenComp) {
|
|
shun-iwasawa |
1b1839 |
if (m_masterHardness == 0.0) m_masterHardness = m_layerHardness;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
bool BokehUtils::MyThread::init() {
|
|
shun-iwasawa |
1b1839 |
// obtain the input image size
|
|
shun-iwasawa |
1b1839 |
int lx, ly;
|
|
shun-iwasawa |
1b1839 |
lx = m_layerTileRas->getSize().lx;
|
|
shun-iwasawa |
1b1839 |
ly = m_layerTileRas->getSize().ly;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// memory allocation for input
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly);
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras->lock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in = (kiss_fft_cpx*)m_kissfft_comp_in_ras->getRawData();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// allocation check
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_comp_in == 0) return false;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (m_isTerminated) {
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
return false;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// memory allocation for output
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly);
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out_ras->lock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out = (kiss_fft_cpx*)m_kissfft_comp_out_ras->getRawData();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// allocation check
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_comp_out == 0) {
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in = 0;
|
|
shun-iwasawa |
1b1839 |
return false;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (m_isTerminated) {
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in = 0;
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out = 0;
|
|
shun-iwasawa |
1b1839 |
return false;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// create the forward FFT plan
|
|
shun-iwasawa |
1b1839 |
int dims[2] = {ly, lx};
|
|
shun-iwasawa |
1b1839 |
int ndims = 2;
|
|
shun-iwasawa |
1b1839 |
m_kissfft_plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
|
|
shun-iwasawa |
1b1839 |
// allocation and cancel check
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_plan_fwd == NULL || m_isTerminated) {
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in = 0;
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out = 0;
|
|
shun-iwasawa |
1b1839 |
return false;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// create the backward FFT plan
|
|
shun-iwasawa |
1b1839 |
m_kissfft_plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0);
|
|
shun-iwasawa |
1b1839 |
// allocation and cancel check
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_plan_bkwd == NULL || m_isTerminated) {
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in = 0;
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out = 0;
|
|
shun-iwasawa |
1b1839 |
kiss_fft_free(m_kissfft_plan_fwd);
|
|
shun-iwasawa |
1b1839 |
m_kissfft_plan_fwd = NULL;
|
|
shun-iwasawa |
1b1839 |
return false;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// return true if all the initializations are done
|
|
shun-iwasawa |
1b1839 |
return true;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// convert layer RGB to exposure
|
|
shun-iwasawa |
1b1839 |
// multiply alpha
|
|
shun-iwasawa |
1b1839 |
// set to kiss_fft_cpx
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::MyThread::setLayerRaster(const RASTER srcRas,
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* dstMem,
|
|
shun-iwasawa |
1b1839 |
TDimensionI dim) {
|
|
shun-iwasawa |
1b1839 |
for (int j = 0; j < dim.ly; j++) {
|
|
shun-iwasawa |
1b1839 |
PIXEL* pix = srcRas->pixels(j);
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dim.lx; i++, pix++) {
|
|
shun-iwasawa |
1b1839 |
if (pix->m != 0) {
|
|
shun-iwasawa |
1b1839 |
double val = (m_channel == Red) ? (double)pix->r
|
|
shun-iwasawa |
1b1839 |
: (m_channel == Green) ? (double)pix->g
|
|
shun-iwasawa |
1b1839 |
: (double)pix->b;
|
|
shun-iwasawa |
1b1839 |
// multiply the exposure by alpha channel value
|
|
shun-iwasawa |
1b1839 |
dstMem[j * dim.lx + i].r =
|
|
shun-iwasawa |
1b1839 |
valueToExposure(val / (double)PIXEL::maxChannelValue,
|
|
shun-iwasawa |
1b1839 |
m_layerHardness) *
|
|
shun-iwasawa |
1b1839 |
((double)pix->m / (double)PIXEL::maxChannelValue);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::MyThread::run() {
|
|
shun-iwasawa |
1b1839 |
// get the source image size
|
|
shun-iwasawa |
1b1839 |
TDimensionI dim = m_layerTileRas->getSize();
|
|
shun-iwasawa |
1b1839 |
// int lx,ly;
|
|
shun-iwasawa |
1b1839 |
int lx = m_layerTileRas->getSize().lx;
|
|
shun-iwasawa |
1b1839 |
int ly = m_layerTileRas->getSize().ly;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// initialize
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dim.lx * dim.ly; i++) {
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in[i].r = 0.0; // real part
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in[i].i = 0.0; // imaginary part
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
TRaster32P ras32 = (TRaster32P)m_layerTileRas;
|
|
shun-iwasawa |
1b1839 |
TRaster64P ras64 = (TRaster64P)m_layerTileRas;
|
|
shun-iwasawa |
1b1839 |
// Prepare data for FFT.
|
|
shun-iwasawa |
1b1839 |
// Convert the RGB values to the exposure, then multiply it by the alpha
|
|
shun-iwasawa |
1b1839 |
// channel value
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
lock.lockForRead();
|
|
shun-iwasawa |
1b1839 |
if (ras32)
|
|
shun-iwasawa |
1b1839 |
setLayerRaster<traster32p, tpixel32="">(ras32, m_kissfft_comp_in, dim);</traster32p,>
|
|
shun-iwasawa |
1b1839 |
else if (ras64)
|
|
shun-iwasawa |
1b1839 |
setLayerRaster<traster64p, tpixel64="">(ras64, m_kissfft_comp_in, dim);</traster64p,>
|
|
shun-iwasawa |
1b1839 |
else {
|
|
shun-iwasawa |
1b1839 |
lock.unlock();
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
lock.unlock();
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (checkTerminationAndCleanupThread()) return;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(m_kissfft_plan_fwd, m_kissfft_comp_in, m_kissfft_comp_out);
|
|
shun-iwasawa |
1b1839 |
kiss_fft_free(m_kissfft_plan_fwd); // we don't need this plan anymore
|
|
shun-iwasawa |
1b1839 |
m_kissfft_plan_fwd = NULL;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (checkTerminationAndCleanupThread()) return;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Filtering. Multiply by the iris FFT data
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < lx * ly; i++) {
|
|
shun-iwasawa |
1b1839 |
float re, im;
|
|
shun-iwasawa |
1b1839 |
re = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].r -
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out[i].i * m_kissfft_comp_iris[i].i;
|
|
shun-iwasawa |
1b1839 |
im = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].i +
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_iris[i].r * m_kissfft_comp_out[i].i;
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out[i].r = re;
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out[i].i = im;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (checkTerminationAndCleanupThread()) return;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(m_kissfft_plan_bkwd, m_kissfft_comp_out,
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in); // Backward FFT
|
|
shun-iwasawa |
1b1839 |
kiss_fft_free(m_kissfft_plan_bkwd); // we don't need this plan anymore
|
|
shun-iwasawa |
1b1839 |
m_kissfft_plan_bkwd = NULL;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// In the backward FFT above, "m_kissfft_comp_out" is used as input and
|
|
shun-iwasawa |
1b1839 |
// "m_kissfft_comp_in" as output.
|
|
shun-iwasawa |
1b1839 |
// So we don't need "m_kissfft_comp_out" anymore.
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_out = 0;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (checkTerminationAndCleanupThread()) return;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
QMutexLocker locker(&mutex);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double* alp_p = m_alpha_bokeh;
|
|
shun-iwasawa |
1b1839 |
double4* res_p = m_result;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dim.lx * dim.ly; i++, alp_p++, res_p++) {
|
|
shun-iwasawa |
1b1839 |
if ((*alp_p) < 0.00001) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double exposure =
|
|
shun-iwasawa |
1b1839 |
(double)m_kissfft_comp_in[getCoord(i, dim.lx, dim.ly)].r /
|
|
shun-iwasawa |
1b1839 |
(double)(dim.lx * dim.ly);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// convert to layer hardness
|
|
shun-iwasawa |
1b1839 |
if (m_masterHardness != m_layerHardness)
|
|
shun-iwasawa |
1b1839 |
exposure =
|
|
shun-iwasawa |
1b1839 |
std::pow(exposure / (*alp_p), m_layerHardness / m_masterHardness) *
|
|
shun-iwasawa |
1b1839 |
(*alp_p);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double* res = (m_channel == Red) ? (&((*res_p).x))
|
|
shun-iwasawa |
1b1839 |
: (m_channel == Green) ? (&((*res_p).y))
|
|
shun-iwasawa |
1b1839 |
: (&((*res_p).z));
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// composite exposure
|
|
shun-iwasawa |
1b1839 |
if ((*alp_p) >= 1.0 || (*res) == 0.0)
|
|
shun-iwasawa |
1b1839 |
(*res) = exposure;
|
|
shun-iwasawa |
1b1839 |
else {
|
|
shun-iwasawa |
1b1839 |
(*res) *= 1.0 - (*alp_p);
|
|
shun-iwasawa |
1b1839 |
(*res) += exposure;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// over compoite alpha
|
|
shun-iwasawa |
1b1839 |
if (m_channel == Red) {
|
|
shun-iwasawa |
1b1839 |
if ((*res_p).w < 1.0) {
|
|
shun-iwasawa |
1b1839 |
if ((*alp_p) > 1.0)
|
|
shun-iwasawa |
1b1839 |
(*res_p).w = 1.0;
|
|
shun-iwasawa |
1b1839 |
else
|
|
shun-iwasawa |
1b1839 |
(*res_p).w = (*alp_p) + (*res_p).w * (1.0 - (*alp_p));
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
//---
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
m_kissfft_comp_in = 0;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
m_finished = true;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
bool BokehUtils::MyThread::checkTerminationAndCleanupThread() {
|
|
shun-iwasawa |
1b1839 |
if (!m_isTerminated) return false;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_comp_in) m_kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_comp_out) m_kissfft_comp_out_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_plan_fwd) kiss_fft_free(m_kissfft_plan_fwd);
|
|
shun-iwasawa |
1b1839 |
if (m_kissfft_plan_bkwd) kiss_fft_free(m_kissfft_plan_bkwd);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
m_finished = true;
|
|
shun-iwasawa |
1b1839 |
return true;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------
|
|
shun-iwasawa |
1b1839 |
BokehUtils::BokehRefThread::BokehRefThread(
|
|
shun-iwasawa |
1b1839 |
int channel, kiss_fft_cpx* fftcpx_channel_before,
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_channel, kiss_fft_cpx* fftcpx_alpha,
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_iris, double4* result_buff,
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_fwd, kiss_fftnd_cfg kissfft_plan_bkwd,
|
|
shun-iwasawa |
1b1839 |
TDimensionI& dim)
|
|
shun-iwasawa |
1b1839 |
: m_channel(channel)
|
|
shun-iwasawa |
1b1839 |
, m_fftcpx_channel_before(fftcpx_channel_before)
|
|
shun-iwasawa |
1b1839 |
, m_fftcpx_channel(fftcpx_channel)
|
|
shun-iwasawa |
1b1839 |
, m_fftcpx_alpha(fftcpx_alpha)
|
|
shun-iwasawa |
1b1839 |
, m_fftcpx_iris(fftcpx_iris)
|
|
shun-iwasawa |
1b1839 |
, m_result_buff(result_buff)
|
|
shun-iwasawa |
1b1839 |
, m_kissfft_plan_fwd(kissfft_plan_fwd)
|
|
shun-iwasawa |
1b1839 |
, m_kissfft_plan_bkwd(kissfft_plan_bkwd)
|
|
shun-iwasawa |
1b1839 |
, m_dim(dim)
|
|
shun-iwasawa |
1b1839 |
, m_finished(false)
|
|
shun-iwasawa |
1b1839 |
, m_isTerminated(false) {}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::BokehRefThread::run() {
|
|
shun-iwasawa |
1b1839 |
// execute channel fft
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(m_kissfft_plan_fwd, m_fftcpx_channel_before, m_fftcpx_channel);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (m_isTerminated) {
|
|
shun-iwasawa |
1b1839 |
m_finished = true;
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int size = m_dim.lx * m_dim.ly;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// multiply filter
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++) {
|
|
shun-iwasawa |
1b1839 |
float re, im;
|
|
shun-iwasawa |
1b1839 |
re = m_fftcpx_channel[i].r * m_fftcpx_iris[i].r -
|
|
shun-iwasawa |
1b1839 |
m_fftcpx_channel[i].i * m_fftcpx_iris[i].i;
|
|
shun-iwasawa |
1b1839 |
im = m_fftcpx_channel[i].r * m_fftcpx_iris[i].i +
|
|
shun-iwasawa |
1b1839 |
m_fftcpx_iris[i].r * m_fftcpx_channel[i].i;
|
|
shun-iwasawa |
1b1839 |
m_fftcpx_channel[i].r = re;
|
|
shun-iwasawa |
1b1839 |
m_fftcpx_channel[i].i = im;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// execute invert fft
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(m_kissfft_plan_bkwd, m_fftcpx_channel, m_fftcpx_channel_before);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (m_isTerminated) {
|
|
shun-iwasawa |
1b1839 |
m_finished = true;
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// pixels with smaller index : normal composite exposure value
|
|
shun-iwasawa |
1b1839 |
// pixels with the same or larger index : replace exposure value
|
|
shun-iwasawa |
1b1839 |
double4* result_p = m_result_buff;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, result_p++) {
|
|
shun-iwasawa |
1b1839 |
// modify fft coordinate to normal
|
|
shun-iwasawa |
1b1839 |
int coord = getCoord(i, m_dim.lx, m_dim.ly);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double alpha = (double)m_fftcpx_alpha[coord].r / (double)size;
|
|
shun-iwasawa |
1b1839 |
// ignore transpalent pixels
|
|
shun-iwasawa |
1b1839 |
if (alpha < 0.00001) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double exposure = (double)m_fftcpx_channel_before[coord].r / (double)size;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// in case of using upper layer at all
|
|
shun-iwasawa |
1b1839 |
if (alpha >= 1.0 || (m_channel == 0 && (*result_p).x == 0.0) ||
|
|
shun-iwasawa |
1b1839 |
(m_channel == 1 && (*result_p).y == 0.0) ||
|
|
shun-iwasawa |
1b1839 |
(m_channel == 2 && (*result_p).z == 0.0)) {
|
|
shun-iwasawa |
1b1839 |
// set exposure
|
|
shun-iwasawa |
1b1839 |
if (m_channel == 0) // R
|
|
shun-iwasawa |
1b1839 |
(*result_p).x = exposure;
|
|
shun-iwasawa |
1b1839 |
else if (m_channel == 1) // G
|
|
shun-iwasawa |
1b1839 |
(*result_p).y = exposure;
|
|
shun-iwasawa |
1b1839 |
else // B
|
|
shun-iwasawa |
1b1839 |
(*result_p).z = exposure;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// in case of compositing both layers
|
|
shun-iwasawa |
1b1839 |
else {
|
|
shun-iwasawa |
1b1839 |
if (m_channel == 0) // R
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
(*result_p).x *= 1.0 - alpha;
|
|
shun-iwasawa |
1b1839 |
(*result_p).x += exposure;
|
|
shun-iwasawa |
1b1839 |
} else if (m_channel == 1) // G
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
(*result_p).y *= 1.0 - alpha;
|
|
shun-iwasawa |
1b1839 |
(*result_p).y += exposure;
|
|
shun-iwasawa |
1b1839 |
} else // B
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
(*result_p).z *= 1.0 - alpha;
|
|
shun-iwasawa |
1b1839 |
(*result_p).z += exposure;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
m_finished = true;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// normalize the source raster image to 0-1 and set to dstMem
|
|
shun-iwasawa |
1b1839 |
// returns true if the source is (seems to be) premultiplied
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
template void BokehUtils::setSourceRaster<traster32p, tpixel32="">(</traster32p,>
|
|
shun-iwasawa |
1b1839 |
const TRaster32P srcRas, double4* dstMem, TDimensionI dim);
|
|
shun-iwasawa |
1b1839 |
template void BokehUtils::setSourceRaster<traster64p, tpixel64="">(</traster64p,>
|
|
shun-iwasawa |
1b1839 |
const TRaster64P srcRas, double4* dstMem, TDimensionI dim);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::setSourceRaster(const RASTER srcRas, double4* dstMem,
|
|
shun-iwasawa |
1b1839 |
TDimensionI dim) {
|
|
shun-iwasawa |
1b1839 |
double4* chann_p = dstMem;
|
|
shun-iwasawa |
1b1839 |
for (int j = 0; j < dim.ly; j++) {
|
|
shun-iwasawa |
1b1839 |
PIXEL* pix = srcRas->pixels(j);
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dim.lx; i++, pix++, chann_p++) {
|
|
shun-iwasawa |
1b1839 |
(*chann_p).x = (double)pix->r / (double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
1b1839 |
(*chann_p).y = (double)pix->g / (double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
1b1839 |
(*chann_p).z = (double)pix->b / (double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
1b1839 |
(*chann_p).w = (double)pix->m / (double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// normalize brightness of the depth reference image to unsigned char
|
|
shun-iwasawa |
1b1839 |
// and store into detMem
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
template void BokehUtils::setDepthRaster<traster32p, tpixel32="">(</traster32p,>
|
|
shun-iwasawa |
1b1839 |
const TRaster32P srcRas, unsigned char* dstMem, TDimensionI dim);
|
|
shun-iwasawa |
1b1839 |
template void BokehUtils::setDepthRaster<traster64p, tpixel64="">(</traster64p,>
|
|
shun-iwasawa |
1b1839 |
const TRaster64P srcRas, unsigned char* dstMem, TDimensionI dim);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::setDepthRaster(const RASTER srcRas, unsigned char* dstMem,
|
|
shun-iwasawa |
1b1839 |
TDimensionI dim) {
|
|
shun-iwasawa |
1b1839 |
unsigned char* depth_p = dstMem;
|
|
shun-iwasawa |
1b1839 |
for (int j = 0; j < dim.ly; j++) {
|
|
shun-iwasawa |
1b1839 |
PIXEL* pix = srcRas->pixels(j);
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dim.lx; i++, pix++, depth_p++) {
|
|
shun-iwasawa |
1b1839 |
// normalize brightness to 0-1
|
|
shun-iwasawa |
1b1839 |
double val = ((double)pix->r * 0.3 + (double)pix->g * 0.59 +
|
|
shun-iwasawa |
1b1839 |
(double)pix->b * 0.11) /
|
|
shun-iwasawa |
1b1839 |
(double)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
1b1839 |
// convert to unsigned char
|
|
shun-iwasawa |
1b1839 |
(*depth_p) = (unsigned char)(val * (double)UCHAR_MAX + 0.5);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// create the depth index map
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::defineSegemntDepth(
|
|
shun-iwasawa |
1b1839 |
const unsigned char* indexMap_main, const unsigned char* indexMap_sub,
|
|
shun-iwasawa |
1b1839 |
const double* mainSub_ratio, const unsigned char* depth_buff,
|
|
shun-iwasawa |
1b1839 |
const TDimensionI& dimOut, QVector<double>& segmentDepth_main,</double>
|
|
shun-iwasawa |
1b1839 |
QVector<double>& segmentDepth_sub, const double focusDepth,</double>
|
|
shun-iwasawa |
1b1839 |
int distancePrecision, double nearDepth, double farDepth) {
|
|
shun-iwasawa |
1b1839 |
QSet<int> segmentValues;</int>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// histogram parameters
|
|
shun-iwasawa |
1b1839 |
struct HISTO {
|
|
shun-iwasawa |
1b1839 |
int pix_amount;
|
|
shun-iwasawa |
1b1839 |
int belongingSegmentValue; // value to which temporary segmented
|
|
shun-iwasawa |
1b1839 |
int segmentId;
|
|
shun-iwasawa |
1b1839 |
int segmentId_sub;
|
|
shun-iwasawa |
1b1839 |
};
|
|
shun-iwasawa |
1b1839 |
std::array<histo, 256=""> histo;</histo,>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// initialize
|
|
shun-iwasawa |
1b1839 |
for (int h = 0; h < 256; h++) {
|
|
shun-iwasawa |
1b1839 |
histo[h].pix_amount = 0;
|
|
shun-iwasawa |
1b1839 |
histo[h].belongingSegmentValue = -1;
|
|
shun-iwasawa |
1b1839 |
histo[h].segmentId = -1;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int size = dimOut.lx * dimOut.ly;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// max and min
|
|
shun-iwasawa |
1b1839 |
int minHisto = (int)UCHAR_MAX;
|
|
shun-iwasawa |
1b1839 |
int maxHisto = 0;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
unsigned char* depth_p = (unsigned char*)depth_buff;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, depth_p++) {
|
|
shun-iwasawa |
1b1839 |
histo[(int)*depth_p].pix_amount++;
|
|
shun-iwasawa |
1b1839 |
// update max and min
|
|
shun-iwasawa |
1b1839 |
if ((int)*depth_p < minHisto) minHisto = (int)*depth_p;
|
|
shun-iwasawa |
1b1839 |
if ((int)*depth_p > maxHisto) maxHisto = (int)*depth_p;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// the maximum and the minimum depth become the segment layers
|
|
shun-iwasawa |
1b1839 |
segmentValues.insert(minHisto);
|
|
shun-iwasawa |
1b1839 |
segmentValues.insert(maxHisto);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// The nearest depth in the histogram
|
|
shun-iwasawa |
1b1839 |
double minDepth =
|
|
shun-iwasawa |
1b1839 |
(farDepth - nearDepth) * (double)minHisto / 255.0 + nearDepth;
|
|
shun-iwasawa |
1b1839 |
// The furthest depth in the histogram
|
|
shun-iwasawa |
1b1839 |
double maxDepth =
|
|
shun-iwasawa |
1b1839 |
(farDepth - nearDepth) * (double)maxHisto / 255.0 + nearDepth;
|
|
shun-iwasawa |
1b1839 |
// focus depth becomes the segment layer as well
|
|
shun-iwasawa |
1b1839 |
if (minDepth < focusDepth && focusDepth < maxDepth)
|
|
shun-iwasawa |
1b1839 |
segmentValues.insert(std::round(
|
|
shun-iwasawa |
1b1839 |
(double)UCHAR_MAX * (focusDepth - nearDepth) / (farDepth - nearDepth)));
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// set the initial segmentation for each depth value
|
|
shun-iwasawa |
1b1839 |
for (int h = 0; h < 256; h++) {
|
|
shun-iwasawa |
1b1839 |
for (int seg = 0; seg < segmentValues.size(); seg++) {
|
|
shun-iwasawa |
1b1839 |
// set the segment
|
|
shun-iwasawa |
1b1839 |
if (histo[h].belongingSegmentValue == -1) {
|
|
shun-iwasawa |
1b1839 |
histo[h].belongingSegmentValue = segmentValues.values().at(seg);
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// error amount at the current registered layers
|
|
shun-iwasawa |
1b1839 |
int tmpError = std::abs(h - histo[h].belongingSegmentValue);
|
|
shun-iwasawa |
1b1839 |
if (tmpError == 0) break;
|
|
shun-iwasawa |
1b1839 |
// new error amount
|
|
shun-iwasawa |
1b1839 |
int newError = std::abs(h - segmentValues.values().at(seg));
|
|
shun-iwasawa |
1b1839 |
// compare the two and update
|
|
shun-iwasawa |
1b1839 |
if (newError < tmpError)
|
|
shun-iwasawa |
1b1839 |
histo[h].belongingSegmentValue = segmentValues.values().at(seg);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// add the segment layers to the distance precision value
|
|
shun-iwasawa |
1b1839 |
while (segmentValues.size() < distancePrecision) {
|
|
shun-iwasawa |
1b1839 |
// add a new segment at the value which will reduce the error amount in
|
|
shun-iwasawa |
1b1839 |
// maximum
|
|
shun-iwasawa |
1b1839 |
double tmpMaxErrorMod = 0;
|
|
shun-iwasawa |
1b1839 |
int tmpBestNewSegVal;
|
|
shun-iwasawa |
1b1839 |
bool newSegFound = false;
|
|
shun-iwasawa |
1b1839 |
for (int h = minHisto + 1; h < maxHisto; h++) {
|
|
shun-iwasawa |
1b1839 |
// if it is already set as the segment, continue
|
|
shun-iwasawa |
1b1839 |
if (histo[h].belongingSegmentValue == h) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double errorModAmount = 0;
|
|
shun-iwasawa |
1b1839 |
// estimate how much the error will be reduced if the current h becomes
|
|
shun-iwasawa |
1b1839 |
// segment
|
|
shun-iwasawa |
1b1839 |
for (int i = minHisto + 1; i < maxHisto; i++) {
|
|
shun-iwasawa |
1b1839 |
// compare the current segment value and h and take the nearest value
|
|
shun-iwasawa |
1b1839 |
// if h is near (from i), then accumulate the estimated error reduction
|
|
shun-iwasawa |
1b1839 |
// amount
|
|
shun-iwasawa |
1b1839 |
if (std::abs(i - histo[i].belongingSegmentValue) >
|
|
shun-iwasawa |
1b1839 |
std::abs(i - h)) // the current segment value has
|
|
luz paz |
6454c4 |
// priority, if the distance is the same
|
|
shun-iwasawa |
1b1839 |
errorModAmount +=
|
|
shun-iwasawa |
1b1839 |
(std::abs(i - histo[i].belongingSegmentValue) - std::abs(i - h)) *
|
|
shun-iwasawa |
1b1839 |
histo[i].pix_amount;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// if h will reduce the error, update the candidate segment value
|
|
shun-iwasawa |
1b1839 |
if (errorModAmount > tmpMaxErrorMod) {
|
|
shun-iwasawa |
1b1839 |
tmpMaxErrorMod = errorModAmount;
|
|
shun-iwasawa |
1b1839 |
tmpBestNewSegVal = h;
|
|
shun-iwasawa |
1b1839 |
newSegFound = true;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (!newSegFound) break;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// register tmpBestNewSegVal to the segment values list
|
|
shun-iwasawa |
1b1839 |
segmentValues.insert(tmpBestNewSegVal);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// update belongingSegmentValue
|
|
shun-iwasawa |
1b1839 |
for (int h = minHisto + 1; h < maxHisto; h++) {
|
|
shun-iwasawa |
1b1839 |
// compare the current segment value and h and take the nearest value
|
|
shun-iwasawa |
1b1839 |
// if tmpBestNewSegVal is near (from h), then update the
|
|
shun-iwasawa |
1b1839 |
// belongingSegmentValue
|
|
shun-iwasawa |
1b1839 |
if (std::abs(h - histo[h].belongingSegmentValue) >
|
|
shun-iwasawa |
1b1839 |
std::abs(h - tmpBestNewSegVal)) // the current segment value has
|
|
luz paz |
6454c4 |
// priority, if the distance is the same
|
|
shun-iwasawa |
1b1839 |
histo[h].belongingSegmentValue = tmpBestNewSegVal;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// set indices from the farthest and create the index table for each depth
|
|
shun-iwasawa |
1b1839 |
// value
|
|
shun-iwasawa |
1b1839 |
QVector<int> segValVec;</int>
|
|
shun-iwasawa |
1b1839 |
int tmpSegVal = -1;
|
|
shun-iwasawa |
1b1839 |
int tmpSegId = -1;
|
|
shun-iwasawa |
1b1839 |
for (int h = 255; h >= 0; h--) {
|
|
shun-iwasawa |
1b1839 |
if (histo[h].belongingSegmentValue != tmpSegVal) {
|
|
shun-iwasawa |
1b1839 |
segmentDepth_main.push_back((double)histo[h].belongingSegmentValue /
|
|
shun-iwasawa |
1b1839 |
(double)UCHAR_MAX);
|
|
shun-iwasawa |
1b1839 |
tmpSegVal = histo[h].belongingSegmentValue;
|
|
shun-iwasawa |
1b1839 |
tmpSegId++;
|
|
shun-iwasawa |
1b1839 |
segValVec.push_back(tmpSegVal);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
histo[h].segmentId = tmpSegId;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// "sub" depth segment value list for interporation
|
|
shun-iwasawa |
1b1839 |
for (int d = 0; d < segmentDepth_main.size() - 1; d++)
|
|
shun-iwasawa |
1b1839 |
segmentDepth_sub.push_back(
|
|
shun-iwasawa |
1b1839 |
(segmentDepth_main.at(d) + segmentDepth_main.at(d + 1)) / 2.0);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// create the "sub" index table for each depth value
|
|
shun-iwasawa |
1b1839 |
tmpSegId = 0;
|
|
shun-iwasawa |
1b1839 |
for (int seg = 0; seg < segValVec.size() - 1; seg++) {
|
|
shun-iwasawa |
1b1839 |
int hMax = (seg == 0) ? 255 : segValVec.at(seg);
|
|
shun-iwasawa |
1b1839 |
int hMin = (seg == segValVec.size() - 2) ? 0 : segValVec.at(seg + 1) + 1;
|
|
shun-iwasawa |
1b1839 |
for (int h = hMax; h >= hMin; h--) histo[h].segmentId_sub = tmpSegId;
|
|
shun-iwasawa |
1b1839 |
tmpSegId++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// convert the depth value to the segment index by using the index table
|
|
shun-iwasawa |
1b1839 |
depth_p = (unsigned char*)depth_buff;
|
|
shun-iwasawa |
1b1839 |
unsigned char* main_p = (unsigned char*)indexMap_main;
|
|
shun-iwasawa |
1b1839 |
unsigned char* sub_p = (unsigned char*)indexMap_sub;
|
|
shun-iwasawa |
1b1839 |
// mainSub_ratio represents the composition ratio of the image with "main"
|
|
shun-iwasawa |
1b1839 |
// separation.
|
|
shun-iwasawa |
1b1839 |
double* ratio_p = (double*)mainSub_ratio;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, depth_p++, main_p++, sub_p++, ratio_p++) {
|
|
shun-iwasawa |
1b1839 |
*main_p = (unsigned char)histo[(int)*depth_p].segmentId;
|
|
shun-iwasawa |
1b1839 |
*sub_p = (unsigned char)histo[(int)*depth_p].segmentId_sub;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double depth = (double)*depth_p / (double)UCHAR_MAX;
|
|
shun-iwasawa |
1b1839 |
double main_segDepth = segmentDepth_main.at(*main_p);
|
|
shun-iwasawa |
1b1839 |
double sub_segDepth = segmentDepth_sub.at(*sub_p);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (main_segDepth == sub_segDepth)
|
|
shun-iwasawa |
1b1839 |
*ratio_p = 1.0;
|
|
shun-iwasawa |
1b1839 |
else {
|
|
shun-iwasawa |
1b1839 |
*ratio_p = 1.0 - (main_segDepth - depth) / (main_segDepth - sub_segDepth);
|
|
shun-iwasawa |
1b1839 |
*ratio_p = clamp01(*ratio_p);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// convert source image value rgb -> exposure
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::convertRGBToExposure(const double4* source_buff, int size,
|
|
shun-iwasawa |
1b1839 |
double filmGamma) {
|
|
shun-iwasawa |
1b1839 |
double4* source_p = (double4*)source_buff;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, source_p++) {
|
|
shun-iwasawa |
1b1839 |
// continue if alpha channel is 0
|
|
shun-iwasawa |
1b1839 |
if ((*source_p).w == 0.0) {
|
|
shun-iwasawa |
1b1839 |
(*source_p).x = 0.0;
|
|
shun-iwasawa |
1b1839 |
(*source_p).y = 0.0;
|
|
shun-iwasawa |
1b1839 |
(*source_p).z = 0.0;
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// RGB value -> exposure
|
|
shun-iwasawa |
1b1839 |
(*source_p).x = valueToExposure((*source_p).x, filmGamma);
|
|
shun-iwasawa |
1b1839 |
(*source_p).y = valueToExposure((*source_p).y, filmGamma);
|
|
shun-iwasawa |
1b1839 |
(*source_p).z = valueToExposure((*source_p).z, filmGamma);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// multiply with alpha channel
|
|
shun-iwasawa |
1b1839 |
(*source_p).x *= (*source_p).w;
|
|
shun-iwasawa |
1b1839 |
(*source_p).y *= (*source_p).w;
|
|
shun-iwasawa |
1b1839 |
(*source_p).z *= (*source_p).w;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// convert result image value exposure -> rgb
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::convertExposureToRGB(const double4* result_buff, int size,
|
|
shun-iwasawa |
1b1839 |
double filmGamma) {
|
|
shun-iwasawa |
1b1839 |
double4* res_p = (double4*)result_buff;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, res_p++) {
|
|
shun-iwasawa |
1b1839 |
(*res_p).x = clamp01(exposureToValue((*res_p).x, filmGamma));
|
|
shun-iwasawa |
1b1839 |
(*res_p).y = clamp01(exposureToValue((*res_p).y, filmGamma));
|
|
shun-iwasawa |
1b1839 |
(*res_p).z = clamp01(exposureToValue((*res_p).z, filmGamma));
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//-----------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// obtain iris size from the depth value
|
|
shun-iwasawa |
1b1839 |
//-----------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
double BokehUtils::calcIrisSize(
|
|
shun-iwasawa |
1b1839 |
const double depth, // relative depth where near depth is set to 0 and far
|
|
shun-iwasawa |
1b1839 |
// depth is 1
|
|
shun-iwasawa |
1b1839 |
const double bokehPixelAmount, const double onFocusDistance,
|
|
shun-iwasawa |
1b1839 |
const double bokehAdjustment, double nearDepth,
|
|
shun-iwasawa |
1b1839 |
double farDepth) // actual depth of black and white reference pixels
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
double realDepth = nearDepth + (farDepth - nearDepth) * depth;
|
|
shun-iwasawa |
1b1839 |
return ((double)onFocusDistance - realDepth) * bokehPixelAmount *
|
|
shun-iwasawa |
1b1839 |
bokehAdjustment;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
namespace {
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// apply single median filter
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void doSingleMedian(const double4* source_buff,
|
|
shun-iwasawa |
1b1839 |
const double4* segment_layer_buff,
|
|
shun-iwasawa |
1b1839 |
const unsigned char* indexMap_mainSub, int index, int lx,
|
|
shun-iwasawa |
1b1839 |
int ly, const unsigned char* generation_buff, int curGen) {
|
|
shun-iwasawa |
1b1839 |
double4* source_p = (double4*)source_buff;
|
|
shun-iwasawa |
1b1839 |
double4* layer_p = (double4*)segment_layer_buff;
|
|
shun-iwasawa |
1b1839 |
unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
|
|
shun-iwasawa |
1b1839 |
unsigned char* gen_p = (unsigned char*)generation_buff;
|
|
shun-iwasawa |
1b1839 |
for (int posY = 0; posY < ly; posY++) {
|
|
shun-iwasawa |
1b1839 |
for (int posX = 0; posX < lx;
|
|
shun-iwasawa |
1b1839 |
posX++, source_p++, layer_p++, indexMap_p++, gen_p++) {
|
|
shun-iwasawa |
1b1839 |
// continue if the current pixel is at the same or far depth
|
|
shun-iwasawa |
1b1839 |
if ((int)(*indexMap_p) <= index) continue;
|
|
shun-iwasawa |
1b1839 |
// continue if the current pixel is already extended
|
|
shun-iwasawa |
1b1839 |
if ((*gen_p) > 0) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// check out the neighbor pixels. store brightness in neighbor[x].w
|
|
shun-iwasawa |
1b1839 |
double4 neighbor[8];
|
|
shun-iwasawa |
1b1839 |
int neighbor_amount = 0;
|
|
shun-iwasawa |
1b1839 |
for (int ky = posY - 1; ky <= posY + 1; ky++) {
|
|
shun-iwasawa |
1b1839 |
for (int kx = posX - 1; kx <= posX + 1; kx++) {
|
|
shun-iwasawa |
1b1839 |
// skip the current pixel itself
|
|
shun-iwasawa |
1b1839 |
if (kx == posX && ky == posY) continue;
|
|
shun-iwasawa |
1b1839 |
// border condition
|
|
shun-iwasawa |
1b1839 |
if (ky < 0 || ky >= ly || kx < 0 || kx >= lx) continue;
|
|
shun-iwasawa |
1b1839 |
// index in the buffer
|
|
shun-iwasawa |
1b1839 |
int neighborId = ky * lx + kx;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if ((int)indexMap_mainSub[neighborId] !=
|
|
shun-iwasawa |
1b1839 |
index && // pixels from the original image can be used as
|
|
shun-iwasawa |
1b1839 |
// neighbors
|
|
shun-iwasawa |
1b1839 |
(generation_buff[neighborId] == 0 || // pixels which is not yet
|
|
shun-iwasawa |
1b1839 |
// be extended cannot be
|
|
shun-iwasawa |
1b1839 |
// used as neighbors
|
|
shun-iwasawa |
1b1839 |
generation_buff[neighborId] == curGen)) // pixels which is
|
|
shun-iwasawa |
1b1839 |
// extended in the
|
|
shun-iwasawa |
1b1839 |
// current median
|
|
shun-iwasawa |
1b1839 |
// generation cannot be
|
|
shun-iwasawa |
1b1839 |
// used as neighbors
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
// compute brightness (actually, it is "pseudo" brightness
|
|
shun-iwasawa |
1b1839 |
// since the source buffer is already converted to exposure)
|
|
shun-iwasawa |
1b1839 |
double brightness = source_buff[neighborId].x * 0.3 +
|
|
shun-iwasawa |
1b1839 |
source_buff[neighborId].y * 0.59 +
|
|
shun-iwasawa |
1b1839 |
source_buff[neighborId].z * 0.11;
|
|
shun-iwasawa |
1b1839 |
// insert with sorting
|
|
shun-iwasawa |
1b1839 |
int ins_index;
|
|
shun-iwasawa |
1b1839 |
for (ins_index = 0; ins_index < neighbor_amount; ins_index++) {
|
|
shun-iwasawa |
1b1839 |
if (neighbor[ins_index].w < brightness) break;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// displace neighbor values from neighbor_amount-1 to ins_index
|
|
shun-iwasawa |
1b1839 |
for (int k = neighbor_amount - 1; k >= ins_index; k--) {
|
|
shun-iwasawa |
1b1839 |
neighbor[k + 1].x = neighbor[k].x;
|
|
shun-iwasawa |
1b1839 |
neighbor[k + 1].y = neighbor[k].y;
|
|
shun-iwasawa |
1b1839 |
neighbor[k + 1].z = neighbor[k].z;
|
|
shun-iwasawa |
1b1839 |
neighbor[k + 1].w = neighbor[k].w;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// set the neighbor value
|
|
shun-iwasawa |
1b1839 |
neighbor[ins_index].x = source_buff[neighborId].x;
|
|
shun-iwasawa |
1b1839 |
neighbor[ins_index].y = source_buff[neighborId].y;
|
|
shun-iwasawa |
1b1839 |
neighbor[ins_index].z = source_buff[neighborId].z;
|
|
shun-iwasawa |
1b1839 |
neighbor[ins_index].w = brightness;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// increment the count
|
|
shun-iwasawa |
1b1839 |
neighbor_amount++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// If there is no neighbor pixles available, continue
|
|
shun-iwasawa |
1b1839 |
if (neighbor_amount == 0) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// switch the behavior when there are even number of neighbors
|
|
shun-iwasawa |
1b1839 |
bool flag = ((posX + posY) % 2 == 0);
|
|
shun-iwasawa |
1b1839 |
// pick up the medium index
|
|
shun-iwasawa |
1b1839 |
int pickIndex = (flag)
|
|
shun-iwasawa |
1b1839 |
? (int)std::floor((double)(neighbor_amount - 1) / 2.0)
|
|
shun-iwasawa |
1b1839 |
: (int)std::ceil((double)(neighbor_amount - 1) / 2.0);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// set the medium pixel values
|
|
shun-iwasawa |
1b1839 |
(*layer_p).x = neighbor[pickIndex].x;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).y = neighbor[pickIndex].y;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).z = neighbor[pickIndex].z;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).w = (*source_p).w;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// set the generation
|
|
shun-iwasawa |
1b1839 |
(*gen_p) = (unsigned char)curGen;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
void doSingleExtend(const double4* source_buff,
|
|
shun-iwasawa |
1b1839 |
const double4* segment_layer_buff,
|
|
shun-iwasawa |
1b1839 |
const unsigned char* indexMap_mainSub, int index, int lx,
|
|
shun-iwasawa |
1b1839 |
int ly, const unsigned char* generation_buff, int curGen) {
|
|
shun-iwasawa |
1b1839 |
double4* source_p = (double4*)source_buff;
|
|
shun-iwasawa |
1b1839 |
double4* layer_p = (double4*)segment_layer_buff;
|
|
shun-iwasawa |
1b1839 |
unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
|
|
shun-iwasawa |
1b1839 |
unsigned char* gen_p = (unsigned char*)generation_buff;
|
|
shun-iwasawa |
1b1839 |
for (int posY = 0; posY < ly; posY++) {
|
|
shun-iwasawa |
1b1839 |
for (int posX = 0; posX < lx;
|
|
shun-iwasawa |
1b1839 |
posX++, source_p++, layer_p++, indexMap_p++, gen_p++) {
|
|
shun-iwasawa |
1b1839 |
// continue if the current pixel is at the same or far depth
|
|
shun-iwasawa |
1b1839 |
if ((int)(*indexMap_p) <= index) continue;
|
|
shun-iwasawa |
1b1839 |
// continue if the current pixel is already extended
|
|
shun-iwasawa |
1b1839 |
if ((*gen_p) > 0) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// check out the neighbor pixels. store brightness in neighbor[x].w
|
|
shun-iwasawa |
1b1839 |
double4 neighbor[8];
|
|
shun-iwasawa |
1b1839 |
bool neighbor_found = false;
|
|
shun-iwasawa |
1b1839 |
for (int ky = posY - 1; ky <= posY + 1; ky++) {
|
|
shun-iwasawa |
1b1839 |
for (int kx = posX - 1; kx <= posX + 1; kx++) {
|
|
shun-iwasawa |
1b1839 |
// skip the current pixel itself
|
|
shun-iwasawa |
1b1839 |
if (kx == posX && ky == posY) continue;
|
|
shun-iwasawa |
1b1839 |
// border condition
|
|
shun-iwasawa |
1b1839 |
if (ky < 0 || ky >= ly || kx < 0 || kx >= lx) continue;
|
|
shun-iwasawa |
1b1839 |
// index in the buffer
|
|
shun-iwasawa |
1b1839 |
int neighborId = ky * lx + kx;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if ((int)indexMap_mainSub[neighborId] !=
|
|
shun-iwasawa |
1b1839 |
index && // pixels from the original image can be used as
|
|
shun-iwasawa |
1b1839 |
// neighbors
|
|
shun-iwasawa |
1b1839 |
(generation_buff[neighborId] == 0 || // pixels which is not yet
|
|
shun-iwasawa |
1b1839 |
// be extended cannot be
|
|
shun-iwasawa |
1b1839 |
// used as neighbors
|
|
shun-iwasawa |
1b1839 |
generation_buff[neighborId] == curGen)) // pixels which is
|
|
shun-iwasawa |
1b1839 |
// extended in the
|
|
shun-iwasawa |
1b1839 |
// current median
|
|
shun-iwasawa |
1b1839 |
// generation cannot be
|
|
shun-iwasawa |
1b1839 |
// used as neighbors
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
// increment the count
|
|
shun-iwasawa |
1b1839 |
neighbor_found = true;
|
|
shun-iwasawa |
1b1839 |
break;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
if (neighbor_found) break;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// If there is no neighbor pixles available, continue
|
|
shun-iwasawa |
1b1839 |
if (!neighbor_found) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// set the medium pixel values
|
|
shun-iwasawa |
1b1839 |
(*layer_p).x = (*source_p).x;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).y = (*source_p).y;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).z = (*source_p).z;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).w = (*source_p).w;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// set the generation
|
|
shun-iwasawa |
1b1839 |
(*gen_p) = (unsigned char)curGen;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
} // namespace
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// generate the segment layer source at the current depth
|
|
shun-iwasawa |
1b1839 |
// considering fillGap and doMedian options
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::retrieveLayer(const double4* source_buff,
|
|
shun-iwasawa |
1b1839 |
const double4* segment_layer_buff,
|
|
shun-iwasawa |
1b1839 |
const unsigned char* indexMap_mainSub, int index,
|
|
shun-iwasawa |
1b1839 |
int lx, int ly, bool fillGap, bool doMedian,
|
|
shun-iwasawa |
1b1839 |
int margin) {
|
|
shun-iwasawa |
1b1839 |
// only when fillGap is ON and doMedian is OFF,
|
|
shun-iwasawa |
1b1839 |
// fill the region where will be behind of the near layers
|
|
shun-iwasawa |
1b1839 |
// bool fill = (fillGap && !doMedian);
|
|
shun-iwasawa |
1b1839 |
// retrieve the regions with the current depth
|
|
shun-iwasawa |
1b1839 |
double4* source_p = (double4*)source_buff;
|
|
shun-iwasawa |
1b1839 |
double4* layer_p = (double4*)segment_layer_buff;
|
|
shun-iwasawa |
1b1839 |
unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < lx * ly; i++, source_p++, layer_p++, indexMap_p++) {
|
|
shun-iwasawa |
1b1839 |
// continue if the current pixel is at the far layer
|
|
shun-iwasawa |
1b1839 |
// consider the fill flag if the current pixel is at the near layer
|
|
shun-iwasawa |
1b1839 |
// if ((int)(*indexMap_p) < index || (!fill && (int)(*indexMap_p) > index))
|
|
shun-iwasawa |
1b1839 |
if ((int)(*indexMap_p) != index) continue;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// copy pixel values
|
|
shun-iwasawa |
1b1839 |
(*layer_p).x = (*source_p).x;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).y = (*source_p).y;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).z = (*source_p).z;
|
|
shun-iwasawa |
1b1839 |
(*layer_p).w = (*source_p).w;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (!fillGap && !doMedian) return;
|
|
shun-iwasawa |
1b1839 |
if (margin == 0) return;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// extend pixels by using median filter
|
|
shun-iwasawa |
1b1839 |
unsigned char* generation_buff;
|
|
shun-iwasawa |
1b1839 |
TRasterGR8P generation_buff_ras = allocateRasterAndLock<unsigned char="">(</unsigned>
|
|
shun-iwasawa |
1b1839 |
&generation_buff, TDimensionI(lx, ly));
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// extend (margin * 2) pixels in order to enough cover when two adjacent
|
|
shun-iwasawa |
1b1839 |
// layers are both blurred in the maximum radius
|
|
shun-iwasawa |
1b1839 |
for (int gen = 0; gen < margin * 2; gen++) {
|
|
shun-iwasawa |
1b1839 |
if (doMedian)
|
|
shun-iwasawa |
1b1839 |
// apply single median filter
|
|
shun-iwasawa |
1b1839 |
doSingleMedian(source_buff, segment_layer_buff, indexMap_mainSub, index,
|
|
shun-iwasawa |
1b1839 |
lx, ly, generation_buff, gen + 1);
|
|
shun-iwasawa |
1b1839 |
else
|
|
shun-iwasawa |
1b1839 |
// put the source pixel as-is
|
|
shun-iwasawa |
1b1839 |
doSingleExtend(source_buff, segment_layer_buff, indexMap_mainSub, index,
|
|
shun-iwasawa |
1b1839 |
lx, ly, generation_buff, gen + 1);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
generation_buff_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// normal-composite the layer as is, without filtering
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::compositeAsIs(const double4* segment_layer_buff,
|
|
shun-iwasawa |
1b1839 |
const double4* result_buff_mainSub, int size) {
|
|
shun-iwasawa |
1b1839 |
double4* layer_p = (double4*)segment_layer_buff;
|
|
shun-iwasawa |
1b1839 |
double4* result_p = (double4*)result_buff_mainSub;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, layer_p++, result_p++) {
|
|
shun-iwasawa |
1b1839 |
// in case the pixel is full opac
|
|
shun-iwasawa |
1b1839 |
if ((*layer_p).w == 1.0f) {
|
|
shun-iwasawa |
1b1839 |
(*result_p).x = (*layer_p).x;
|
|
shun-iwasawa |
1b1839 |
(*result_p).y = (*layer_p).y;
|
|
shun-iwasawa |
1b1839 |
(*result_p).z = (*layer_p).z;
|
|
shun-iwasawa |
1b1839 |
(*result_p).w = 1.0f;
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// in case the pixel is full transparent
|
|
shun-iwasawa |
1b1839 |
else if ((*layer_p).w == 0.0f)
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
// in case the pixel is semi-transparent, do normal composite
|
|
shun-iwasawa |
1b1839 |
else {
|
|
shun-iwasawa |
1b1839 |
(*result_p).x = (*layer_p).x + (*result_p).x * (1.0 - (*layer_p).w);
|
|
shun-iwasawa |
1b1839 |
(*result_p).y = (*layer_p).y + (*result_p).y * (1.0 - (*layer_p).w);
|
|
shun-iwasawa |
1b1839 |
(*result_p).z = (*layer_p).z + (*result_p).z * (1.0 - (*layer_p).w);
|
|
shun-iwasawa |
1b1839 |
(*result_p).w = (*layer_p).w + (*result_p).w * (1.0 - (*layer_p).w);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// Resize / flip the iris image according to the size ratio.
|
|
shun-iwasawa |
1b1839 |
// Normalize the brightness of the iris image.
|
|
shun-iwasawa |
1b1839 |
// Enlarge the iris to the output size.
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::convertIris(const double irisSize,
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* kissfft_comp_iris_before,
|
|
shun-iwasawa |
1b1839 |
const TDimensionI& dimOut, const TRectD& irisBBox,
|
|
shun-iwasawa |
1b1839 |
const TTile& irisTile) {
|
|
shun-iwasawa |
1b1839 |
// the original size of iris image
|
|
shun-iwasawa |
1b1839 |
double2 irisOrgSize = {irisBBox.getLx(), irisBBox.getLy()};
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Get the size ratio of iris based on width. The ratio can be negative value.
|
|
shun-iwasawa |
1b1839 |
double irisSizeResampleRatio = irisSize / irisOrgSize.x;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Create the raster for resized iris
|
|
shun-iwasawa |
1b1839 |
double2 resizedIrisSize = {std::abs(irisSizeResampleRatio) * irisOrgSize.x,
|
|
shun-iwasawa |
1b1839 |
std::abs(irisSizeResampleRatio) * irisOrgSize.y};
|
|
shun-iwasawa |
1b1839 |
// add 1 pixel margins to all sides
|
|
shun-iwasawa |
1b1839 |
int2 filterSize = {int(std::ceil(resizedIrisSize.x)) + 2,
|
|
shun-iwasawa |
1b1839 |
int(std::ceil(resizedIrisSize.y)) + 2};
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
TPointD resizeOffset((double)filterSize.x - resizedIrisSize.x,
|
|
shun-iwasawa |
1b1839 |
(double)filterSize.y - resizedIrisSize.y);
|
|
shun-iwasawa |
1b1839 |
// Add some adjustment in order to absorb the difference of the cases when the
|
|
shun-iwasawa |
1b1839 |
// iris size is odd and even numbers.
|
|
shun-iwasawa |
1b1839 |
// Try to set the center of the iris to the center of the screen
|
|
shun-iwasawa |
1b1839 |
if ((dimOut.lx - filterSize.x) % 2 == 1) filterSize.x++;
|
|
shun-iwasawa |
1b1839 |
if ((dimOut.ly - filterSize.y) % 2 == 1) filterSize.y++;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Terminate if the filter size becomes bigger than the output size.
|
|
shun-iwasawa |
1b1839 |
if (filterSize.x > dimOut.lx || filterSize.y > dimOut.ly) {
|
|
shun-iwasawa |
1b1839 |
std::cout
|
|
shun-iwasawa |
1b1839 |
<< "Error: The iris filter size becomes larger than the source size!"
|
|
shun-iwasawa |
1b1839 |
<< std::endl;
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
TRaster64P resizedIris(TDimension(filterSize.x, filterSize.y));
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Add some adjustment in order to absorb the 0.5 translation to be done in
|
|
shun-iwasawa |
1b1839 |
// resample()
|
|
shun-iwasawa |
1b1839 |
TAffine aff;
|
|
shun-iwasawa |
1b1839 |
TPointD affOffset(0.5, 0.5);
|
|
shun-iwasawa |
1b1839 |
affOffset += TPointD((dimOut.lx % 2 == 1) ? 0.5 : 0.0,
|
|
shun-iwasawa |
1b1839 |
(dimOut.ly % 2 == 1) ? 0.5 : 0.0);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
aff = TTranslation(resizedIris->getCenterD() + affOffset);
|
|
shun-iwasawa |
1b1839 |
aff *= TScale(irisSizeResampleRatio);
|
|
shun-iwasawa |
1b1839 |
aff *= TTranslation(-(irisTile.getRaster()->getCenterD() + affOffset));
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// resample the iris
|
|
shun-iwasawa |
1b1839 |
TRop::resample(resizedIris, irisTile.getRaster(), aff);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// accumulated value
|
|
shun-iwasawa |
1b1839 |
float irisValAmount = 0.0f;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int iris_j = 0;
|
|
shun-iwasawa |
1b1839 |
// Initialize
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris_before[i].r = 0.0f;
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris_before[i].i = 0.0f;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
for (int j = (dimOut.ly - filterSize.y) / 2; iris_j < filterSize.y;
|
|
shun-iwasawa |
1b1839 |
j++, iris_j++) {
|
|
shun-iwasawa |
1b1839 |
TPixel64* pix = resizedIris->pixels(iris_j);
|
|
shun-iwasawa |
1b1839 |
int iris_i = 0;
|
|
shun-iwasawa |
1b1839 |
for (int i = (dimOut.lx - filterSize.x) / 2; iris_i < filterSize.x;
|
|
shun-iwasawa |
1b1839 |
i++, iris_i++) {
|
|
shun-iwasawa |
1b1839 |
// Value = 0.3R 0.59G 0.11B
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris_before[j * dimOut.lx + i].r =
|
|
shun-iwasawa |
1b1839 |
((float)pix->r * 0.3f + (float)pix->g * 0.59f +
|
|
shun-iwasawa |
1b1839 |
(float)pix->b * 0.11f) /
|
|
shun-iwasawa |
1b1839 |
(float)USHRT_MAX;
|
|
shun-iwasawa |
1b1839 |
irisValAmount += kissfft_comp_iris_before[j * dimOut.lx + i].r;
|
|
shun-iwasawa |
1b1839 |
pix++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Normalize value
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris_before[i].r /= irisValAmount;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// retrieve segment layer image for each channel
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::retrieveChannel(const double4* segment_layer_buff, // src
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_r_before, // dst
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_g_before, // dst
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_b_before, // dst
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_a_before, // dst
|
|
shun-iwasawa |
1b1839 |
int size) {
|
|
shun-iwasawa |
1b1839 |
double4* layer_p = (double4*)segment_layer_buff;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, layer_p++) {
|
|
shun-iwasawa |
1b1839 |
fftcpx_r_before[i].r = (*layer_p).x;
|
|
shun-iwasawa |
1b1839 |
fftcpx_g_before[i].r = (*layer_p).y;
|
|
shun-iwasawa |
1b1839 |
fftcpx_b_before[i].r = (*layer_p).z;
|
|
shun-iwasawa |
1b1839 |
fftcpx_a_before[i].r = (*layer_p).w;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// multiply filter on channel
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::multiplyFilter(kiss_fft_cpx* fftcpx_channel, // dst
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_iris, // filter
|
|
shun-iwasawa |
1b1839 |
int size) {
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++) {
|
|
shun-iwasawa |
1b1839 |
float re, im;
|
|
shun-iwasawa |
1b1839 |
re = fftcpx_channel[i].r * fftcpx_iris[i].r -
|
|
shun-iwasawa |
1b1839 |
fftcpx_channel[i].i * fftcpx_iris[i].i;
|
|
shun-iwasawa |
1b1839 |
im = fftcpx_channel[i].r * fftcpx_iris[i].i +
|
|
shun-iwasawa |
1b1839 |
fftcpx_channel[i].i * fftcpx_iris[i].r;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
fftcpx_channel[i].r = re;
|
|
shun-iwasawa |
1b1839 |
fftcpx_channel[i].i = im;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// normal comosite the alpha channel
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::compositeAlpha(const double4* result_buff, // dst
|
|
shun-iwasawa |
1b1839 |
const kiss_fft_cpx* fftcpx_alpha, // alpha
|
|
shun-iwasawa |
1b1839 |
int lx, int ly) {
|
|
shun-iwasawa |
1b1839 |
int size = lx * ly;
|
|
shun-iwasawa |
1b1839 |
double4* result_p = (double4*)result_buff;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size; i++, result_p++) {
|
|
shun-iwasawa |
1b1839 |
// modify fft coordinate to normal
|
|
shun-iwasawa |
1b1839 |
double alpha = (double)fftcpx_alpha[getCoord(i, lx, ly)].r / (double)size;
|
|
shun-iwasawa |
1b1839 |
alpha = clamp01(alpha);
|
|
shun-iwasawa |
1b1839 |
(*result_p).w = alpha + ((*result_p).w * (1.0 - alpha));
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// interpolate main and sub exposures
|
|
shun-iwasawa |
1b1839 |
// set to result
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::interpolateExposureAndConvertToRGB(
|
|
shun-iwasawa |
1b1839 |
const double4* result_main_buff, // result1
|
|
shun-iwasawa |
1b1839 |
const double4* result_sub_buff, // result2
|
|
shun-iwasawa |
1b1839 |
const double* mainSub_ratio, // ratio
|
|
shun-iwasawa |
1b1839 |
const double4* result_buff, // dst
|
|
shun-iwasawa |
1b1839 |
int size, double layerHardnessRatio) {
|
|
shun-iwasawa |
1b1839 |
double4* resultMain_p = (double4*)result_main_buff;
|
|
shun-iwasawa |
1b1839 |
double4* resultSub_p = (double4*)result_sub_buff;
|
|
shun-iwasawa |
1b1839 |
double* ratio_p = (double*)mainSub_ratio;
|
|
shun-iwasawa |
1b1839 |
double4* out_p = (double4*)result_buff;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < size;
|
|
shun-iwasawa |
1b1839 |
i++, resultMain_p++, resultSub_p++, ratio_p++, out_p++) {
|
|
shun-iwasawa |
1b1839 |
// interpolate main and sub exposures
|
|
shun-iwasawa |
1b1839 |
double4 result;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
result.x =
|
|
shun-iwasawa |
1b1839 |
(*resultMain_p).x * (*ratio_p) + (*resultSub_p).x * (1.0 - (*ratio_p));
|
|
shun-iwasawa |
1b1839 |
result.y =
|
|
shun-iwasawa |
1b1839 |
(*resultMain_p).y * (*ratio_p) + (*resultSub_p).y * (1.0 - (*ratio_p));
|
|
shun-iwasawa |
1b1839 |
result.z =
|
|
shun-iwasawa |
1b1839 |
(*resultMain_p).z * (*ratio_p) + (*resultSub_p).z * (1.0 - (*ratio_p));
|
|
shun-iwasawa |
1b1839 |
result.w =
|
|
shun-iwasawa |
1b1839 |
(*resultMain_p).w * (*ratio_p) + (*resultSub_p).w * (1.0 - (*ratio_p));
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// continue for transparent pixel
|
|
shun-iwasawa |
1b1839 |
if (result.w == 0.0) {
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// convert exposure by layer hardness
|
|
shun-iwasawa |
1b1839 |
if (layerHardnessRatio != 1.0) {
|
|
shun-iwasawa |
efd92b |
result.x = std::pow(result.x / result.w, layerHardnessRatio) * result.w;
|
|
shun-iwasawa |
efd92b |
result.y = std::pow(result.y / result.w, layerHardnessRatio) * result.w;
|
|
shun-iwasawa |
efd92b |
result.z = std::pow(result.z / result.w, layerHardnessRatio) * result.w;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// in case the result is replaced by the upper layer pixel
|
|
shun-iwasawa |
1b1839 |
if (result.w >= 1.0f) {
|
|
shun-iwasawa |
1b1839 |
(*out_p).x = result.x;
|
|
shun-iwasawa |
1b1839 |
(*out_p).y = result.y;
|
|
shun-iwasawa |
1b1839 |
(*out_p).z = result.z;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// in case the layers will be composed
|
|
shun-iwasawa |
1b1839 |
else {
|
|
shun-iwasawa |
1b1839 |
(*out_p).x = (*out_p).x * (1.0 - result.w) + result.x;
|
|
shun-iwasawa |
1b1839 |
(*out_p).y = (*out_p).y * (1.0 - result.w) + result.y;
|
|
shun-iwasawa |
1b1839 |
(*out_p).z = (*out_p).z * (1.0 - result.w) + result.z;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
(*out_p).w = (*out_p).w * (1.0 - result.w) + result.w;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
//"Over" composite the layer to the output exposure.
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::compositLayerAsIs(TTile& layerTile, double4* result,
|
|
shun-iwasawa |
1b1839 |
TDimensionI& dimOut, double filmGamma) {
|
|
shun-iwasawa |
1b1839 |
double4* layer_buff;
|
|
shun-iwasawa |
1b1839 |
TRasterGR8P layer_buff_ras(dimOut.lx * sizeof(double4), dimOut.ly);
|
|
shun-iwasawa |
1b1839 |
layer_buff_ras->lock();
|
|
shun-iwasawa |
1b1839 |
layer_buff = (double4*)layer_buff_ras->getRawData();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
TRaster32P ras32 = (TRaster32P)layerTile.getRaster();
|
|
shun-iwasawa |
1b1839 |
TRaster64P ras64 = (TRaster64P)layerTile.getRaster();
|
|
shun-iwasawa |
1b1839 |
lock.lockForRead();
|
|
shun-iwasawa |
1b1839 |
if (ras32)
|
|
shun-iwasawa |
1b1839 |
BokehUtils::setSourceRaster<traster32p, tpixel32="">(ras32, layer_buff,</traster32p,>
|
|
shun-iwasawa |
1b1839 |
dimOut);
|
|
shun-iwasawa |
1b1839 |
else if (ras64)
|
|
shun-iwasawa |
1b1839 |
BokehUtils::setSourceRaster<traster64p, tpixel64="">(ras64, layer_buff,</traster64p,>
|
|
shun-iwasawa |
1b1839 |
dimOut);
|
|
shun-iwasawa |
1b1839 |
lock.unlock();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double4* lay_p = layer_buff;
|
|
shun-iwasawa |
1b1839 |
double4* res_p = result;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dimOut.lx * dimOut.ly; i++, lay_p++, res_p++) {
|
|
shun-iwasawa |
1b1839 |
if ((*lay_p).w <= 0.0)
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
else if ((*lay_p).w < 1.0) {
|
|
shun-iwasawa |
1b1839 |
// composite exposure
|
|
shun-iwasawa |
1b1839 |
(*res_p).x = valueToExposure((*lay_p).x, filmGamma) * (*lay_p).w +
|
|
shun-iwasawa |
1b1839 |
(*res_p).x * (1.0 - (*lay_p).w);
|
|
shun-iwasawa |
1b1839 |
(*res_p).y = valueToExposure((*lay_p).y, filmGamma) * (*lay_p).w +
|
|
shun-iwasawa |
1b1839 |
(*res_p).y * (1.0 - (*lay_p).w);
|
|
shun-iwasawa |
1b1839 |
(*res_p).z = valueToExposure((*lay_p).z, filmGamma) * (*lay_p).w +
|
|
shun-iwasawa |
1b1839 |
(*res_p).z * (1.0 - (*lay_p).w);
|
|
shun-iwasawa |
1b1839 |
// over composite alpha
|
|
shun-iwasawa |
1b1839 |
(*res_p).w = (*lay_p).w + ((*res_p).w * (1.0 - (*lay_p).w));
|
|
shun-iwasawa |
1b1839 |
(*res_p).w = clamp01((*res_p).w);
|
|
shun-iwasawa |
1b1839 |
} else // replace by upper layer
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
(*res_p).x = valueToExposure((*lay_p).x, filmGamma);
|
|
shun-iwasawa |
1b1839 |
(*res_p).y = valueToExposure((*lay_p).y, filmGamma);
|
|
shun-iwasawa |
1b1839 |
(*res_p).z = valueToExposure((*lay_p).z, filmGamma);
|
|
shun-iwasawa |
1b1839 |
(*res_p).w = 1.0;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
layer_buff_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// Do FFT the alpha channel.
|
|
shun-iwasawa |
1b1839 |
// Forward FFT -> Multiply by the iris data -> Backward FFT
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::calcAlfaChannelBokeh(kiss_fft_cpx* kissfft_comp_iris,
|
|
shun-iwasawa |
1b1839 |
TTile& layerTile, double* alpha_bokeh) {
|
|
shun-iwasawa |
1b1839 |
// Obtain the source size
|
|
shun-iwasawa |
1b1839 |
int lx, ly;
|
|
shun-iwasawa |
1b1839 |
lx = layerTile.getRaster()->getSize().lx;
|
|
shun-iwasawa |
1b1839 |
ly = layerTile.getRaster()->getSize().ly;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Allocate the FFT data
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx *kissfft_comp_in, *kissfft_comp_out;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
TRasterGR8P kissfft_comp_in_ras(lx * sizeof(kiss_fft_cpx), ly);
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_in_ras->lock();
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_in = (kiss_fft_cpx*)kissfft_comp_in_ras->getRawData();
|
|
shun-iwasawa |
1b1839 |
TRasterGR8P kissfft_comp_out_ras(lx * sizeof(kiss_fft_cpx), ly);
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_out_ras->lock();
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_out = (kiss_fft_cpx*)kissfft_comp_out_ras->getRawData();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Initialize the FFT data
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < lx * ly; i++) {
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_in[i].r = 0.0; // real part
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_in[i].i = 0.0; // imaginary part
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
TRaster32P ras32 = (TRaster32P)layerTile.getRaster();
|
|
shun-iwasawa |
1b1839 |
TRaster64P ras64 = (TRaster64P)layerTile.getRaster();
|
|
shun-iwasawa |
1b1839 |
if (ras32) {
|
|
shun-iwasawa |
1b1839 |
for (int j = 0; j < ly; j++) {
|
|
shun-iwasawa |
1b1839 |
TPixel32* pix = ras32->pixels(j);
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < lx; i++) {
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_in[j * lx + i].r = (double)pix->m / (double)UCHAR_MAX;
|
|
shun-iwasawa |
1b1839 |
pix++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
} else if (ras64) {
|
|
shun-iwasawa |
1b1839 |
for (int j = 0; j < ly; j++) {
|
|
shun-iwasawa |
1b1839 |
TPixel64* pix = ras64->pixels(j);
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < lx; i++) {
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_in[j * lx + i].r = (double)pix->m / (double)USHRT_MAX;
|
|
shun-iwasawa |
1b1839 |
pix++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
} else
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int dims[2] = {ly, lx};
|
|
shun-iwasawa |
1b1839 |
int ndims = 2;
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(plan_fwd, kissfft_comp_in, kissfft_comp_out);
|
|
shun-iwasawa |
1b1839 |
kiss_fft_free(plan_fwd); // we don't need this plan anymore
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Filtering. Multiply by the iris FFT data
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < lx * ly; i++) {
|
|
shun-iwasawa |
1b1839 |
float re, im;
|
|
shun-iwasawa |
1b1839 |
re = kissfft_comp_out[i].r * kissfft_comp_iris[i].r -
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_out[i].i * kissfft_comp_iris[i].i;
|
|
shun-iwasawa |
1b1839 |
im = kissfft_comp_out[i].r * kissfft_comp_iris[i].i +
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris[i].r * kissfft_comp_out[i].i;
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_out[i].r = re;
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_out[i].i = im;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(plan_bkwd, kissfft_comp_out, kissfft_comp_in); // Backward FFT
|
|
shun-iwasawa |
1b1839 |
kiss_fft_free(plan_bkwd); // we don't need this plan anymore
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// In the backward FFT above, "kissfft_comp_out" is used as input and
|
|
shun-iwasawa |
1b1839 |
// "kissfft_comp_in" as output.
|
|
shun-iwasawa |
1b1839 |
// So we don't need "kissfft_comp_out" anymore.
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_out_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// store to the buffer
|
|
shun-iwasawa |
1b1839 |
double* alp_p = alpha_bokeh;
|
|
shun-iwasawa |
1b1839 |
for (int j = 0; j < ly; j++) {
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < lx; i++, alp_p++) {
|
|
shun-iwasawa |
1b1839 |
(*alp_p) = (double)kissfft_comp_in[getCoord(j * lx + i, lx, ly)].r /
|
|
shun-iwasawa |
1b1839 |
(double)(lx * ly);
|
|
shun-iwasawa |
1b1839 |
(*alp_p) = clamp01(*alp_p);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_in_ras->unlock();
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//-----------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// convert to channel value and set to output
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
template void BokehUtils::setOutputRaster<traster32p, tpixel32="">(</traster32p,>
|
|
shun-iwasawa |
1b1839 |
double4* src, const TRaster32P dstRas, TDimensionI& dim, int2 margin);
|
|
shun-iwasawa |
1b1839 |
template void BokehUtils::setOutputRaster<traster64p, tpixel64="">(</traster64p,>
|
|
shun-iwasawa |
1b1839 |
double4* src, const TRaster64P dstRas, TDimensionI& dim, int2 margin);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
1b1839 |
void BokehUtils::setOutputRaster(double4* src, const RASTER dstRas,
|
|
shun-iwasawa |
1b1839 |
TDimensionI& dim, int2 margin) {
|
|
shun-iwasawa |
1b1839 |
double4* src_p = src + (margin.y * dim.lx);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
for (int j = 0; j < dstRas->getLy(); j++) {
|
|
shun-iwasawa |
1b1839 |
PIXEL* outPix = dstRas->pixels(j);
|
|
shun-iwasawa |
1b1839 |
src_p += margin.x;
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < dstRas->getLx(); i++, outPix++, src_p++) {
|
|
shun-iwasawa |
1b1839 |
double val;
|
|
shun-iwasawa |
1b1839 |
val = (*src_p).x * (double)PIXEL::maxChannelValue + 0.5;
|
|
shun-iwasawa |
1b1839 |
outPix->r =
|
|
shun-iwasawa |
1b1839 |
(typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
1b1839 |
? (double)PIXEL::maxChannelValue
|
|
shun-iwasawa |
1b1839 |
: val);
|
|
shun-iwasawa |
1b1839 |
val = (*src_p).y * (double)PIXEL::maxChannelValue + 0.5;
|
|
shun-iwasawa |
1b1839 |
outPix->g =
|
|
shun-iwasawa |
1b1839 |
(typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
1b1839 |
? (double)PIXEL::maxChannelValue
|
|
shun-iwasawa |
1b1839 |
: val);
|
|
shun-iwasawa |
1b1839 |
val = (*src_p).z * (double)PIXEL::maxChannelValue + 0.5;
|
|
shun-iwasawa |
1b1839 |
outPix->b =
|
|
shun-iwasawa |
1b1839 |
(typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
1b1839 |
? (double)PIXEL::maxChannelValue
|
|
shun-iwasawa |
1b1839 |
: val);
|
|
shun-iwasawa |
1b1839 |
val = (*src_p).w * (double)PIXEL::maxChannelValue + 0.5;
|
|
shun-iwasawa |
1b1839 |
outPix->m =
|
|
shun-iwasawa |
1b1839 |
(typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
1b1839 |
? (double)PIXEL::maxChannelValue
|
|
shun-iwasawa |
1b1839 |
: val);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
src_p += margin.x;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//-----------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
// Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
|
|
shun-iwasawa |
1b1839 |
double BokehUtils::getBokehPixelAmount(const double bokehAmount,
|
|
shun-iwasawa |
1b1839 |
const TAffine affine) {
|
|
shun-iwasawa |
1b1839 |
/*--- Convert to vector --- */
|
|
shun-iwasawa |
1b1839 |
TPointD vect(bokehAmount, 0.0);
|
|
shun-iwasawa |
1b1839 |
/*--- Apply geometrical transformation ---*/
|
|
shun-iwasawa |
1b1839 |
// For the following lines I referred to lines 586-592 of
|
|
shun-iwasawa |
1b1839 |
// sources/stdfx/motionblurfx.cpp
|
|
shun-iwasawa |
1b1839 |
TAffine aff(affine);
|
|
shun-iwasawa |
1b1839 |
aff.a13 = aff.a23 = 0; /* ignore translation */
|
|
shun-iwasawa |
1b1839 |
vect = aff * vect;
|
|
shun-iwasawa |
1b1839 |
/*--- return the length of the vector ---*/
|
|
shun-iwasawa |
1b1839 |
return std::sqrt(vect.x * vect.x + vect.y * vect.y);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//-----------------------------------------------------
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
Iwa_BokehCommonFx::Iwa_BokehCommonFx()
|
|
shun-iwasawa |
1b1839 |
: m_onFocusDistance(0.5), m_bokehAmount(30.0), m_hardness(0.3) {
|
|
shun-iwasawa |
1b1839 |
addInputPort("Iris", m_iris);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Set the ranges of common parameters
|
|
shun-iwasawa |
1b1839 |
m_onFocusDistance->setValueRange(0.0, 10.);
|
|
shun-iwasawa |
1b1839 |
m_bokehAmount->setValueRange(0.0, 300.0);
|
|
shun-iwasawa |
1b1839 |
m_bokehAmount->setMeasureName("fxLength");
|
|
shun-iwasawa |
1b1839 |
m_hardness->setValueRange(0.05, 3.0);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
bool Iwa_BokehCommonFx::doGetBBox(double frame, TRectD& bBox,
|
|
shun-iwasawa |
1b1839 |
const TRenderSettings& info) {
|
|
shun-iwasawa |
1b1839 |
bBox = TConsts::infiniteRectD;
|
|
shun-iwasawa |
1b1839 |
return true;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
bool Iwa_BokehCommonFx::canHandle(const TRenderSettings& info, double frame) {
|
|
shun-iwasawa |
1b1839 |
return false;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//--------------------------------------------
|
|
shun-iwasawa |
1b1839 |
void Iwa_BokehCommonFx::doFx(TTile& tile, double frame,
|
|
shun-iwasawa |
1b1839 |
const TRenderSettings& settings,
|
|
shun-iwasawa |
1b1839 |
double bokehPixelAmount, int margin,
|
|
shun-iwasawa |
1b1839 |
TDimensionI& dimOut, TRectD& irisBBox,
|
|
shun-iwasawa |
1b1839 |
TTile& irisTile, QList<layervalue>& layerValues,</layervalue>
|
|
shun-iwasawa |
1b1839 |
QMap<int, char*="" unsigned="">& ctrls) {</int,>
|
|
shun-iwasawa |
1b1839 |
// This fx is relatively heavy so the multi thread computation is introduced.
|
|
shun-iwasawa |
1b1839 |
// Lock the mutex here in order to prevent multiple rendering tasks run at the
|
|
shun-iwasawa |
1b1839 |
// same time.
|
|
shun-iwasawa |
1b1839 |
// QMutexLocker fx_locker(&fx_mutex);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
QList<trastergr8p> rasterList;</trastergr8p>
|
|
shun-iwasawa |
1b1839 |
QList<kiss_fftnd_cfg> planList;</kiss_fftnd_cfg>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* kissfft_comp_iris;
|
|
shun-iwasawa |
1b1839 |
double* alpha_bokeh = nullptr;
|
|
shun-iwasawa |
1b1839 |
double4* result = nullptr;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<kiss_fft_cpx>(&kissfft_comp_iris, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<double>(&alpha_bokeh, dimOut));</double>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<double4>(&result, dimOut));</double4>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double4 zero = {0.0, 0.0, 0.0, 0.0};
|
|
shun-iwasawa |
1b1839 |
// initialize
|
|
shun-iwasawa |
1b1839 |
std::fill_n(result, dimOut.lx * dimOut.ly, zero);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double masterHardness = m_hardness->getValue(frame);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// compute layers from further to nearer
|
|
shun-iwasawa |
1b1839 |
for (int i = 0; i < layerValues.size(); i++) {
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
LayerValue layer = layerValues.at(i);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//-------------------
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// separate layer by the reference image
|
|
shun-iwasawa |
1b1839 |
int ctrlIndex = layer.depth_ref;
|
|
shun-iwasawa |
1b1839 |
if (ctrlIndex > 0 && ctrls.contains(ctrlIndex) && ctrls[ctrlIndex] != 0) {
|
|
shun-iwasawa |
1b1839 |
TTile* layerTile = layer.sourceTile;
|
|
shun-iwasawa |
1b1839 |
if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster());
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
doBokehRef(result, frame, settings, bokehPixelAmount, margin, dimOut,
|
|
shun-iwasawa |
1b1839 |
irisBBox, irisTile, kissfft_comp_iris, layer,
|
|
shun-iwasawa |
1b1839 |
ctrls[ctrlIndex]);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//-------------------
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double layerHardness = layer.layerHardness;
|
|
shun-iwasawa |
1b1839 |
// The iris size of the current layer
|
|
shun-iwasawa |
1b1839 |
double irisSize = layer.irisSize;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// in case the layer is at the focus
|
|
shun-iwasawa |
1b1839 |
if (-1.0 <= irisSize && 1.0 >= irisSize) {
|
|
shun-iwasawa |
1b1839 |
TTile* layerTile = layer.sourceTile;
|
|
shun-iwasawa |
1b1839 |
if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster());
|
|
shun-iwasawa |
1b1839 |
BokehUtils::compositLayerAsIs(*layerTile, result, dimOut, masterHardness);
|
|
shun-iwasawa |
1b1839 |
// Continue to the next layer
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
// prepare for iris FFT
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* kissfft_comp_iris_before;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
&kissfft_comp_iris_before, dimOut));
|
|
shun-iwasawa |
1b1839 |
// Resize / flip the iris image according to the size ratio.
|
|
shun-iwasawa |
1b1839 |
// Normalize the brightness of the iris image.
|
|
shun-iwasawa |
1b1839 |
// Enlarge the iris to the output size.
|
|
shun-iwasawa |
1b1839 |
BokehUtils::convertIris(irisSize, kissfft_comp_iris_before, dimOut,
|
|
shun-iwasawa |
1b1839 |
irisBBox, irisTile);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Create the FFT plan for the iris image.
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg iris_kissfft_plan;
|
|
shun-iwasawa |
1b1839 |
while (1) {
|
|
shun-iwasawa |
1b1839 |
int dims[2] = {dimOut.ly, dimOut.lx};
|
|
shun-iwasawa |
1b1839 |
int ndims = 2;
|
|
shun-iwasawa |
1b1839 |
iris_kissfft_plan = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
|
|
shun-iwasawa |
1b1839 |
if (iris_kissfft_plan != NULL) break;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// Do FFT the iris image.
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(iris_kissfft_plan, kissfft_comp_iris_before,
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris);
|
|
shun-iwasawa |
1b1839 |
kiss_fft_free(iris_kissfft_plan);
|
|
shun-iwasawa |
1b1839 |
// release the iris buffer
|
|
shun-iwasawa |
1b1839 |
rasterList.takeLast()->unlock();
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Up to here, FFT-ed iris data is stored in kissfft_comp_iris
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//- - - - - - - - - - - - -
|
|
shun-iwasawa |
1b1839 |
// Prepare the layer rasters
|
|
shun-iwasawa |
1b1839 |
TTile* layerTile = layer.sourceTile;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster());
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
//- - - - - - - - - - - - -
|
|
shun-iwasawa |
1b1839 |
// Do FFT the alpha channel.
|
|
shun-iwasawa |
1b1839 |
// Forward FFT -> Multiply by the iris data -> Backward FFT
|
|
shun-iwasawa |
1b1839 |
BokehUtils::calcAlfaChannelBokeh(kissfft_comp_iris, *layerTile,
|
|
shun-iwasawa |
1b1839 |
alpha_bokeh);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
BokehUtils::MyThread threadR(
|
|
shun-iwasawa |
1b1839 |
BokehUtils::MyThread::Red, layerTile->getRaster(), result, alpha_bokeh,
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris, layerHardness, masterHardness);
|
|
shun-iwasawa |
1b1839 |
BokehUtils::MyThread threadG(
|
|
shun-iwasawa |
1b1839 |
BokehUtils::MyThread::Green, layerTile->getRaster(), result,
|
|
shun-iwasawa |
1b1839 |
alpha_bokeh, kissfft_comp_iris, layerHardness, masterHardness);
|
|
shun-iwasawa |
1b1839 |
BokehUtils::MyThread threadB(
|
|
shun-iwasawa |
1b1839 |
BokehUtils::MyThread::Blue, layerTile->getRaster(), result, alpha_bokeh,
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris, layerHardness, masterHardness);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// If you set this flag to true, the fx will be forced to compute in single
|
|
shun-iwasawa |
1b1839 |
// thread.
|
|
shun-iwasawa |
1b1839 |
// Under some specific condition (such as calling from single-threaded
|
|
shun-iwasawa |
1b1839 |
// tcomposer)
|
|
shun-iwasawa |
1b1839 |
// we may need to use this flag... For now, I'll keep this option unused.
|
|
shun-iwasawa |
1b1839 |
// TODO: investigate this.
|
|
shun-iwasawa |
1b1839 |
bool renderInSingleThread = false;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Start the thread when the initialization is done.
|
|
shun-iwasawa |
1b1839 |
// Red channel
|
|
shun-iwasawa |
1b1839 |
int waitCount = 0;
|
|
shun-iwasawa |
1b1839 |
while (1) {
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if ((settings.m_isCanceled && *settings.m_isCanceled) ||
|
|
shun-iwasawa |
1b1839 |
waitCount >= 20) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
if (threadR.init()) {
|
|
shun-iwasawa |
1b1839 |
if (renderInSingleThread)
|
|
shun-iwasawa |
1b1839 |
threadR.run();
|
|
shun-iwasawa |
1b1839 |
else
|
|
shun-iwasawa |
1b1839 |
threadR.start();
|
|
shun-iwasawa |
1b1839 |
break;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
QThread::msleep(500);
|
|
shun-iwasawa |
1b1839 |
waitCount++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
waitCount = 0;
|
|
shun-iwasawa |
1b1839 |
while (1) {
|
|
shun-iwasawa |
1b1839 |
if ((settings.m_isCanceled && *settings.m_isCanceled) ||
|
|
shun-iwasawa |
1b1839 |
waitCount >= 20) // 10 seconds
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
if (!threadR.isFinished()) threadR.terminateThread();
|
|
shun-iwasawa |
1b1839 |
while (!threadR.isFinished()) {
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
if (threadG.init()) {
|
|
shun-iwasawa |
1b1839 |
if (renderInSingleThread)
|
|
shun-iwasawa |
1b1839 |
threadG.run();
|
|
shun-iwasawa |
1b1839 |
else
|
|
shun-iwasawa |
1b1839 |
threadG.start();
|
|
shun-iwasawa |
1b1839 |
break;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
QThread::msleep(500);
|
|
shun-iwasawa |
1b1839 |
waitCount++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
waitCount = 0;
|
|
shun-iwasawa |
1b1839 |
while (1) {
|
|
shun-iwasawa |
1b1839 |
if ((settings.m_isCanceled && *settings.m_isCanceled) ||
|
|
shun-iwasawa |
1b1839 |
waitCount >= 20) // 10 seconds
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
if (!threadR.isFinished()) threadR.terminateThread();
|
|
shun-iwasawa |
1b1839 |
if (!threadG.isFinished()) threadG.terminateThread();
|
|
shun-iwasawa |
1b1839 |
while (!threadR.isFinished() || !threadG.isFinished()) {
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
if (threadB.init()) {
|
|
shun-iwasawa |
1b1839 |
if (renderInSingleThread)
|
|
shun-iwasawa |
1b1839 |
threadB.run();
|
|
shun-iwasawa |
1b1839 |
else
|
|
shun-iwasawa |
1b1839 |
threadB.start();
|
|
shun-iwasawa |
1b1839 |
break;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
QThread::msleep(500);
|
|
shun-iwasawa |
1b1839 |
waitCount++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
/*
|
|
shun-iwasawa |
1b1839 |
* What is done in the thread for each RGB channel:
|
|
shun-iwasawa |
1b1839 |
* - Convert channel value -> Exposure
|
|
shun-iwasawa |
1b1839 |
* - Multiply by alpha channel
|
|
shun-iwasawa |
1b1839 |
* - Forward FFT
|
|
shun-iwasawa |
1b1839 |
* - Multiply by the iris FFT data
|
|
shun-iwasawa |
1b1839 |
* - Backward FFT
|
|
shun-iwasawa |
1b1839 |
* - Convert Exposure -> channel value
|
|
shun-iwasawa |
1b1839 |
*/
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
waitCount = 0;
|
|
shun-iwasawa |
1b1839 |
while (1) {
|
|
shun-iwasawa |
1b1839 |
if ((settings.m_isCanceled && *settings.m_isCanceled) ||
|
|
shun-iwasawa |
1b1839 |
waitCount >= 2000) // 100 second timeout
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
if (!threadR.isFinished()) threadR.terminateThread();
|
|
shun-iwasawa |
1b1839 |
if (!threadG.isFinished()) threadG.terminateThread();
|
|
shun-iwasawa |
1b1839 |
if (!threadB.isFinished()) threadB.terminateThread();
|
|
shun-iwasawa |
1b1839 |
while (!threadR.isFinished() || !threadG.isFinished() ||
|
|
shun-iwasawa |
1b1839 |
!threadB.isFinished()) {
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
if (threadR.isFinished() && threadG.isFinished() && threadB.isFinished())
|
|
shun-iwasawa |
1b1839 |
break;
|
|
shun-iwasawa |
1b1839 |
QThread::msleep(50);
|
|
shun-iwasawa |
1b1839 |
waitCount++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// convert result image value exposure -> rgb
|
|
shun-iwasawa |
1b1839 |
BokehUtils::convertExposureToRGB(result, dimOut.lx * dimOut.ly,
|
|
shun-iwasawa |
1b1839 |
masterHardness);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// clear result raster
|
|
shun-iwasawa |
1b1839 |
tile.getRaster()->clear();
|
|
shun-iwasawa |
1b1839 |
TRaster32P outRas32 = (TRaster32P)tile.getRaster();
|
|
shun-iwasawa |
1b1839 |
TRaster64P outRas64 = (TRaster64P)tile.getRaster();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int2 outMargin = {(dimOut.lx - tile.getRaster()->getSize().lx) / 2,
|
|
shun-iwasawa |
1b1839 |
(dimOut.ly - tile.getRaster()->getSize().ly) / 2};
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
lock.lockForWrite();
|
|
shun-iwasawa |
1b1839 |
if (outRas32)
|
|
shun-iwasawa |
1b1839 |
BokehUtils::setOutputRaster<traster32p, tpixel32="">(result, outRas32, dimOut,</traster32p,>
|
|
shun-iwasawa |
1b1839 |
outMargin);
|
|
shun-iwasawa |
1b1839 |
else if (outRas64)
|
|
shun-iwasawa |
1b1839 |
BokehUtils::setOutputRaster<traster64p, tpixel64="">(result, outRas64, dimOut,</traster64p,>
|
|
shun-iwasawa |
1b1839 |
outMargin);
|
|
shun-iwasawa |
1b1839 |
lock.unlock();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
void Iwa_BokehCommonFx::doBokehRef(double4* result, double frame,
|
|
shun-iwasawa |
1b1839 |
const TRenderSettings& settings,
|
|
shun-iwasawa |
1b1839 |
double bokehPixelAmount, int margin,
|
|
shun-iwasawa |
1b1839 |
TDimensionI& dimOut, TRectD& irisBBox,
|
|
shun-iwasawa |
1b1839 |
TTile& irisTile,
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* kissfft_comp_iris,
|
|
shun-iwasawa |
1b1839 |
LayerValue layer, unsigned char* ctrl) {
|
|
shun-iwasawa |
1b1839 |
QList<trastergr8p> rasterList;</trastergr8p>
|
|
shun-iwasawa |
1b1839 |
QList<kiss_fftnd_cfg> planList;</kiss_fftnd_cfg>
|
|
shun-iwasawa |
1b1839 |
// source image
|
|
shun-iwasawa |
1b1839 |
double4* source_buff;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<double4>(&source_buff, dimOut));</double4>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
TRaster32P ras32 = (TRaster32P)layer.sourceTile->getRaster();
|
|
shun-iwasawa |
1b1839 |
TRaster64P ras64 = (TRaster64P)layer.sourceTile->getRaster();
|
|
shun-iwasawa |
1b1839 |
lock.lockForRead();
|
|
shun-iwasawa |
1b1839 |
if (ras32)
|
|
shun-iwasawa |
1b1839 |
BokehUtils::setSourceRaster<traster32p, tpixel32="">(ras32, source_buff,</traster32p,>
|
|
shun-iwasawa |
1b1839 |
dimOut);
|
|
shun-iwasawa |
1b1839 |
else if (ras64)
|
|
shun-iwasawa |
1b1839 |
BokehUtils::setSourceRaster<traster64p, tpixel64="">(ras64, source_buff,</traster64p,>
|
|
shun-iwasawa |
1b1839 |
dimOut);
|
|
shun-iwasawa |
1b1839 |
lock.unlock();
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// create the index map, which indicates which layer each pixel belongs to
|
|
shun-iwasawa |
1b1839 |
// make two separations and interporate the results in order to avoid
|
|
shun-iwasawa |
1b1839 |
// artifacts appear at the layer border
|
|
shun-iwasawa |
1b1839 |
unsigned char* indexMap_main_buff;
|
|
shun-iwasawa |
1b1839 |
unsigned char* indexMap_sub_buff;
|
|
shun-iwasawa |
1b1839 |
double* mainSub_ratio_buff;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<unsigned char="">(&indexMap_main_buff, dimOut));</unsigned>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<unsigned char="">(&indexMap_sub_buff, dimOut));</unsigned>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<double>(&mainSub_ratio_buff, dimOut));</double>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
QVector<double> segmentDepth_main;</double>
|
|
shun-iwasawa |
1b1839 |
QVector<double> segmentDepth_sub;</double>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double layerDitance = layer.distance;
|
|
shun-iwasawa |
1b1839 |
double nearDepth = layerDitance - layer.depthRange * layerDitance;
|
|
shun-iwasawa |
1b1839 |
double farDepth = nearDepth + layer.depthRange;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int distancePrecision = layer.distancePrecision;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// create the depth index map
|
|
shun-iwasawa |
1b1839 |
BokehUtils::defineSegemntDepth(
|
|
shun-iwasawa |
1b1839 |
indexMap_main_buff, indexMap_sub_buff, mainSub_ratio_buff, ctrl, dimOut,
|
|
shun-iwasawa |
1b1839 |
segmentDepth_main, segmentDepth_sub, m_onFocusDistance->getValue(frame),
|
|
shun-iwasawa |
1b1839 |
distancePrecision, nearDepth, farDepth);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
int size = dimOut.lx * dimOut.ly;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double4* layer_buff;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<double4>(&layer_buff, dimOut));</double4>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* kissfft_comp_iris_before;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<kiss_fft_cpx>(&kissfft_comp_iris_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// alpha channel
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_alpha_before;
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_alpha;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// RGB channels
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_r_before;
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_g_before;
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_b_before;
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_r;
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_g;
|
|
shun-iwasawa |
1b1839 |
kiss_fft_cpx* fftcpx_b;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(
|
|
shun-iwasawa |
1b1839 |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// for accumulating result image
|
|
shun-iwasawa |
1b1839 |
double4* result_main_buff;
|
|
shun-iwasawa |
1b1839 |
double4* result_sub_buff;
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<double4>(&result_main_buff, dimOut));</double4>
|
|
shun-iwasawa |
1b1839 |
rasterList.append(allocateRasterAndLock<double4>(&result_sub_buff, dimOut));</double4>
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// fft plans
|
|
shun-iwasawa |
1b1839 |
int dims[2] = {dimOut.ly, dimOut.lx};
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_fwd);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_bkwd);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_r_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_r_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_g_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_g_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_b_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd_cfg kissfft_plan_b_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_r_fwd);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_r_bkwd);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_g_fwd);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_g_bkwd);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_b_fwd);
|
|
shun-iwasawa |
1b1839 |
planList.append(kissfft_plan_b_bkwd);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// initialize result memory
|
|
shun-iwasawa |
1b1839 |
memset(result_main_buff, 0, sizeof(double4) * size);
|
|
shun-iwasawa |
1b1839 |
memset(result_sub_buff, 0, sizeof(double4) * size);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double masterHardness = (double)m_hardness->getValue(frame);
|
|
shun-iwasawa |
1b1839 |
double layerHardness = layer.layerHardness;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// convert source image value rgb -> exposure
|
|
shun-iwasawa |
1b1839 |
// note that premultiplied source image is already unpremultiplied before this
|
|
shun-iwasawa |
1b1839 |
// function
|
|
shun-iwasawa |
1b1839 |
BokehUtils::convertRGBToExposure(source_buff, size, layerHardness);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
double focus = m_onFocusDistance->getValue(frame);
|
|
shun-iwasawa |
1b1839 |
double adjust = layer.bokehAdjustment;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
for (int mainSub = 0; mainSub < 2; mainSub++) {
|
|
shun-iwasawa |
1b1839 |
double4* result_buff_mainSub;
|
|
shun-iwasawa |
1b1839 |
QVector<double> segmentDepth_mainSub;</double>
|
|
shun-iwasawa |
1b1839 |
unsigned char* indexMap_mainSub;
|
|
shun-iwasawa |
1b1839 |
if (mainSub == 0) {
|
|
shun-iwasawa |
1b1839 |
result_buff_mainSub = result_main_buff;
|
|
shun-iwasawa |
1b1839 |
segmentDepth_mainSub = segmentDepth_main;
|
|
shun-iwasawa |
1b1839 |
indexMap_mainSub = indexMap_main_buff;
|
|
shun-iwasawa |
1b1839 |
} else {
|
|
shun-iwasawa |
1b1839 |
result_buff_mainSub = result_sub_buff;
|
|
shun-iwasawa |
1b1839 |
segmentDepth_mainSub = segmentDepth_sub;
|
|
shun-iwasawa |
1b1839 |
indexMap_mainSub = indexMap_sub_buff;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// compute from further to nearer
|
|
shun-iwasawa |
1b1839 |
for (int index = 0; index < segmentDepth_mainSub.size(); index++) {
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// compute iris size
|
|
shun-iwasawa |
1b1839 |
double irisSize = BokehUtils::calcIrisSize(segmentDepth_mainSub.at(index),
|
|
shun-iwasawa |
1b1839 |
bokehPixelAmount, focus,
|
|
shun-iwasawa |
1b1839 |
adjust, nearDepth, farDepth);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
memset(layer_buff, 0, sizeof(double4) * size);
|
|
shun-iwasawa |
1b1839 |
BokehUtils::retrieveLayer(
|
|
shun-iwasawa |
1b1839 |
source_buff, layer_buff, indexMap_mainSub, index, dimOut.lx,
|
|
shun-iwasawa |
1b1839 |
dimOut.ly, layer.fillGap, layer.doMedian,
|
|
shun-iwasawa |
1b1839 |
(index == segmentDepth_mainSub.size() - 1) ? 0 : margin);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// in case the current segment is at the focus
|
|
shun-iwasawa |
1b1839 |
if (-1.0 <= irisSize && 1.0 >= irisSize) {
|
|
shun-iwasawa |
1b1839 |
BokehUtils::compositeAsIs(layer_buff, result_buff_mainSub, size);
|
|
shun-iwasawa |
1b1839 |
continue;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// Resize / flip the iris image according to the size ratio.
|
|
shun-iwasawa |
1b1839 |
// Normalize the brightness of the iris image.
|
|
shun-iwasawa |
1b1839 |
// Enlarge the iris to the output size.
|
|
shun-iwasawa |
1b1839 |
BokehUtils::convertIris(irisSize, kissfft_comp_iris_before, dimOut,
|
|
shun-iwasawa |
1b1839 |
irisBBox, irisTile);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
// Do FFT the iris image.
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(kissfft_plan_fwd, kissfft_comp_iris_before, kissfft_comp_iris);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// initialize alpha
|
|
shun-iwasawa |
1b1839 |
memset(fftcpx_alpha_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
1b1839 |
// initialize channels
|
|
shun-iwasawa |
1b1839 |
memset(fftcpx_r_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
1b1839 |
memset(fftcpx_g_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
1b1839 |
memset(fftcpx_b_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// retrieve segment layer image for each channel
|
|
shun-iwasawa |
1b1839 |
BokehUtils::retrieveChannel(layer_buff, // src
|
|
shun-iwasawa |
1b1839 |
fftcpx_r_before, // dst
|
|
shun-iwasawa |
1b1839 |
fftcpx_g_before, // dst
|
|
shun-iwasawa |
1b1839 |
fftcpx_b_before, // dst
|
|
shun-iwasawa |
1b1839 |
fftcpx_alpha_before, // dst
|
|
shun-iwasawa |
1b1839 |
size);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// forward fft of alpha channel
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(kissfft_plan_fwd, fftcpx_alpha_before, fftcpx_alpha);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// multiply filter on alpha
|
|
shun-iwasawa |
1b1839 |
BokehUtils::multiplyFilter(fftcpx_alpha, // dst
|
|
shun-iwasawa |
1b1839 |
kissfft_comp_iris, // filter
|
|
shun-iwasawa |
1b1839 |
size);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// inverse fft the alpha channel
|
|
shun-iwasawa |
1b1839 |
// note that the result is multiplied by the image size
|
|
shun-iwasawa |
1b1839 |
kiss_fftnd(kissfft_plan_bkwd, fftcpx_alpha, fftcpx_alpha_before);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// over composite the alpha channel
|
|
shun-iwasawa |
1b1839 |
BokehUtils::compositeAlpha(result_buff_mainSub, // dst
|
|
shun-iwasawa |
1b1839 |
fftcpx_alpha_before, // alpha
|
|
shun-iwasawa |
1b1839 |
dimOut.lx, dimOut.ly);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// create worker threads
|
|
shun-iwasawa |
1b1839 |
BokehUtils::BokehRefThread threadR(
|
|
shun-iwasawa |
1b1839 |
0, fftcpx_r_before, fftcpx_r, fftcpx_alpha_before, kissfft_comp_iris,
|
|
shun-iwasawa |
1b1839 |
result_buff_mainSub, kissfft_plan_r_fwd, kissfft_plan_r_bkwd, dimOut);
|
|
shun-iwasawa |
1b1839 |
BokehUtils::BokehRefThread threadG(
|
|
shun-iwasawa |
1b1839 |
1, fftcpx_g_before, fftcpx_g, fftcpx_alpha_before, kissfft_comp_iris,
|
|
shun-iwasawa |
1b1839 |
result_buff_mainSub, kissfft_plan_g_fwd, kissfft_plan_g_bkwd, dimOut);
|
|
shun-iwasawa |
1b1839 |
BokehUtils::BokehRefThread threadB(
|
|
shun-iwasawa |
1b1839 |
2, fftcpx_b_before, fftcpx_b, fftcpx_alpha_before, kissfft_comp_iris,
|
|
shun-iwasawa |
1b1839 |
result_buff_mainSub, kissfft_plan_b_fwd, kissfft_plan_b_bkwd, dimOut);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// If you set this flag to true, the fx will be forced to compute in
|
|
shun-iwasawa |
1b1839 |
// single thread.
|
|
shun-iwasawa |
1b1839 |
// Under some specific condition (such as calling from single-threaded
|
|
shun-iwasawa |
1b1839 |
// tcomposer)
|
|
shun-iwasawa |
1b1839 |
// we may need to use this flag... For now, I'll keep this option unused.
|
|
shun-iwasawa |
1b1839 |
// TODO: investigate this.
|
|
shun-iwasawa |
1b1839 |
bool renderInSingleThread = false;
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
if (renderInSingleThread) {
|
|
shun-iwasawa |
1b1839 |
threadR.run();
|
|
shun-iwasawa |
1b1839 |
threadG.run();
|
|
shun-iwasawa |
1b1839 |
threadB.run();
|
|
shun-iwasawa |
1b1839 |
} else {
|
|
shun-iwasawa |
1b1839 |
threadR.start();
|
|
shun-iwasawa |
1b1839 |
threadG.start();
|
|
shun-iwasawa |
1b1839 |
threadB.start();
|
|
shun-iwasawa |
1b1839 |
int waitCount = 0;
|
|
shun-iwasawa |
1b1839 |
while (1) {
|
|
shun-iwasawa |
1b1839 |
if ((settings.m_isCanceled && *settings.m_isCanceled) ||
|
|
shun-iwasawa |
1b1839 |
waitCount >= 2000) // 100 second timeout
|
|
shun-iwasawa |
1b1839 |
{
|
|
shun-iwasawa |
1b1839 |
if (!threadR.isFinished()) threadR.terminateThread();
|
|
shun-iwasawa |
1b1839 |
if (!threadG.isFinished()) threadG.terminateThread();
|
|
shun-iwasawa |
1b1839 |
if (!threadB.isFinished()) threadB.terminateThread();
|
|
shun-iwasawa |
1b1839 |
while (!threadR.isFinished() || !threadG.isFinished() ||
|
|
shun-iwasawa |
1b1839 |
!threadB.isFinished()) {
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
if (threadR.isFinished() && threadG.isFinished() &&
|
|
shun-iwasawa |
1b1839 |
threadB.isFinished())
|
|
shun-iwasawa |
1b1839 |
break;
|
|
shun-iwasawa |
1b1839 |
QThread::msleep(50);
|
|
shun-iwasawa |
1b1839 |
waitCount++;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
} // for each segment
|
|
shun-iwasawa |
1b1839 |
} // main and sub
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// cancel check
|
|
shun-iwasawa |
1b1839 |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
return;
|
|
shun-iwasawa |
1b1839 |
}
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
BokehUtils::interpolateExposureAndConvertToRGB(
|
|
shun-iwasawa |
1b1839 |
result_main_buff, // result1
|
|
shun-iwasawa |
1b1839 |
result_sub_buff, // result2
|
|
shun-iwasawa |
1b1839 |
mainSub_ratio_buff, // ratio
|
|
shun-iwasawa |
1b1839 |
result, // dst
|
|
shun-iwasawa |
1b1839 |
size, layerHardness / masterHardness);
|
|
shun-iwasawa |
1b1839 |
|
|
shun-iwasawa |
1b1839 |
// release rasters and plans
|
|
shun-iwasawa |
1b1839 |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
1b1839 |
}
|