|
shun-iwasawa |
e2505a |
#include "iwa_bokehreffx.h"
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
#include "trop.h"
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
#include <qreadwritelock></qreadwritelock>
|
|
shun-iwasawa |
e2505a |
#include <qset></qset>
|
|
shun-iwasawa |
e2505a |
#include <math.h></math.h>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
namespace {
|
|
shun-iwasawa |
e2505a |
QMutex fx_mutex;
|
|
shun-iwasawa |
e2505a |
QReadWriteLock lock;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
template <typename t=""></typename>
|
|
shun-iwasawa |
e2505a |
TRasterGR8P allocateRasterAndLock(T** buf, TDimensionI dim) {
|
|
shun-iwasawa |
e2505a |
TRasterGR8P ras(dim.lx * sizeof(T), dim.ly);
|
|
shun-iwasawa |
e2505a |
ras->lock();
|
|
shun-iwasawa |
e2505a |
*buf = (T*)ras->getRawData();
|
|
shun-iwasawa |
e2505a |
return ras;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// modify fft coordinate to normal
|
|
shun-iwasawa |
e2505a |
inline int getCoord(int index, int lx, int ly) {
|
|
shun-iwasawa |
e2505a |
int i = index % lx;
|
|
shun-iwasawa |
e2505a |
int j = index / lx;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
int cx = i - lx / 2;
|
|
shun-iwasawa |
e2505a |
int cy = j - ly / 2;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
if (cx < 0) cx += lx;
|
|
shun-iwasawa |
e2505a |
if (cy < 0) cy += ly;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
return cy * lx + cx;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// release all registered raster memories
|
|
shun-iwasawa |
e2505a |
void releaseAllRasters(QList<trastergr8p>& rasterList) {</trastergr8p>
|
|
shun-iwasawa |
e2505a |
for (int r = 0; r < rasterList.size(); r++) rasterList.at(r)->unlock();
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// release all registered raster memories and free all fft plans
|
|
shun-iwasawa |
e2505a |
void releaseAllRastersAndPlans(QList<trastergr8p>& rasterList,</trastergr8p>
|
|
shun-iwasawa |
e2505a |
QList<kiss_fftnd_cfg>& planList) {</kiss_fftnd_cfg>
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
for (int p = 0; p < planList.size(); p++) kiss_fft_free(planList.at(p));
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
09e972 |
}; // namespace
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------
|
|
shun-iwasawa |
e2505a |
BokehRefThread::BokehRefThread(int channel, kiss_fft_cpx* fftcpx_channel_before,
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_channel,
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_alpha,
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_iris, float4* result_buff,
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_fwd,
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_bkwd,
|
|
shun-iwasawa |
e2505a |
TDimensionI& dim)
|
|
shun-iwasawa |
e2505a |
: m_channel(channel)
|
|
shun-iwasawa |
e2505a |
, m_fftcpx_channel_before(fftcpx_channel_before)
|
|
shun-iwasawa |
e2505a |
, m_fftcpx_channel(fftcpx_channel)
|
|
shun-iwasawa |
e2505a |
, m_fftcpx_alpha(fftcpx_alpha)
|
|
shun-iwasawa |
e2505a |
, m_fftcpx_iris(fftcpx_iris)
|
|
shun-iwasawa |
e2505a |
, m_result_buff(result_buff)
|
|
shun-iwasawa |
e2505a |
, m_kissfft_plan_fwd(kissfft_plan_fwd)
|
|
shun-iwasawa |
e2505a |
, m_kissfft_plan_bkwd(kissfft_plan_bkwd)
|
|
shun-iwasawa |
e2505a |
, m_dim(dim)
|
|
shun-iwasawa |
e2505a |
, m_finished(false)
|
|
shun-iwasawa |
e2505a |
, m_isTerminated(false) {}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
void BokehRefThread::run() {
|
|
shun-iwasawa |
e2505a |
// execute channel fft
|
|
shun-iwasawa |
e2505a |
kiss_fftnd(m_kissfft_plan_fwd, m_fftcpx_channel_before, m_fftcpx_channel);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (m_isTerminated) {
|
|
shun-iwasawa |
e2505a |
m_finished = true;
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
int size = m_dim.lx * m_dim.ly;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// multiply filter
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++) {
|
|
shun-iwasawa |
e2505a |
float re, im;
|
|
shun-iwasawa |
e2505a |
re = m_fftcpx_channel[i].r * m_fftcpx_iris[i].r -
|
|
shun-iwasawa |
e2505a |
m_fftcpx_channel[i].i * m_fftcpx_iris[i].i;
|
|
shun-iwasawa |
e2505a |
im = m_fftcpx_channel[i].r * m_fftcpx_iris[i].i +
|
|
shun-iwasawa |
e2505a |
m_fftcpx_iris[i].r * m_fftcpx_channel[i].i;
|
|
shun-iwasawa |
e2505a |
m_fftcpx_channel[i].r = re;
|
|
shun-iwasawa |
e2505a |
m_fftcpx_channel[i].i = im;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
// execute invert fft
|
|
shun-iwasawa |
e2505a |
kiss_fftnd(m_kissfft_plan_bkwd, m_fftcpx_channel, m_fftcpx_channel_before);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (m_isTerminated) {
|
|
shun-iwasawa |
e2505a |
m_finished = true;
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// normal composite exposure value
|
|
shun-iwasawa |
e2505a |
float4* result_p = m_result_buff;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++, result_p++) {
|
|
shun-iwasawa |
e2505a |
// modify fft coordinate to normal
|
|
shun-iwasawa |
e2505a |
int coord = getCoord(i, m_dim.lx, m_dim.ly);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
float alpha = m_fftcpx_alpha[coord].r / (float)size;
|
|
shun-iwasawa |
e2505a |
// ignore transpalent pixels
|
|
shun-iwasawa |
e2505a |
if (alpha == 0.0f) continue;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
float exposure = m_fftcpx_channel_before[coord].r / (float)size;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// in case of using upper layer at all
|
|
shun-iwasawa |
e2505a |
if (alpha >= 1.0f || (m_channel == 0 && (*result_p).x == 0.0f) ||
|
|
shun-iwasawa |
e2505a |
(m_channel == 1 && (*result_p).y == 0.0f) ||
|
|
shun-iwasawa |
e2505a |
(m_channel == 2 && (*result_p).z == 0.0f)) {
|
|
shun-iwasawa |
e2505a |
// set exposure
|
|
shun-iwasawa |
e2505a |
if (m_channel == 0) // R
|
|
shun-iwasawa |
e2505a |
(*result_p).x = exposure;
|
|
shun-iwasawa |
e2505a |
else if (m_channel == 1) // G
|
|
shun-iwasawa |
e2505a |
(*result_p).y = exposure;
|
|
shun-iwasawa |
e2505a |
else // B
|
|
shun-iwasawa |
e2505a |
(*result_p).z = exposure;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
// in case of compositing both layers
|
|
shun-iwasawa |
e2505a |
else {
|
|
shun-iwasawa |
e2505a |
if (m_channel == 0) // R
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
(*result_p).x *= 1.0f - alpha;
|
|
shun-iwasawa |
e2505a |
(*result_p).x += exposure;
|
|
shun-iwasawa |
e2505a |
} else if (m_channel == 1) // G
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
(*result_p).y *= 1.0f - alpha;
|
|
shun-iwasawa |
e2505a |
(*result_p).y += exposure;
|
|
shun-iwasawa |
e2505a |
} else // B
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
(*result_p).z *= 1.0f - alpha;
|
|
shun-iwasawa |
e2505a |
(*result_p).z += exposure;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
m_finished = true;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//============================================================
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
// Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
|
|
shun-iwasawa |
e2505a |
// DONE
|
|
shun-iwasawa |
e2505a |
float Iwa_BokehRefFx::getBokehPixelAmount(const double frame,
|
|
shun-iwasawa |
e2505a |
const TAffine affine) {
|
|
shun-iwasawa |
e2505a |
// Convert to vector
|
|
shun-iwasawa |
e2505a |
TPointD vect;
|
|
shun-iwasawa |
e2505a |
vect.x = m_bokehAmount->getValue(frame);
|
|
shun-iwasawa |
e2505a |
vect.y = 0.0;
|
|
shun-iwasawa |
e2505a |
// Apply geometrical transformation
|
|
shun-iwasawa |
e2505a |
TAffine aff(affine);
|
|
shun-iwasawa |
e2505a |
aff.a13 = aff.a23 = 0; // ignore translation
|
|
shun-iwasawa |
e2505a |
vect = aff * vect;
|
|
shun-iwasawa |
e2505a |
// return the length of the vector
|
|
shun-iwasawa |
e2505a |
return sqrtf((float)(vect.x * vect.x + vect.y * vect.y));
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
// normalize the source raster image to 0-1 and set to dstMem
|
|
shun-iwasawa |
e2505a |
// returns true if the source is (seems to be) premultiplied
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
e2505a |
bool Iwa_BokehRefFx::setSourceRaster(const RASTER srcRas, float4* dstMem,
|
|
shun-iwasawa |
e2505a |
TDimensionI dim) {
|
|
shun-iwasawa |
e2505a |
bool isPremultiplied = true;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
float4* chann_p = dstMem;
|
|
shun-iwasawa |
e2505a |
for (int j = 0; j < dim.ly; j++) {
|
|
shun-iwasawa |
e2505a |
PIXEL* pix = srcRas->pixels(j);
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < dim.lx; i++, pix++, chann_p++) {
|
|
shun-iwasawa |
e2505a |
(*chann_p).x = (float)pix->r / (float)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
e2505a |
(*chann_p).y = (float)pix->g / (float)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
e2505a |
(*chann_p).z = (float)pix->b / (float)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
e2505a |
(*chann_p).w = (float)pix->m / (float)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// if there is at least one pixel of which any of RGB channels has
|
|
shun-iwasawa |
e2505a |
// higher value than alpha channel, the source image can be jusged
|
|
shun-iwasawa |
e2505a |
// as NON-premultiplied.
|
|
shun-iwasawa |
e2505a |
if (isPremultiplied &&
|
|
shun-iwasawa |
e2505a |
((*chann_p).x > (*chann_p).w || (*chann_p).y > (*chann_p).w ||
|
|
shun-iwasawa |
e2505a |
(*chann_p).z > (*chann_p).w))
|
|
shun-iwasawa |
e2505a |
isPremultiplied = false;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
return isPremultiplied;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
// normalize brightness of the depth reference image to unsigned char
|
|
shun-iwasawa |
e2505a |
// and store into detMem
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::setDepthRaster(const RASTER srcRas, unsigned char* dstMem,
|
|
shun-iwasawa |
e2505a |
TDimensionI dim) {
|
|
shun-iwasawa |
e2505a |
unsigned char* depth_p = dstMem;
|
|
shun-iwasawa |
e2505a |
for (int j = 0; j < dim.ly; j++) {
|
|
shun-iwasawa |
e2505a |
PIXEL* pix = srcRas->pixels(j);
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < dim.lx; i++, pix++, depth_p++) {
|
|
shun-iwasawa |
e2505a |
// normalize brightness to 0-1
|
|
shun-iwasawa |
e2505a |
float val = ((float)pix->r * 0.3f + (float)pix->g * 0.59f +
|
|
shun-iwasawa |
e2505a |
(float)pix->b * 0.11f) /
|
|
shun-iwasawa |
e2505a |
(float)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
e2505a |
// convert to unsigned char
|
|
shun-iwasawa |
e2505a |
(*depth_p) = (unsigned char)(val * (float)UCHAR_MAX + 0.5f);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::setDepthRasterGray(const RASTER srcRas,
|
|
shun-iwasawa |
e2505a |
unsigned char* dstMem,
|
|
shun-iwasawa |
e2505a |
TDimensionI dim) {
|
|
shun-iwasawa |
e2505a |
unsigned char* depth_p = dstMem;
|
|
shun-iwasawa |
e2505a |
for (int j = 0; j < dim.ly; j++) {
|
|
shun-iwasawa |
e2505a |
PIXEL* pix = srcRas->pixels(j);
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < dim.lx; i++, pix++, depth_p++) {
|
|
shun-iwasawa |
e2505a |
// normalize brightness to 0-1
|
|
shun-iwasawa |
e2505a |
float val = (float)pix->value / (float)PIXEL::maxChannelValue;
|
|
shun-iwasawa |
e2505a |
// convert to unsigned char
|
|
shun-iwasawa |
e2505a |
(*depth_p) = (unsigned char)(val * (float)UCHAR_MAX + 0.5f);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
// create the depth index map based on the histogram
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::defineSegemntDepth(
|
|
shun-iwasawa |
e2505a |
const unsigned char* indexMap_main, const unsigned char* indexMap_sub,
|
|
shun-iwasawa |
e2505a |
const float* mainSub_ratio, const unsigned char* depth_buff,
|
|
shun-iwasawa |
e2505a |
const TDimensionI& dimOut, const double frame,
|
|
shun-iwasawa |
e2505a |
QVector<float>& segmentDepth_main, QVector<float>& segmentDepth_sub) {</float></float>
|
|
shun-iwasawa |
e2505a |
QSet<int> segmentValues;</int>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// histogram parameters
|
|
shun-iwasawa |
e2505a |
struct HISTO {
|
|
shun-iwasawa |
e2505a |
int pix_amount;
|
|
shun-iwasawa |
e2505a |
int belongingSegmentValue; // value to which temporary segmented
|
|
shun-iwasawa |
e2505a |
int segmentId;
|
|
shun-iwasawa |
e2505a |
int segmentId_sub;
|
|
shun-iwasawa |
e2505a |
} histo[256];
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// initialize
|
|
shun-iwasawa |
e2505a |
for (int h = 0; h < 256; h++) {
|
|
shun-iwasawa |
e2505a |
histo[h].pix_amount = 0;
|
|
shun-iwasawa |
e2505a |
histo[h].belongingSegmentValue = -1;
|
|
shun-iwasawa |
e2505a |
histo[h].segmentId = -1;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
int size = dimOut.lx * dimOut.ly;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// max and min
|
|
shun-iwasawa |
e2505a |
int minHisto = (int)UCHAR_MAX;
|
|
shun-iwasawa |
e2505a |
int maxHisto = 0;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
unsigned char* depth_p = (unsigned char*)depth_buff;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++, depth_p++) {
|
|
shun-iwasawa |
e2505a |
histo[(int)*depth_p].pix_amount++;
|
|
shun-iwasawa |
e2505a |
// update max and min
|
|
shun-iwasawa |
e2505a |
if ((int)*depth_p < minHisto) minHisto = (int)*depth_p;
|
|
shun-iwasawa |
e2505a |
if ((int)*depth_p > maxHisto) maxHisto = (int)*depth_p;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// the maximum and the minimum depth become the segment layers
|
|
shun-iwasawa |
e2505a |
segmentValues.insert(minHisto);
|
|
shun-iwasawa |
e2505a |
segmentValues.insert(maxHisto);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// focus depth becomes the segment layer as well
|
|
shun-iwasawa |
e2505a |
int focusVal = (int)(m_onFocusDistance->getValue(frame) * (double)UCHAR_MAX);
|
|
shun-iwasawa |
e2505a |
if (minHisto < focusVal && focusVal < maxHisto)
|
|
shun-iwasawa |
e2505a |
segmentValues.insert(focusVal);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// set the initial segmentation for each depth value
|
|
shun-iwasawa |
e2505a |
for (int h = 0; h < 256; h++) {
|
|
shun-iwasawa |
e2505a |
for (int seg = 0; seg < segmentValues.size(); seg++) {
|
|
shun-iwasawa |
e2505a |
// set the segment
|
|
shun-iwasawa |
e2505a |
if (histo[h].belongingSegmentValue == -1) {
|
|
shun-iwasawa |
e2505a |
histo[h].belongingSegmentValue = segmentValues.values().at(seg);
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
// error amount at the current registered layers
|
|
shun-iwasawa |
0e4a7a |
int tmpError = std::abs(h - histo[h].belongingSegmentValue);
|
|
shun-iwasawa |
e2505a |
if (tmpError == 0) break;
|
|
shun-iwasawa |
e2505a |
// new error amount
|
|
shun-iwasawa |
0e4a7a |
int newError = std::abs(h - segmentValues.values().at(seg));
|
|
shun-iwasawa |
e2505a |
// compare the two and update
|
|
shun-iwasawa |
e2505a |
if (newError < tmpError)
|
|
shun-iwasawa |
e2505a |
histo[h].belongingSegmentValue = segmentValues.values().at(seg);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// add the segment layers to the distance precision value
|
|
shun-iwasawa |
e2505a |
while (segmentValues.size() < m_distancePrecision->getValue()) {
|
|
shun-iwasawa |
e2505a |
// add a new segment at the value which will reduce the error amount in
|
|
shun-iwasawa |
e2505a |
// maximum
|
|
shun-iwasawa |
e2505a |
double tmpMaxErrorMod = 0;
|
|
shun-iwasawa |
e2505a |
int tmpBestNewSegVal;
|
|
shun-iwasawa |
e2505a |
bool newSegFound = false;
|
|
shun-iwasawa |
e2505a |
for (int h = minHisto + 1; h < maxHisto; h++) {
|
|
shun-iwasawa |
e2505a |
// if it is already set as the segment, continue
|
|
shun-iwasawa |
e2505a |
if (histo[h].belongingSegmentValue == h) continue;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
double errorModAmount = 0;
|
|
shun-iwasawa |
e2505a |
// estimate how much the error will be reduced if the current h becomes
|
|
shun-iwasawa |
e2505a |
// segment
|
|
shun-iwasawa |
e2505a |
for (int i = minHisto + 1; i < maxHisto; i++) {
|
|
shun-iwasawa |
e2505a |
// compare the current segment value and h and take the nearest value
|
|
shun-iwasawa |
e2505a |
// if h is near (from i), then accumulate the estimated error reduction
|
|
shun-iwasawa |
e2505a |
// amount
|
|
shun-iwasawa |
0e4a7a |
if (std::abs(i - histo[i].belongingSegmentValue) >
|
|
shun-iwasawa |
0e4a7a |
std::abs(i - h)) // the current segment value has
|
|
shun-iwasawa |
0e4a7a |
// proirity, if the distance is the same
|
|
shun-iwasawa |
e2505a |
errorModAmount +=
|
|
shun-iwasawa |
0e4a7a |
(std::abs(i - histo[i].belongingSegmentValue) - std::abs(i - h)) *
|
|
shun-iwasawa |
e2505a |
histo[i].pix_amount;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// if h will reduce the error, update the candidate segment value
|
|
shun-iwasawa |
e2505a |
if (errorModAmount > tmpMaxErrorMod) {
|
|
shun-iwasawa |
e2505a |
tmpMaxErrorMod = errorModAmount;
|
|
shun-iwasawa |
e2505a |
tmpBestNewSegVal = h;
|
|
shun-iwasawa |
e2505a |
newSegFound = true;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
if (!newSegFound) break;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// register tmpBestNewSegVal to the segment values list
|
|
shun-iwasawa |
e2505a |
segmentValues.insert(tmpBestNewSegVal);
|
|
shun-iwasawa |
e2505a |
// std::cout << "insert " << tmpBestNewSegVal << std::endl;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// update belongingSegmentValue
|
|
shun-iwasawa |
e2505a |
for (int h = minHisto + 1; h < maxHisto; h++) {
|
|
shun-iwasawa |
e2505a |
// compare the current segment value and h and take the nearest value
|
|
shun-iwasawa |
e2505a |
// if tmpBestNewSegVal is near (from h), then update the
|
|
shun-iwasawa |
e2505a |
// belongingSegmentValue
|
|
shun-iwasawa |
0e4a7a |
if (std::abs(h - histo[h].belongingSegmentValue) >
|
|
shun-iwasawa |
0e4a7a |
std::abs(h - tmpBestNewSegVal)) // the current segment value has
|
|
shun-iwasawa |
0e4a7a |
// proirity, if the distance is the same
|
|
shun-iwasawa |
e2505a |
histo[h].belongingSegmentValue = tmpBestNewSegVal;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// set indices from the farthest and create the index table for each depth
|
|
shun-iwasawa |
e2505a |
// value
|
|
shun-iwasawa |
e2505a |
QVector<int> segValVec;</int>
|
|
shun-iwasawa |
e2505a |
int tmpSegVal = -1;
|
|
shun-iwasawa |
e2505a |
int tmpSegId = -1;
|
|
shun-iwasawa |
e2505a |
for (int h = 255; h >= 0; h--) {
|
|
shun-iwasawa |
e2505a |
if (histo[h].belongingSegmentValue != tmpSegVal) {
|
|
shun-iwasawa |
e2505a |
segmentDepth_main.push_back((float)histo[h].belongingSegmentValue /
|
|
shun-iwasawa |
e2505a |
(float)UCHAR_MAX);
|
|
shun-iwasawa |
e2505a |
tmpSegVal = histo[h].belongingSegmentValue;
|
|
shun-iwasawa |
e2505a |
tmpSegId++;
|
|
shun-iwasawa |
e2505a |
segValVec.push_back(tmpSegVal);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
histo[h].segmentId = tmpSegId;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// "sub" depth segment value list for interporation
|
|
shun-iwasawa |
e2505a |
for (int d = 0; d < segmentDepth_main.size() - 1; d++)
|
|
shun-iwasawa |
e2505a |
segmentDepth_sub.push_back(
|
|
shun-iwasawa |
e2505a |
(segmentDepth_main.at(d) + segmentDepth_main.at(d + 1)) / 2.0f);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// create the "sub" index table for each depth value
|
|
shun-iwasawa |
e2505a |
tmpSegId = 0;
|
|
shun-iwasawa |
e2505a |
for (int seg = 0; seg < segValVec.size() - 1; seg++) {
|
|
shun-iwasawa |
e2505a |
int hMax = (seg == 0) ? 255 : segValVec.at(seg);
|
|
shun-iwasawa |
e2505a |
int hMin = (seg == segValVec.size() - 2) ? 0 : segValVec.at(seg + 1) + 1;
|
|
shun-iwasawa |
e2505a |
for (int h = hMax; h >= hMin; h--) histo[h].segmentId_sub = tmpSegId;
|
|
shun-iwasawa |
e2505a |
tmpSegId++;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// convert the depth value to the segment index by using the index table
|
|
shun-iwasawa |
e2505a |
depth_p = (unsigned char*)depth_buff;
|
|
shun-iwasawa |
e2505a |
unsigned char* main_p = (unsigned char*)indexMap_main;
|
|
shun-iwasawa |
e2505a |
unsigned char* sub_p = (unsigned char*)indexMap_sub;
|
|
shun-iwasawa |
e2505a |
// mainSub_ratio represents the composition ratio of the image with "main"
|
|
shun-iwasawa |
e2505a |
// separation.
|
|
shun-iwasawa |
e2505a |
float* ratio_p = (float*)mainSub_ratio;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++, depth_p++, main_p++, sub_p++, ratio_p++) {
|
|
shun-iwasawa |
e2505a |
*main_p = (unsigned char)histo[(int)*depth_p].segmentId;
|
|
shun-iwasawa |
e2505a |
*sub_p = (unsigned char)histo[(int)*depth_p].segmentId_sub;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
float depth = (float)*depth_p / (float)UCHAR_MAX;
|
|
shun-iwasawa |
e2505a |
float main_segDepth = segmentDepth_main.at(*main_p);
|
|
shun-iwasawa |
e2505a |
float sub_segDepth = segmentDepth_sub.at(*sub_p);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
if (main_segDepth == sub_segDepth)
|
|
shun-iwasawa |
e2505a |
*ratio_p = 1.0f;
|
|
shun-iwasawa |
e2505a |
else {
|
|
shun-iwasawa |
e2505a |
*ratio_p =
|
|
shun-iwasawa |
e2505a |
1.0f - (main_segDepth - depth) / (main_segDepth - sub_segDepth);
|
|
shun-iwasawa |
e2505a |
if (*ratio_p > 1.0f) *ratio_p = 1.0f;
|
|
shun-iwasawa |
e2505a |
if (*ratio_p < 0.0f) *ratio_p = 0.0f;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
// set the result
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
template <typename pixel="" raster,="" typename=""></typename>
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::setOutputRaster(float4* srcMem, const RASTER dstRas,
|
|
shun-iwasawa |
e2505a |
TDimensionI dim, TDimensionI margin) {
|
|
shun-iwasawa |
e2505a |
int out_j = 0;
|
|
shun-iwasawa |
e2505a |
for (int j = margin.ly; j < dstRas->getLy() + margin.ly; j++, out_j++) {
|
|
shun-iwasawa |
e2505a |
PIXEL* pix = dstRas->pixels(out_j);
|
|
shun-iwasawa |
e2505a |
float4* chan_p = srcMem;
|
|
shun-iwasawa |
e2505a |
chan_p += j * dim.lx + margin.lx;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < dstRas->getLx(); i++, pix++, chan_p++) {
|
|
shun-iwasawa |
e2505a |
float val;
|
|
shun-iwasawa |
e2505a |
val = (*chan_p).x * (float)PIXEL::maxChannelValue + 0.5f;
|
|
shun-iwasawa |
e2505a |
pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
e2505a |
? (float)PIXEL::maxChannelValue
|
|
shun-iwasawa |
e2505a |
: val);
|
|
shun-iwasawa |
e2505a |
val = (*chan_p).y * (float)PIXEL::maxChannelValue + 0.5f;
|
|
shun-iwasawa |
e2505a |
pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
e2505a |
? (float)PIXEL::maxChannelValue
|
|
shun-iwasawa |
e2505a |
: val);
|
|
shun-iwasawa |
e2505a |
val = (*chan_p).z * (float)PIXEL::maxChannelValue + 0.5f;
|
|
shun-iwasawa |
e2505a |
pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
e2505a |
? (float)PIXEL::maxChannelValue
|
|
shun-iwasawa |
e2505a |
: val);
|
|
shun-iwasawa |
e2505a |
val = (*chan_p).w * (float)PIXEL::maxChannelValue + 0.5f;
|
|
shun-iwasawa |
e2505a |
pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
|
|
shun-iwasawa |
e2505a |
? (float)PIXEL::maxChannelValue
|
|
shun-iwasawa |
e2505a |
: val);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
// obtain iris size from the depth value
|
|
shun-iwasawa |
e2505a |
//------------------------------------------------------------
|
|
shun-iwasawa |
e2505a |
inline float Iwa_BokehRefFx::calcIrisSize(const float depth,
|
|
shun-iwasawa |
e2505a |
const float bokehPixelAmount,
|
|
shun-iwasawa |
e2505a |
const double onFocusDistance) {
|
|
shun-iwasawa |
e2505a |
return ((float)onFocusDistance - depth) * bokehPixelAmount;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// resize/invert the iris according to the size ratio
|
|
shun-iwasawa |
e2505a |
// normalize the brightness
|
|
shun-iwasawa |
e2505a |
// resize to the output size
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::convertIris(const float irisSize, const TRectD& irisBBox,
|
|
shun-iwasawa |
e2505a |
const TTile& irisTile,
|
|
shun-iwasawa |
e2505a |
const TDimensionI& dimOut,
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_iris_before) {
|
|
shun-iwasawa |
e2505a |
// original size of the iris image
|
|
shun-iwasawa |
e2505a |
TDimensionD irisOrgSize = irisBBox.getSize();
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// obtain size ratio based on width
|
|
shun-iwasawa |
e2505a |
double irisSizeResampleRatio = irisSize / irisOrgSize.lx;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// create the raster for resizing
|
|
shun-iwasawa |
0e4a7a |
TDimensionD resizedIrisSize(std::abs(irisSizeResampleRatio) * irisOrgSize.lx,
|
|
shun-iwasawa |
0e4a7a |
std::abs(irisSizeResampleRatio) * irisOrgSize.ly);
|
|
shun-iwasawa |
e2505a |
TDimensionI filterSize((int)std::ceil(resizedIrisSize.lx),
|
|
shun-iwasawa |
e2505a |
(int)std::ceil(resizedIrisSize.ly));
|
|
shun-iwasawa |
e2505a |
TPointD resizeOffset((double)filterSize.lx - resizedIrisSize.lx,
|
|
shun-iwasawa |
e2505a |
(double)filterSize.ly - resizedIrisSize.ly);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
bool isIrisOffset[2] = {false, false};
|
|
shun-iwasawa |
e2505a |
// iris shape must be exactly at the center of the image
|
|
shun-iwasawa |
e2505a |
if ((dimOut.lx - filterSize.lx) % 2 == 1) {
|
|
shun-iwasawa |
e2505a |
filterSize.lx++;
|
|
shun-iwasawa |
e2505a |
isIrisOffset[0] = true;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
if ((dimOut.ly - filterSize.ly) % 2 == 1) {
|
|
shun-iwasawa |
e2505a |
filterSize.ly++;
|
|
shun-iwasawa |
e2505a |
isIrisOffset[1] = true;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// if the filter size becomes larger than the output size, return
|
|
shun-iwasawa |
e2505a |
if (filterSize.lx > dimOut.lx || filterSize.ly > dimOut.ly) {
|
|
shun-iwasawa |
e2505a |
std::cout
|
|
shun-iwasawa |
e2505a |
<< "Error: The iris filter size becomes larger than the source size!"
|
|
shun-iwasawa |
e2505a |
<< std::endl;
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
TRaster64P resizedIris(filterSize);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// offset
|
|
shun-iwasawa |
e2505a |
TAffine aff;
|
|
shun-iwasawa |
e2505a |
TPointD affOffset((isIrisOffset[0]) ? 0.5 : 1.0,
|
|
shun-iwasawa |
e2505a |
(isIrisOffset[1]) ? 0.5 : 1.0);
|
|
shun-iwasawa |
e2505a |
if (!isIrisOffset[0]) affOffset.x -= resizeOffset.x / 2;
|
|
shun-iwasawa |
e2505a |
if (!isIrisOffset[1]) affOffset.y -= resizeOffset.y / 2;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
aff = TTranslation(resizedIris->getCenterD() + affOffset);
|
|
shun-iwasawa |
e2505a |
aff *= TScale(irisSizeResampleRatio);
|
|
shun-iwasawa |
e2505a |
aff *= TTranslation(-(irisTile.getRaster()->getCenterD() + affOffset));
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// resample the iris
|
|
shun-iwasawa |
e2505a |
TRop::resample(resizedIris, irisTile.getRaster(), aff);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// sum of the value
|
|
shun-iwasawa |
e2505a |
float irisValAmount = 0.0;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
int iris_j = 0;
|
|
shun-iwasawa |
e2505a |
// initialize
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
|
|
shun-iwasawa |
e2505a |
fftcpx_iris_before[i].r = 0.0;
|
|
shun-iwasawa |
e2505a |
fftcpx_iris_before[i].i = 0.0;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
for (int j = (dimOut.ly - filterSize.ly) / 2; iris_j < filterSize.ly;
|
|
shun-iwasawa |
e2505a |
j++, iris_j++) {
|
|
shun-iwasawa |
e2505a |
TPixel64* pix = resizedIris->pixels(iris_j);
|
|
shun-iwasawa |
e2505a |
int iris_i = 0;
|
|
shun-iwasawa |
e2505a |
for (int i = (dimOut.lx - filterSize.lx) / 2; iris_i < filterSize.lx;
|
|
shun-iwasawa |
e2505a |
i++, iris_i++) {
|
|
shun-iwasawa |
e2505a |
// Value = 0.3R 0.59G 0.11B
|
|
shun-iwasawa |
e2505a |
fftcpx_iris_before[j * dimOut.lx + i].r =
|
|
shun-iwasawa |
e2505a |
((float)pix->r * 0.3f + (float)pix->g * 0.59f +
|
|
shun-iwasawa |
e2505a |
(float)pix->b * 0.11f) /
|
|
shun-iwasawa |
e2505a |
(float)USHRT_MAX;
|
|
shun-iwasawa |
e2505a |
irisValAmount += fftcpx_iris_before[j * dimOut.lx + i].r;
|
|
shun-iwasawa |
e2505a |
pix++;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Normalize value
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < dimOut.lx * dimOut.ly; i++)
|
|
shun-iwasawa |
e2505a |
fftcpx_iris_before[i].r /= irisValAmount;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// convert source image value rgb -> exposure
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::convertRGBToExposure(const float4* source_buff, int size,
|
|
shun-iwasawa |
e2505a |
float filmGamma,
|
|
shun-iwasawa |
e2505a |
bool sourceIsPremultiplied) {
|
|
shun-iwasawa |
e2505a |
float4* source_p = (float4*)source_buff;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++, source_p++) {
|
|
shun-iwasawa |
e2505a |
// continue if alpha channel is 0
|
|
shun-iwasawa |
e2505a |
if ((*source_p).w == 0.0f) {
|
|
shun-iwasawa |
e2505a |
(*source_p).x = 0.0f;
|
|
shun-iwasawa |
e2505a |
(*source_p).y = 0.0f;
|
|
shun-iwasawa |
e2505a |
(*source_p).z = 0.0f;
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// unmultiply for premultiplied source
|
|
shun-iwasawa |
e2505a |
// this will not be applied to non-premultiplied image such as "DigiBook"
|
|
shun-iwasawa |
e2505a |
// (digital overlay)
|
|
shun-iwasawa |
e2505a |
if (sourceIsPremultiplied) {
|
|
shun-iwasawa |
e2505a |
// unpremultiply
|
|
shun-iwasawa |
e2505a |
(*source_p).x /= (*source_p).w;
|
|
shun-iwasawa |
e2505a |
(*source_p).y /= (*source_p).w;
|
|
shun-iwasawa |
e2505a |
(*source_p).z /= (*source_p).w;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// RGB value -> exposure
|
|
shun-iwasawa |
e2505a |
(*source_p).x = pow(10, ((*source_p).x - 0.5f) / filmGamma);
|
|
shun-iwasawa |
e2505a |
(*source_p).y = pow(10, ((*source_p).y - 0.5f) / filmGamma);
|
|
shun-iwasawa |
e2505a |
(*source_p).z = pow(10, ((*source_p).z - 0.5f) / filmGamma);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// multiply with alpha channel
|
|
shun-iwasawa |
e2505a |
(*source_p).x *= (*source_p).w;
|
|
shun-iwasawa |
e2505a |
(*source_p).y *= (*source_p).w;
|
|
shun-iwasawa |
e2505a |
(*source_p).z *= (*source_p).w;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// generate the segment layer source at the current depth
|
|
shun-iwasawa |
e2505a |
// considering fillGap and doMedian options
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::retrieveLayer(const float4* source_buff,
|
|
shun-iwasawa |
e2505a |
const float4* segment_layer_buff,
|
|
shun-iwasawa |
e2505a |
const unsigned char* indexMap_mainSub,
|
|
shun-iwasawa |
e2505a |
int index, int lx, int ly, bool fillGap,
|
|
shun-iwasawa |
e2505a |
bool doMedian, int margin) {
|
|
shun-iwasawa |
e2505a |
// only when fillGap is ON and doMedian is OFF,
|
|
shun-iwasawa |
e2505a |
// fill the region where will be behind of the near layers
|
|
shun-iwasawa |
e2505a |
bool fill = (fillGap && !doMedian);
|
|
shun-iwasawa |
e2505a |
// retrieve the regions with the current depth
|
|
shun-iwasawa |
e2505a |
float4* source_p = (float4*)source_buff;
|
|
shun-iwasawa |
e2505a |
float4* layer_p = (float4*)segment_layer_buff;
|
|
shun-iwasawa |
e2505a |
unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < lx * ly; i++, source_p++, layer_p++, indexMap_p++) {
|
|
shun-iwasawa |
e2505a |
// continue if the current pixel is at the far layer
|
|
shun-iwasawa |
e2505a |
// consider the fill flag if the current pixel is at the near layer
|
|
shun-iwasawa |
e2505a |
if ((int)(*indexMap_p) < index || (!fill && (int)(*indexMap_p) > index))
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// copy pixel values
|
|
shun-iwasawa |
e2505a |
(*layer_p).x = (*source_p).x;
|
|
shun-iwasawa |
e2505a |
(*layer_p).y = (*source_p).y;
|
|
shun-iwasawa |
e2505a |
(*layer_p).z = (*source_p).z;
|
|
shun-iwasawa |
e2505a |
(*layer_p).w = (*source_p).w;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
if (!fillGap || !doMedian) return;
|
|
shun-iwasawa |
e2505a |
if (margin == 0) return;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// extend pixels by using median filter
|
|
shun-iwasawa |
e2505a |
unsigned char* generation_buff;
|
|
shun-iwasawa |
e2505a |
TRasterGR8P generation_buff_ras = allocateRasterAndLock<unsigned char="">(</unsigned>
|
|
shun-iwasawa |
e2505a |
&generation_buff, TDimensionI(lx, ly));
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
09e972 |
// extend (margin * 2) pixels in order to enough cover when two adjacent
|
|
shun-iwasawa |
09e972 |
// layers are both blurred in the maximum radius
|
|
shun-iwasawa |
09e972 |
for (int gen = 0; gen < margin * 2; gen++) {
|
|
shun-iwasawa |
e2505a |
// apply single median filter
|
|
shun-iwasawa |
e2505a |
doSingleMedian(source_buff, segment_layer_buff, indexMap_mainSub, index, lx,
|
|
shun-iwasawa |
e2505a |
ly, generation_buff, gen + 1);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
generation_buff_ras->unlock();
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// apply single median filter
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::doSingleMedian(const float4* source_buff,
|
|
shun-iwasawa |
e2505a |
const float4* segment_layer_buff,
|
|
shun-iwasawa |
e2505a |
const unsigned char* indexMap_mainSub,
|
|
shun-iwasawa |
e2505a |
int index, int lx, int ly,
|
|
shun-iwasawa |
e2505a |
const unsigned char* generation_buff,
|
|
shun-iwasawa |
e2505a |
int curGen) {
|
|
shun-iwasawa |
e2505a |
float4* source_p = (float4*)source_buff;
|
|
shun-iwasawa |
e2505a |
float4* layer_p = (float4*)segment_layer_buff;
|
|
shun-iwasawa |
e2505a |
unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
|
|
shun-iwasawa |
e2505a |
unsigned char* gen_p = (unsigned char*)generation_buff;
|
|
shun-iwasawa |
e2505a |
for (int posY = 0; posY < ly; posY++) {
|
|
shun-iwasawa |
e2505a |
for (int posX = 0; posX < lx;
|
|
shun-iwasawa |
e2505a |
posX++, source_p++, layer_p++, indexMap_p++, gen_p++) {
|
|
shun-iwasawa |
e2505a |
// continue if the current pixel is at the same or far depth
|
|
shun-iwasawa |
e2505a |
if ((int)(*indexMap_p) <= index) continue;
|
|
shun-iwasawa |
e2505a |
// continue if the current pixel is already extended
|
|
shun-iwasawa |
e2505a |
if ((*gen_p) > 0) continue;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// check out the neighbor pixels. store brightness in neighbor[x].w
|
|
shun-iwasawa |
e2505a |
float4 neighbor[8];
|
|
shun-iwasawa |
e2505a |
int neighbor_amount = 0;
|
|
shun-iwasawa |
e2505a |
for (int ky = posY - 1; ky <= posY + 1; ky++) {
|
|
shun-iwasawa |
e2505a |
for (int kx = posX - 1; kx <= posX + 1; kx++) {
|
|
shun-iwasawa |
e2505a |
// skip the current pixel itself
|
|
shun-iwasawa |
e2505a |
if (kx == posX && ky == posY) continue;
|
|
shun-iwasawa |
e2505a |
// border condition
|
|
shun-iwasawa |
e2505a |
if (ky < 0 || ky >= ly || kx < 0 || kx >= lx) continue;
|
|
shun-iwasawa |
e2505a |
// index in the buffer
|
|
shun-iwasawa |
e2505a |
int neighborId = ky * lx + kx;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
if ((int)indexMap_mainSub[neighborId] !=
|
|
shun-iwasawa |
e2505a |
index && // pixels from the original image can be used as
|
|
shun-iwasawa |
e2505a |
// neighbors
|
|
shun-iwasawa |
e2505a |
(generation_buff[neighborId] == 0 || // pixels which is not yet
|
|
shun-iwasawa |
e2505a |
// be extended cannot be
|
|
shun-iwasawa |
e2505a |
// used as neighbors
|
|
shun-iwasawa |
e2505a |
generation_buff[neighborId] == curGen)) // pixels which is
|
|
shun-iwasawa |
e2505a |
// extended in the
|
|
shun-iwasawa |
e2505a |
// current median
|
|
shun-iwasawa |
e2505a |
// generation cannot be
|
|
shun-iwasawa |
e2505a |
// used as neighbors
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
// compute brightness (actually, it is "pseudo" brightness
|
|
shun-iwasawa |
e2505a |
// since the source buffer is already converted to exposure)
|
|
shun-iwasawa |
e2505a |
float brightness = source_buff[neighborId].x * 0.3f +
|
|
shun-iwasawa |
e2505a |
source_buff[neighborId].y * 0.59f +
|
|
shun-iwasawa |
e2505a |
source_buff[neighborId].z * 0.11f;
|
|
shun-iwasawa |
e2505a |
// insert with sorting
|
|
shun-iwasawa |
e2505a |
int ins_index;
|
|
shun-iwasawa |
e2505a |
for (ins_index = 0; ins_index < neighbor_amount; ins_index++) {
|
|
shun-iwasawa |
e2505a |
if (neighbor[ins_index].w < brightness) break;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
// displace neighbor values from neighbor_amount-1 to ins_index
|
|
shun-iwasawa |
e2505a |
for (int k = neighbor_amount - 1; k >= ins_index; k--) {
|
|
shun-iwasawa |
e2505a |
neighbor[k + 1].x = neighbor[k].x;
|
|
shun-iwasawa |
e2505a |
neighbor[k + 1].y = neighbor[k].y;
|
|
shun-iwasawa |
e2505a |
neighbor[k + 1].z = neighbor[k].z;
|
|
shun-iwasawa |
e2505a |
neighbor[k + 1].w = neighbor[k].w;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
// set the neighbor value
|
|
shun-iwasawa |
e2505a |
neighbor[ins_index].x = source_buff[neighborId].x;
|
|
shun-iwasawa |
e2505a |
neighbor[ins_index].y = source_buff[neighborId].y;
|
|
shun-iwasawa |
e2505a |
neighbor[ins_index].z = source_buff[neighborId].z;
|
|
shun-iwasawa |
e2505a |
neighbor[ins_index].w = brightness;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// increment the count
|
|
shun-iwasawa |
e2505a |
neighbor_amount++;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// If there is no neighbor pixles available, continue
|
|
shun-iwasawa |
e2505a |
if (neighbor_amount == 0) continue;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// switch the behavior when there are even number of neighbors
|
|
shun-iwasawa |
e2505a |
bool flag = ((posX + posY) % 2 == 0);
|
|
shun-iwasawa |
e2505a |
// pick up the medium index
|
|
shun-iwasawa |
e2505a |
int pickIndex = (flag)
|
|
shun-iwasawa |
e2505a |
? (int)std::floor((float)(neighbor_amount - 1) / 2.0f)
|
|
shun-iwasawa |
e2505a |
: (int)std::ceil((float)(neighbor_amount - 1) / 2.0f);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// set the medium pixel values
|
|
shun-iwasawa |
e2505a |
(*layer_p).x = neighbor[pickIndex].x;
|
|
shun-iwasawa |
e2505a |
(*layer_p).y = neighbor[pickIndex].y;
|
|
shun-iwasawa |
e2505a |
(*layer_p).z = neighbor[pickIndex].z;
|
|
shun-iwasawa |
e2505a |
(*layer_p).w = (*source_p).w;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// set the generation
|
|
shun-iwasawa |
e2505a |
(*gen_p) = (unsigned char)curGen;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// normal-composite the layer as is, without filtering
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::compositeAsIs(const float4* segment_layer_buff,
|
|
shun-iwasawa |
e2505a |
const float4* result_buff_mainSub,
|
|
shun-iwasawa |
e2505a |
int size) {
|
|
shun-iwasawa |
e2505a |
float4* layer_p = (float4*)segment_layer_buff;
|
|
shun-iwasawa |
e2505a |
float4* result_p = (float4*)result_buff_mainSub;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++, layer_p++, result_p++) {
|
|
shun-iwasawa |
e2505a |
// in case the pixel is full opac
|
|
shun-iwasawa |
e2505a |
if ((*layer_p).w == 1.0f) {
|
|
shun-iwasawa |
e2505a |
(*result_p).x = (*layer_p).x;
|
|
shun-iwasawa |
e2505a |
(*result_p).y = (*layer_p).y;
|
|
shun-iwasawa |
e2505a |
(*result_p).z = (*layer_p).z;
|
|
shun-iwasawa |
e2505a |
(*result_p).w = 1.0f;
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
// in case the pixel is full transparent
|
|
shun-iwasawa |
e2505a |
else if ((*layer_p).w == 0.0f)
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
// in case the pixel is semi-transparent, do normal composite
|
|
shun-iwasawa |
e2505a |
else {
|
|
shun-iwasawa |
e2505a |
(*result_p).x = (*layer_p).x + (*result_p).x * (1.0f - (*layer_p).w);
|
|
shun-iwasawa |
e2505a |
(*result_p).y = (*layer_p).y + (*result_p).y * (1.0f - (*layer_p).w);
|
|
shun-iwasawa |
e2505a |
(*result_p).z = (*layer_p).z + (*result_p).z * (1.0f - (*layer_p).w);
|
|
shun-iwasawa |
e2505a |
(*result_p).w = (*layer_p).w + (*result_p).w * (1.0f - (*layer_p).w);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// retrieve segment layer image for each channel
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::retrieveChannel(const float4* segment_layer_buff, // src
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_r_before, // dst
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_g_before, // dst
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_b_before, // dst
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_a_before, // dst
|
|
shun-iwasawa |
e2505a |
int size) {
|
|
shun-iwasawa |
e2505a |
float4* layer_p = (float4*)segment_layer_buff;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++, layer_p++) {
|
|
shun-iwasawa |
e2505a |
fftcpx_r_before[i].r = (*layer_p).x;
|
|
shun-iwasawa |
e2505a |
fftcpx_g_before[i].r = (*layer_p).y;
|
|
shun-iwasawa |
e2505a |
fftcpx_b_before[i].r = (*layer_p).z;
|
|
shun-iwasawa |
e2505a |
fftcpx_a_before[i].r = (*layer_p).w;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// multiply filter on channel
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::multiplyFilter(kiss_fft_cpx* fftcpx_channel, // dst
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_iris, // filter
|
|
shun-iwasawa |
e2505a |
int size) {
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++) {
|
|
shun-iwasawa |
e2505a |
float re, im;
|
|
shun-iwasawa |
e2505a |
re = fftcpx_channel[i].r * fftcpx_iris[i].r -
|
|
shun-iwasawa |
e2505a |
fftcpx_channel[i].i * fftcpx_iris[i].i;
|
|
shun-iwasawa |
e2505a |
im = fftcpx_channel[i].r * fftcpx_iris[i].i +
|
|
shun-iwasawa |
e2505a |
fftcpx_channel[i].i * fftcpx_iris[i].r;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
fftcpx_channel[i].r = re;
|
|
shun-iwasawa |
e2505a |
fftcpx_channel[i].i = im;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// normal comosite the alpha channel
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::compositeAlpha(const float4* result_buff, // dst
|
|
shun-iwasawa |
e2505a |
const kiss_fft_cpx* fftcpx_alpha, // alpha
|
|
shun-iwasawa |
e2505a |
int lx, int ly) {
|
|
shun-iwasawa |
e2505a |
int size = lx * ly;
|
|
shun-iwasawa |
e2505a |
float4* result_p = (float4*)result_buff;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size; i++, result_p++) {
|
|
shun-iwasawa |
e2505a |
// modify fft coordinate to normal
|
|
shun-iwasawa |
e2505a |
float alpha = fftcpx_alpha[getCoord(i, lx, ly)].r / (float)size;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
if ((*result_p).w < 1.0f) {
|
|
shun-iwasawa |
e2505a |
if (alpha >= 1.0f)
|
|
shun-iwasawa |
e2505a |
(*result_p).w = 1.0f;
|
|
shun-iwasawa |
e2505a |
else
|
|
shun-iwasawa |
e2505a |
(*result_p).w = alpha + ((*result_p).w * (1.0f - alpha));
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
// interpolate main and sub exposures
|
|
shun-iwasawa |
e2505a |
// convert exposure -> value (0-1)
|
|
shun-iwasawa |
e2505a |
// set to the result
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::interpolateExposureAndConvertToRGB(
|
|
shun-iwasawa |
e2505a |
const float4* result_main_buff, // result1
|
|
shun-iwasawa |
e2505a |
const float4* result_sub_buff, // result2
|
|
shun-iwasawa |
e2505a |
const float* mainSub_ratio, // ratio
|
|
shun-iwasawa |
e2505a |
float filmGamma,
|
|
shun-iwasawa |
e2505a |
const float4* source_buff, // dst
|
|
shun-iwasawa |
e2505a |
int size) {
|
|
shun-iwasawa |
e2505a |
float4* resultMain_p = (float4*)result_main_buff;
|
|
shun-iwasawa |
e2505a |
float4* resultSub_p = (float4*)result_sub_buff;
|
|
shun-iwasawa |
e2505a |
float* ratio_p = (float*)mainSub_ratio;
|
|
shun-iwasawa |
e2505a |
float4* out_p = (float4*)source_buff;
|
|
shun-iwasawa |
e2505a |
for (int i = 0; i < size;
|
|
shun-iwasawa |
e2505a |
i++, resultMain_p++, resultSub_p++, ratio_p++, out_p++) {
|
|
shun-iwasawa |
e2505a |
// interpolate main and sub exposures
|
|
shun-iwasawa |
e2505a |
float4 result;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
result.x =
|
|
shun-iwasawa |
e2505a |
(*resultMain_p).x * (*ratio_p) + (*resultSub_p).x * (1.0f - (*ratio_p));
|
|
shun-iwasawa |
e2505a |
result.y =
|
|
shun-iwasawa |
e2505a |
(*resultMain_p).y * (*ratio_p) + (*resultSub_p).y * (1.0f - (*ratio_p));
|
|
shun-iwasawa |
e2505a |
result.z =
|
|
shun-iwasawa |
e2505a |
(*resultMain_p).z * (*ratio_p) + (*resultSub_p).z * (1.0f - (*ratio_p));
|
|
shun-iwasawa |
e2505a |
result.w =
|
|
shun-iwasawa |
e2505a |
(*resultMain_p).w * (*ratio_p) + (*resultSub_p).w * (1.0f - (*ratio_p));
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
(*out_p).w = result.w;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// convert exposure -> value (0-1)
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// continue for transparent pixel
|
|
shun-iwasawa |
e2505a |
if (result.w == 0.0f) {
|
|
shun-iwasawa |
e2505a |
(*out_p).x = 0.0f;
|
|
shun-iwasawa |
e2505a |
(*out_p).y = 0.0f;
|
|
shun-iwasawa |
e2505a |
(*out_p).z = 0.0f;
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// convert Exposure to value
|
|
shun-iwasawa |
e2505a |
result.x = log10(result.x) * filmGamma + 0.5f;
|
|
shun-iwasawa |
e2505a |
result.y = log10(result.y) * filmGamma + 0.5f;
|
|
shun-iwasawa |
e2505a |
result.z = log10(result.z) * filmGamma + 0.5f;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
(*out_p).x =
|
|
shun-iwasawa |
e2505a |
(result.x > 1.0f) ? 1.0f : ((result.x < 0.0f) ? 0.0f : result.x);
|
|
shun-iwasawa |
e2505a |
(*out_p).y =
|
|
shun-iwasawa |
e2505a |
(result.y > 1.0f) ? 1.0f : ((result.y < 0.0f) ? 0.0f : result.y);
|
|
shun-iwasawa |
e2505a |
(*out_p).z =
|
|
shun-iwasawa |
e2505a |
(result.z > 1.0f) ? 1.0f : ((result.z < 0.0f) ? 0.0f : result.z);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
Iwa_BokehRefFx::Iwa_BokehRefFx()
|
|
shun-iwasawa |
e2505a |
: m_onFocusDistance(0.5)
|
|
shun-iwasawa |
e2505a |
, m_bokehAmount(30.0)
|
|
shun-iwasawa |
e2505a |
, m_hardness(0.3)
|
|
shun-iwasawa |
e2505a |
, m_distancePrecision(10)
|
|
shun-iwasawa |
e2505a |
, m_fillGap(true)
|
|
shun-iwasawa |
e2505a |
, m_doMedian(true) {
|
|
shun-iwasawa |
e2505a |
// Bind parameters
|
|
shun-iwasawa |
e2505a |
addInputPort("Iris", m_iris);
|
|
shun-iwasawa |
e2505a |
addInputPort("Source", m_source);
|
|
shun-iwasawa |
e2505a |
addInputPort("Depth", m_depth);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
bindParam(this, "on_focus_distance", m_onFocusDistance, false);
|
|
shun-iwasawa |
e2505a |
bindParam(this, "bokeh_amount", m_bokehAmount, false);
|
|
shun-iwasawa |
e2505a |
bindParam(this, "hardness", m_hardness, false);
|
|
shun-iwasawa |
e2505a |
bindParam(this, "distance_precision", m_distancePrecision, false);
|
|
shun-iwasawa |
e2505a |
bindParam(this, "fill_gap", m_fillGap, false);
|
|
shun-iwasawa |
e2505a |
bindParam(this, "fill_gap_with_median_filter", m_doMedian, false);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Set the ranges of parameters
|
|
shun-iwasawa |
e2505a |
m_onFocusDistance->setValueRange(0, 1);
|
|
shun-iwasawa |
e2505a |
m_bokehAmount->setValueRange(0, 300);
|
|
shun-iwasawa |
e2505a |
m_bokehAmount->setMeasureName("fxLength");
|
|
shun-iwasawa |
e2505a |
m_hardness->setValueRange(0.05, 20.0);
|
|
shun-iwasawa |
e2505a |
m_distancePrecision->setValueRange(3, 128);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::doCompute(TTile& tile, double frame,
|
|
shun-iwasawa |
e2505a |
const TRenderSettings& settings) {
|
|
shun-iwasawa |
e2505a |
// If any of input is not connected, then do nothing
|
|
shun-iwasawa |
e2505a |
if (!m_iris.isConnected() || !m_source.isConnected() ||
|
|
shun-iwasawa |
e2505a |
!m_depth.isConnected()) {
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->clear();
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
QList<trastergr8p> rasterList;</trastergr8p>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
|
|
shun-iwasawa |
e2505a |
float bokehPixelAmount = getBokehPixelAmount(frame, settings.m_affine);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Obtain the larger size of bokeh between the nearest (black) point and
|
|
shun-iwasawa |
e2505a |
// the farthest (white) point, based on the focus distance.
|
|
shun-iwasawa |
e2505a |
double onFocusDistance = m_onFocusDistance->getValue(frame);
|
|
shun-iwasawa |
e2505a |
float maxIrisSize =
|
|
shun-iwasawa |
e2505a |
bokehPixelAmount * std::max((1.0 - onFocusDistance), onFocusDistance);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
int margin =
|
|
shun-iwasawa |
e2505a |
(maxIrisSize > 1.0f) ? (int)(std::ceil((maxIrisSize - 1.0f) / 2.0f)) : 0;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Range of computation
|
|
shun-iwasawa |
e2505a |
TRectD rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(),
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->getLy()));
|
|
shun-iwasawa |
e2505a |
rectOut = rectOut.enlarge(static_cast<double>(margin));</double>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
TDimensionI dimOut(static_cast<int>(rectOut.getLx() + 0.5),</int>
|
|
shun-iwasawa |
e2505a |
static_cast<int>(rectOut.getLy() + 0.5));</int>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Enlarge the size to the "fast size" for kissfft which has no factors other
|
|
shun-iwasawa |
e2505a |
// than 2,3, or 5.
|
|
shun-iwasawa |
e2505a |
if (dimOut.lx < 10000 && dimOut.ly < 10000) {
|
|
shun-iwasawa |
e2505a |
int new_x = kiss_fft_next_fast_size(dimOut.lx);
|
|
shun-iwasawa |
e2505a |
int new_y = kiss_fft_next_fast_size(dimOut.ly);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
rectOut = rectOut.enlarge(static_cast<double>(new_x - dimOut.lx) / 2.0,</double>
|
|
shun-iwasawa |
e2505a |
static_cast<double>(new_y - dimOut.ly) / 2.0);</double>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
dimOut.lx = new_x;
|
|
shun-iwasawa |
e2505a |
dimOut.ly = new_y;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// - - - Compute the input tiles - - -
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Automatically judge whether the source image is premultiplied or not
|
|
shun-iwasawa |
e2505a |
bool isPremultiplied;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// source image buffer
|
|
shun-iwasawa |
e2505a |
float4* source_buff;
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<float4>(&source_buff, dimOut));</float4>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
// source tile is used only in this focus.
|
|
shun-iwasawa |
e2505a |
// normalized source image data is stored in source_buff.
|
|
shun-iwasawa |
e2505a |
TTile sourceTile;
|
|
shun-iwasawa |
e2505a |
m_source->allocateAndCompute(sourceTile, rectOut.getP00(), dimOut,
|
|
shun-iwasawa |
e2505a |
tile.getRaster(), frame, settings);
|
|
shun-iwasawa |
e2505a |
// normalize the tile image to 0-1 and set to source_buff
|
|
shun-iwasawa |
e2505a |
TRaster32P ras32 = (TRaster32P)sourceTile.getRaster();
|
|
shun-iwasawa |
e2505a |
TRaster64P ras64 = (TRaster64P)sourceTile.getRaster();
|
|
shun-iwasawa |
e2505a |
lock.lockForRead();
|
|
shun-iwasawa |
e2505a |
if (ras32)
|
|
shun-iwasawa |
e2505a |
isPremultiplied =
|
|
shun-iwasawa |
e2505a |
setSourceRaster<traster32p, tpixel32="">(ras32, source_buff, dimOut);</traster32p,>
|
|
shun-iwasawa |
e2505a |
else if (ras64)
|
|
shun-iwasawa |
e2505a |
isPremultiplied =
|
|
shun-iwasawa |
e2505a |
setSourceRaster<traster64p, tpixel64="">(ras64, source_buff, dimOut);</traster64p,>
|
|
shun-iwasawa |
e2505a |
lock.unlock();
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->clear();
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// create the index map, which indicates which layer each pixel belongs to
|
|
shun-iwasawa |
e2505a |
// make two separations and interporate the results in order to avoid
|
|
shun-iwasawa |
e2505a |
// artifacts appear at the layer border
|
|
shun-iwasawa |
e2505a |
unsigned char* indexMap_main;
|
|
shun-iwasawa |
e2505a |
unsigned char* indexMap_sub;
|
|
shun-iwasawa |
e2505a |
rasterList.append(
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<unsigned char="">(&indexMap_main, dimOut));</unsigned>
|
|
shun-iwasawa |
e2505a |
rasterList.append(
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<unsigned char="">(&indexMap_sub, dimOut));</unsigned>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// interporation ratio between two results
|
|
shun-iwasawa |
e2505a |
float* mainSub_ratio;
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<float>(&mainSub_ratio, dimOut));</float>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// - - - depth segmentation - - -
|
|
shun-iwasawa |
e2505a |
QVector<float> segmentDepth_main;</float>
|
|
shun-iwasawa |
e2505a |
QVector<float> segmentDepth_sub;</float>
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
// depth image stored in 256 levels
|
|
shun-iwasawa |
e2505a |
unsigned char* depth_buff;
|
|
shun-iwasawa |
e2505a |
TRasterGR8P depth_buff_ras =
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<unsigned char="">(&depth_buff, dimOut);</unsigned>
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
TTile depthTile;
|
|
shun-iwasawa |
e2505a |
m_depth->allocateAndCompute(depthTile, rectOut.getP00(), dimOut,
|
|
shun-iwasawa |
e2505a |
tile.getRaster(), frame, settings);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
depth_buff_ras->unlock();
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->clear();
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// normalize brightness of the depth reference image to unsigned char
|
|
shun-iwasawa |
e2505a |
// and store into depth_buff
|
|
shun-iwasawa |
e2505a |
TRasterGR8P rasGR8 = (TRasterGR8P)depthTile.getRaster();
|
|
shun-iwasawa |
e2505a |
TRasterGR16P rasGR16 = (TRasterGR16P)depthTile.getRaster();
|
|
shun-iwasawa |
e2505a |
TRaster32P ras32 = (TRaster32P)depthTile.getRaster();
|
|
shun-iwasawa |
e2505a |
TRaster64P ras64 = (TRaster64P)depthTile.getRaster();
|
|
shun-iwasawa |
e2505a |
lock.lockForRead();
|
|
shun-iwasawa |
e2505a |
if (rasGR8)
|
|
shun-iwasawa |
e2505a |
setDepthRasterGray<trastergr8p, tpixelgr8="">(rasGR8, depth_buff, dimOut);</trastergr8p,>
|
|
shun-iwasawa |
e2505a |
else if (rasGR16)
|
|
shun-iwasawa |
e2505a |
setDepthRasterGray<trastergr16p, tpixelgr16="">(rasGR16, depth_buff,</trastergr16p,>
|
|
shun-iwasawa |
e2505a |
dimOut);
|
|
shun-iwasawa |
e2505a |
else if (ras32)
|
|
shun-iwasawa |
e2505a |
setDepthRaster<traster32p, tpixel32="">(ras32, depth_buff, dimOut);</traster32p,>
|
|
shun-iwasawa |
e2505a |
else if (ras64)
|
|
shun-iwasawa |
e2505a |
setDepthRaster<traster64p, tpixel64="">(ras64, depth_buff, dimOut);</traster64p,>
|
|
shun-iwasawa |
e2505a |
lock.unlock();
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
// create the depth index map
|
|
shun-iwasawa |
e2505a |
defineSegemntDepth(indexMap_main, indexMap_sub, mainSub_ratio, depth_buff,
|
|
shun-iwasawa |
e2505a |
dimOut, frame, segmentDepth_main, segmentDepth_sub);
|
|
shun-iwasawa |
e2505a |
// depth image is not needed anymore. release it
|
|
shun-iwasawa |
e2505a |
depth_buff_ras->unlock();
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->clear();
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// - - - iris image - - -
|
|
shun-iwasawa |
e2505a |
// Get the original size of Iris image
|
|
shun-iwasawa |
e2505a |
TRectD irisBBox;
|
|
shun-iwasawa |
e2505a |
m_iris->getBBox(frame, irisBBox, settings);
|
|
shun-iwasawa |
e2505a |
// Compute the iris tile.
|
|
shun-iwasawa |
e2505a |
TTile irisTile;
|
|
shun-iwasawa |
e2505a |
m_iris->allocateAndCompute(
|
|
shun-iwasawa |
e2505a |
irisTile, irisBBox.getP00(),
|
|
shun-iwasawa |
e2505a |
TDimension(static_cast<int>(irisBBox.getLx() + 0.5),</int>
|
|
shun-iwasawa |
e2505a |
static_cast<int>(irisBBox.getLy() + 0.5)),</int>
|
|
shun-iwasawa |
e2505a |
tile.getRaster(), frame, settings);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->clear();
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// for now only CPU computation is supported
|
|
shun-iwasawa |
e2505a |
// when introduced GPGPU capability, computation will be separated here
|
|
shun-iwasawa |
e2505a |
doCompute_CPU(frame, settings, bokehPixelAmount, maxIrisSize, margin, dimOut,
|
|
shun-iwasawa |
e2505a |
source_buff, indexMap_main, indexMap_sub, mainSub_ratio,
|
|
shun-iwasawa |
e2505a |
segmentDepth_main, segmentDepth_sub, irisTile, irisBBox,
|
|
shun-iwasawa |
e2505a |
isPremultiplied);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->clear();
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
TDimensionI actualMargin((dimOut.lx - tile.getRaster()->getSize().lx) / 2,
|
|
shun-iwasawa |
e2505a |
(dimOut.ly - tile.getRaster()->getSize().ly) / 2);
|
|
shun-iwasawa |
e2505a |
// clear the tile raster
|
|
shun-iwasawa |
e2505a |
tile.getRaster()->clear();
|
|
shun-iwasawa |
e2505a |
TRaster32P outRas32 = (TRaster32P)tile.getRaster();
|
|
shun-iwasawa |
e2505a |
TRaster64P outRas64 = (TRaster64P)tile.getRaster();
|
|
shun-iwasawa |
e2505a |
lock.lockForWrite();
|
|
shun-iwasawa |
e2505a |
if (outRas32)
|
|
shun-iwasawa |
e2505a |
setOutputRaster<traster32p, tpixel32="">(source_buff, outRas32, dimOut,</traster32p,>
|
|
shun-iwasawa |
e2505a |
actualMargin);
|
|
shun-iwasawa |
e2505a |
else if (outRas64)
|
|
shun-iwasawa |
e2505a |
setOutputRaster<traster64p, tpixel64="">(source_buff, outRas64, dimOut,</traster64p,>
|
|
shun-iwasawa |
e2505a |
actualMargin);
|
|
shun-iwasawa |
e2505a |
lock.unlock();
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// release all rasters
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
void Iwa_BokehRefFx::doCompute_CPU(
|
|
shun-iwasawa |
e2505a |
const double frame, const TRenderSettings& settings, float bokehPixelAmount,
|
|
shun-iwasawa |
e2505a |
float maxIrisSize, int margin, TDimensionI& dimOut, float4* source_buff,
|
|
shun-iwasawa |
e2505a |
unsigned char* indexMap_main, unsigned char* indexMap_sub,
|
|
shun-iwasawa |
e2505a |
float* mainSub_ratio, QVector<float>& segmentDepth_main,</float>
|
|
shun-iwasawa |
e2505a |
QVector<float>& segmentDepth_sub, TTile& irisTile, TRectD& irisBBox,</float>
|
|
shun-iwasawa |
e2505a |
bool sourceIsPremultiplied) {
|
|
shun-iwasawa |
e2505a |
QList<trastergr8p> rasterList;</trastergr8p>
|
|
shun-iwasawa |
e2505a |
QList<kiss_fftnd_cfg> planList;</kiss_fftnd_cfg>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// This fx is relatively heavy so the multi thread computation is introduced.
|
|
shun-iwasawa |
e2505a |
// Lock the mutex here in order to prevent multiple rendering tasks run at the
|
|
shun-iwasawa |
e2505a |
// same time.
|
|
shun-iwasawa |
e2505a |
QMutexLocker fx_locker(&fx_mutex);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// - - - memory allocation for FFT - - -
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// iris image
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_iris_before;
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_iris;
|
|
shun-iwasawa |
e2505a |
rasterList.append(
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_iris_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_iris, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// segment layers
|
|
shun-iwasawa |
e2505a |
float4* segment_layer_buff;
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<float4>(&segment_layer_buff, dimOut));</float4>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// alpha channel
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_alpha_before;
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_alpha;
|
|
shun-iwasawa |
e2505a |
rasterList.append(
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// RGB channels
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_r_before;
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_g_before;
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_b_before;
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_r;
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_g;
|
|
shun-iwasawa |
e2505a |
kiss_fft_cpx* fftcpx_b;
|
|
shun-iwasawa |
e2505a |
rasterList.append(
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
rasterList.append(
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
rasterList.append(
|
|
shun-iwasawa |
e2505a |
allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b_before, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b, dimOut));</kiss_fft_cpx>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// for accumulating result image
|
|
shun-iwasawa |
e2505a |
float4* result_main_buff;
|
|
shun-iwasawa |
e2505a |
float4* result_sub_buff;
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<float4>(&result_main_buff, dimOut));</float4>
|
|
shun-iwasawa |
e2505a |
rasterList.append(allocateRasterAndLock<float4>(&result_sub_buff, dimOut));</float4>
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRasters(rasterList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// fft plans
|
|
shun-iwasawa |
e2505a |
int dims[2] = {dimOut.ly, dimOut.lx};
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_fwd);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_bkwd);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_r_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_r_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_g_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_g_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_b_fwd = kiss_fftnd_alloc(dims, 2, false, 0, 0);
|
|
shun-iwasawa |
e2505a |
kiss_fftnd_cfg kissfft_plan_b_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_r_fwd);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_r_bkwd);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_g_fwd);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_g_bkwd);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_b_fwd);
|
|
shun-iwasawa |
e2505a |
planList.append(kissfft_plan_b_bkwd);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
int size = dimOut.lx * dimOut.ly;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// initialize result memory
|
|
shun-iwasawa |
e2505a |
memset(result_main_buff, 0, sizeof(float4) * size);
|
|
shun-iwasawa |
e2505a |
memset(result_sub_buff, 0, sizeof(float4) * size);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// obtain parameters
|
|
shun-iwasawa |
e2505a |
float filmGamma = (float)m_hardness->getValue(frame);
|
|
shun-iwasawa |
e2505a |
bool fillGap = m_fillGap->getValue();
|
|
shun-iwasawa |
e2505a |
bool doMedian = m_doMedian->getValue();
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// convert source image value rgb -> exposure
|
|
shun-iwasawa |
e2505a |
convertRGBToExposure(source_buff, size, filmGamma, sourceIsPremultiplied);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// compute twice (main and sub) for interpolation
|
|
shun-iwasawa |
e2505a |
for (int mainSub = 0; mainSub < 2; mainSub++) {
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
float4* result_buff_mainSub;
|
|
shun-iwasawa |
e2505a |
QVector<float> segmentDepth_mainSub;</float>
|
|
shun-iwasawa |
e2505a |
unsigned char* indexMap_mainSub;
|
|
shun-iwasawa |
e2505a |
if (mainSub == 0) // main process
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
result_buff_mainSub = result_main_buff;
|
|
shun-iwasawa |
e2505a |
segmentDepth_mainSub = segmentDepth_main;
|
|
shun-iwasawa |
e2505a |
indexMap_mainSub = indexMap_main;
|
|
shun-iwasawa |
e2505a |
} else // sub process
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
result_buff_mainSub = result_sub_buff;
|
|
shun-iwasawa |
e2505a |
segmentDepth_mainSub = segmentDepth_sub;
|
|
shun-iwasawa |
e2505a |
indexMap_mainSub = indexMap_sub;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Compute from the most distant segment layer
|
|
shun-iwasawa |
e2505a |
for (int index = 0; index < segmentDepth_mainSub.size(); index++) {
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// obtain iris size from the depth value
|
|
shun-iwasawa |
e2505a |
float irisSize =
|
|
shun-iwasawa |
e2505a |
calcIrisSize(segmentDepth_mainSub.at(index), bokehPixelAmount,
|
|
shun-iwasawa |
e2505a |
m_onFocusDistance->getValue(frame));
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// initialize the layer memory
|
|
shun-iwasawa |
e2505a |
memset(segment_layer_buff, 0, sizeof(float4) * size);
|
|
shun-iwasawa |
e2505a |
// generate the segment layer source at the current depth
|
|
shun-iwasawa |
e2505a |
// considering fillGap and doMedian options
|
|
shun-iwasawa |
e2505a |
retrieveLayer(source_buff, segment_layer_buff, indexMap_mainSub, index,
|
|
shun-iwasawa |
e2505a |
dimOut.lx, dimOut.ly, fillGap, doMedian,
|
|
shun-iwasawa |
e2505a |
(index == segmentDepth_mainSub.size() - 1) ? 0 : margin);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// if the layer is at (almost) the focus position
|
|
shun-iwasawa |
e2505a |
if (-1.0 <= irisSize && 1.0 >= irisSize) {
|
|
shun-iwasawa |
e2505a |
// normal-composite the layer as is, without filtering
|
|
shun-iwasawa |
e2505a |
compositeAsIs(segment_layer_buff, result_buff_mainSub, size);
|
|
shun-iwasawa |
e2505a |
// continue to next layer
|
|
shun-iwasawa |
e2505a |
continue;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// resize the iris image
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// resize/invert the iris according to the size ratio
|
|
shun-iwasawa |
e2505a |
// normalize the brightness
|
|
shun-iwasawa |
e2505a |
// resize to the output size
|
|
shun-iwasawa |
e2505a |
convertIris(irisSize, irisBBox, irisTile, dimOut, fftcpx_iris_before);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// Do FFT the iris image.
|
|
shun-iwasawa |
e2505a |
kiss_fftnd(kissfft_plan_fwd, fftcpx_iris_before, fftcpx_iris);
|
|
shun-iwasawa |
e2505a |
// fftwf_execute_dft(fftw_plan_fwd_r, iris_host, iris_host);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// initialize alpha
|
|
shun-iwasawa |
e2505a |
memset(fftcpx_alpha_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
e2505a |
// initialize channels
|
|
shun-iwasawa |
e2505a |
memset(fftcpx_r_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
e2505a |
memset(fftcpx_g_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
e2505a |
memset(fftcpx_b_before, 0, sizeof(kiss_fft_cpx) * size);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// retrieve segment layer image for each channel
|
|
shun-iwasawa |
e2505a |
retrieveChannel(segment_layer_buff, // src
|
|
shun-iwasawa |
e2505a |
fftcpx_r_before, // dst
|
|
shun-iwasawa |
e2505a |
fftcpx_g_before, // dst
|
|
shun-iwasawa |
e2505a |
fftcpx_b_before, // dst
|
|
shun-iwasawa |
e2505a |
fftcpx_alpha_before, // dst
|
|
shun-iwasawa |
e2505a |
size);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// forward fft of alpha channel
|
|
shun-iwasawa |
e2505a |
kiss_fftnd(kissfft_plan_fwd, fftcpx_alpha_before, fftcpx_alpha);
|
|
shun-iwasawa |
e2505a |
// fftwf_execute_dft(fftw_plan_fwd_r, alphaBokeh_host, alphaBokeh_host);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// multiply filter on alpha
|
|
shun-iwasawa |
e2505a |
multiplyFilter(fftcpx_alpha, // dst
|
|
shun-iwasawa |
e2505a |
fftcpx_iris, // filter
|
|
shun-iwasawa |
e2505a |
size);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// inverse fft the alpha channel
|
|
shun-iwasawa |
e2505a |
// note that the result is multiplied by the image size
|
|
shun-iwasawa |
e2505a |
kiss_fftnd(kissfft_plan_bkwd, fftcpx_alpha, fftcpx_alpha_before);
|
|
shun-iwasawa |
e2505a |
// fftwf_execute_dft(fftw_plan_bkwd_r, alphaBokeh_host, alphaBokeh_host);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// normal composite the alpha channel
|
|
shun-iwasawa |
e2505a |
compositeAlpha(result_buff_mainSub, // dst
|
|
shun-iwasawa |
e2505a |
fftcpx_alpha_before, // alpha
|
|
shun-iwasawa |
e2505a |
dimOut.lx, dimOut.ly);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// create worker threads
|
|
shun-iwasawa |
e2505a |
BokehRefThread threadR(0, fftcpx_r_before, fftcpx_r, fftcpx_alpha_before,
|
|
shun-iwasawa |
e2505a |
fftcpx_iris, result_buff_mainSub,
|
|
shun-iwasawa |
e2505a |
kissfft_plan_r_fwd, kissfft_plan_r_bkwd, dimOut);
|
|
shun-iwasawa |
e2505a |
BokehRefThread threadG(1, fftcpx_g_before, fftcpx_g, fftcpx_alpha_before,
|
|
shun-iwasawa |
e2505a |
fftcpx_iris, result_buff_mainSub,
|
|
shun-iwasawa |
e2505a |
kissfft_plan_g_fwd, kissfft_plan_g_bkwd, dimOut);
|
|
shun-iwasawa |
e2505a |
BokehRefThread threadB(2, fftcpx_b_before, fftcpx_b, fftcpx_alpha_before,
|
|
shun-iwasawa |
e2505a |
fftcpx_iris, result_buff_mainSub,
|
|
shun-iwasawa |
e2505a |
kissfft_plan_b_fwd, kissfft_plan_b_bkwd, dimOut);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// If you set this flag to true, the fx will be forced to compute in
|
|
shun-iwasawa |
e2505a |
// single
|
|
shun-iwasawa |
e2505a |
// thread.
|
|
shun-iwasawa |
e2505a |
// Under some specific condition (such as calling from single-threaded
|
|
shun-iwasawa |
e2505a |
// tcomposer)
|
|
shun-iwasawa |
e2505a |
// we may need to use this flag... For now, I'll keep this option unused.
|
|
shun-iwasawa |
e2505a |
// TODO: investigate this.
|
|
shun-iwasawa |
e2505a |
bool renderInSingleThread = false;
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
if (renderInSingleThread) {
|
|
shun-iwasawa |
e2505a |
threadR.run();
|
|
shun-iwasawa |
e2505a |
threadG.run();
|
|
shun-iwasawa |
e2505a |
threadB.run();
|
|
shun-iwasawa |
e2505a |
} else {
|
|
shun-iwasawa |
e2505a |
threadR.start();
|
|
shun-iwasawa |
e2505a |
threadG.start();
|
|
shun-iwasawa |
e2505a |
threadB.start();
|
|
shun-iwasawa |
e2505a |
int waitCount = 0;
|
|
shun-iwasawa |
e2505a |
while (1) {
|
|
shun-iwasawa |
e2505a |
if ((settings.m_isCanceled && *settings.m_isCanceled) ||
|
|
shun-iwasawa |
e2505a |
waitCount >= 2000) // 100 second timeout
|
|
shun-iwasawa |
e2505a |
{
|
|
shun-iwasawa |
e2505a |
if (!threadR.isFinished()) threadR.terminateThread();
|
|
shun-iwasawa |
e2505a |
if (!threadG.isFinished()) threadG.terminateThread();
|
|
shun-iwasawa |
e2505a |
if (!threadB.isFinished()) threadB.terminateThread();
|
|
shun-iwasawa |
e2505a |
while (!threadR.isFinished() || !threadG.isFinished() ||
|
|
shun-iwasawa |
e2505a |
!threadB.isFinished()) {
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
if (threadR.isFinished() && threadG.isFinished() &&
|
|
shun-iwasawa |
e2505a |
threadB.isFinished())
|
|
shun-iwasawa |
e2505a |
break;
|
|
shun-iwasawa |
e2505a |
QThread::msleep(50);
|
|
shun-iwasawa |
e2505a |
waitCount++;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
} // for each layer
|
|
shun-iwasawa |
e2505a |
} // for main and sub
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// cancel check
|
|
shun-iwasawa |
e2505a |
if (settings.m_isCanceled && *settings.m_isCanceled) {
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
return;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// interpolate main and sub exposures
|
|
shun-iwasawa |
e2505a |
// convert exposure -> RGB (0-1)
|
|
shun-iwasawa |
e2505a |
// set to the result
|
|
shun-iwasawa |
e2505a |
interpolateExposureAndConvertToRGB(result_main_buff, // result1
|
|
shun-iwasawa |
e2505a |
result_sub_buff, // result2
|
|
shun-iwasawa |
e2505a |
mainSub_ratio, // ratio
|
|
shun-iwasawa |
e2505a |
filmGamma,
|
|
shun-iwasawa |
e2505a |
source_buff, // dst
|
|
shun-iwasawa |
e2505a |
size);
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
// release rasters and plans
|
|
shun-iwasawa |
e2505a |
releaseAllRastersAndPlans(rasterList, planList);
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
bool Iwa_BokehRefFx::doGetBBox(double frame, TRectD& bBox,
|
|
shun-iwasawa |
e2505a |
const TRenderSettings& info) {
|
|
shun-iwasawa |
e2505a |
bBox = TConsts::infiniteRectD;
|
|
shun-iwasawa |
e2505a |
return true;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
//--------------------------------------------
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
bool Iwa_BokehRefFx::canHandle(const TRenderSettings& info, double frame) {
|
|
shun-iwasawa |
e2505a |
return false;
|
|
shun-iwasawa |
e2505a |
}
|
|
shun-iwasawa |
e2505a |
|
|
shun-iwasawa |
e2505a |
FX_PLUGIN_IDENTIFIER(Iwa_BokehRefFx, "iwa_BokehRefFx")
|