429 lines
12 KiB
C++
429 lines
12 KiB
C++
|
|
|
|
#include "stdfx.h"
|
|
#include "tfxparam.h"
|
|
#include "trop.h"
|
|
#include "tdoubleparam.h"
|
|
#include "trasterfx.h"
|
|
|
|
/* (Daniele)
|
|
|
|
NOTE: Current LocalBlurFx is effectively flawed. Following implementation relies on the idea that
|
|
the blurring filter is separable and therefore appliable on rows and columns, in sequence.
|
|
|
|
It actually is not. It can be easily verified applying the fx on a chessboard with a strong
|
|
blur intensity. The squares will be cast vertically towards blurred regions.
|
|
|
|
Originally, this was a sub-optimal O(lx * ly * blur) algorithm. The following (still separated)
|
|
is O(lx * ly) using precomputed sums in an additional line.
|
|
|
|
The 'correct' algorithm could be implemented again as O(lx * ly * blur), with additional O(lx * blur)
|
|
memory usage with precomputed sums along (blur) rows.
|
|
|
|
Pixels would have to be filtered one by one, but the horizontal filtering per pixel convolution row
|
|
would take O(1). Thus it would be O(blur) (column filtering) per pixel.
|
|
*/
|
|
|
|
//********************************************************************************
|
|
// Local namespace stuff
|
|
//********************************************************************************
|
|
|
|
namespace
|
|
{
|
|
|
|
struct Sums {
|
|
TUINT64 *m_sumsIX_r; //!< m_sumsIX1[i+1] = m_sumsIX1[i] + i * pix.r
|
|
TUINT64 *m_sumsIX_g;
|
|
TUINT64 *m_sumsIX_b;
|
|
TUINT64 *m_sumsIX_m;
|
|
TUINT64 *m_sumsX_r; //!< m_sumsX[i+1] = m_sumsX[i] + pix.r
|
|
TUINT64 *m_sumsX_g;
|
|
TUINT64 *m_sumsX_b;
|
|
TUINT64 *m_sumsX_m;
|
|
|
|
Sums(int length)
|
|
{
|
|
++length;
|
|
m_sumsIX_r = new TUINT64[length];
|
|
m_sumsIX_g = new TUINT64[length];
|
|
m_sumsIX_b = new TUINT64[length];
|
|
m_sumsIX_m = new TUINT64[length];
|
|
|
|
m_sumsX_r = new TUINT64[length];
|
|
m_sumsX_g = new TUINT64[length];
|
|
m_sumsX_b = new TUINT64[length];
|
|
m_sumsX_m = new TUINT64[length];
|
|
}
|
|
|
|
~Sums()
|
|
{
|
|
delete[] m_sumsIX_r;
|
|
delete[] m_sumsIX_g;
|
|
delete[] m_sumsIX_b;
|
|
delete[] m_sumsIX_m;
|
|
|
|
delete[] m_sumsX_r;
|
|
delete[] m_sumsX_g;
|
|
delete[] m_sumsX_b;
|
|
delete[] m_sumsX_m;
|
|
}
|
|
|
|
template <typename Pix>
|
|
void build(Pix *line, int wrap, int n)
|
|
{
|
|
++n;
|
|
|
|
m_sumsIX_r[0] = m_sumsX_r[0] = 0;
|
|
m_sumsIX_g[0] = m_sumsX_g[0] = 0;
|
|
m_sumsIX_b[0] = m_sumsX_b[0] = 0;
|
|
m_sumsIX_m[0] = m_sumsX_m[0] = 0;
|
|
|
|
Pix *pix;
|
|
int i, i_1;
|
|
|
|
for (pix = line, i_1 = 0, i = 1; i < n; pix += wrap, i_1 = i++) {
|
|
m_sumsIX_r[i] = m_sumsIX_r[i_1] + i * pix->r;
|
|
m_sumsIX_g[i] = m_sumsIX_g[i_1] + i * pix->g;
|
|
m_sumsIX_b[i] = m_sumsIX_b[i_1] + i * pix->b;
|
|
m_sumsIX_m[i] = m_sumsIX_m[i_1] + i * pix->m;
|
|
|
|
m_sumsX_r[i] = m_sumsX_r[i_1] + pix->r;
|
|
m_sumsX_g[i] = m_sumsX_g[i_1] + pix->g;
|
|
m_sumsX_b[i] = m_sumsX_b[i_1] + pix->b;
|
|
m_sumsX_m[i] = m_sumsX_m[i_1] + pix->m;
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
template <typename Pix, typename Grey>
|
|
void filterLine(Pix *lineIn, int wrapIn, Grey *lineGr, int wrapGr, Pix *lineOut, int wrapOut, int length,
|
|
double blurFactor, Sums &sums)
|
|
{
|
|
// Build temporary sums
|
|
sums.build(lineIn, wrapIn, length);
|
|
|
|
// Declare vars
|
|
double blur, kLeft, kRight, cLeft, cRight;
|
|
double blurI;
|
|
|
|
// Perform line filtering
|
|
Pix *pixIn, *pixOut;
|
|
Grey *pixGr;
|
|
|
|
int i, iLeft, iRight;
|
|
++length;
|
|
|
|
for (i = 1, pixIn = lineIn, pixGr = lineGr, pixOut = lineOut; i < length;
|
|
++i, pixIn += wrapIn, pixGr += wrapGr, pixOut += wrapOut) {
|
|
blur = pixGr->value * blurFactor; // A table of factors should be made - since we have a finite
|
|
|
|
if (blur > 0.0) {
|
|
blur += 0.5; /*-- 0.5足すのは、注目ピクセルの半径分。例えばBlur0.5は、
|
|
注目ピクセルの外側0.5ピクセルボケるということなので。
|
|
--*/
|
|
|
|
blurI = (double)tfloor(blur);
|
|
|
|
double amount = blur + (2 * blur - blurI - 1) * blurI;
|
|
|
|
double dR = 1.0 / amount;
|
|
double ini = (blur - blurI) / amount;
|
|
|
|
kLeft = dR;
|
|
cLeft = ini - dR * (i - blurI);
|
|
kRight = -dR;
|
|
cRight = blur / amount + dR * i;
|
|
|
|
// NOTE: The normalization factor with blur (not integer) would be:
|
|
// 1.0 / (1 + 2 * blurI - (blurI / blur) * (blurI + 1)) -- could be done using a factors table
|
|
|
|
iLeft = tmax(i - tfloor(blur) - 1, 0);
|
|
iRight = tmin(i + tfloor(blur), length - 1);
|
|
|
|
pixOut->r = troundp(
|
|
kLeft * (sums.m_sumsIX_r[i] - sums.m_sumsIX_r[iLeft]) + kRight * (sums.m_sumsIX_r[iRight] - sums.m_sumsIX_r[i]) +
|
|
cLeft * (sums.m_sumsX_r[i] - sums.m_sumsX_r[iLeft]) + cRight * (sums.m_sumsX_r[iRight] - sums.m_sumsX_r[i]));
|
|
pixOut->g = troundp(
|
|
kLeft * (sums.m_sumsIX_g[i] - sums.m_sumsIX_g[iLeft]) + kRight * (sums.m_sumsIX_g[iRight] - sums.m_sumsIX_g[i]) +
|
|
cLeft * (sums.m_sumsX_g[i] - sums.m_sumsX_g[iLeft]) + cRight * (sums.m_sumsX_g[iRight] - sums.m_sumsX_g[i]));
|
|
pixOut->b = troundp(
|
|
kLeft * (sums.m_sumsIX_b[i] - sums.m_sumsIX_b[iLeft]) + kRight * (sums.m_sumsIX_b[iRight] - sums.m_sumsIX_b[i]) +
|
|
cLeft * (sums.m_sumsX_b[i] - sums.m_sumsX_b[iLeft]) + cRight * (sums.m_sumsX_b[iRight] - sums.m_sumsX_b[i]));
|
|
pixOut->m = troundp(
|
|
kLeft * (sums.m_sumsIX_m[i] - sums.m_sumsIX_m[iLeft]) + kRight * (sums.m_sumsIX_m[iRight] - sums.m_sumsIX_m[i]) +
|
|
cLeft * (sums.m_sumsX_m[i] - sums.m_sumsX_m[iLeft]) + cRight * (sums.m_sumsX_m[iRight] - sums.m_sumsX_m[i]));
|
|
} else
|
|
*pixOut = *pixIn;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
template <typename Pix, typename Grey>
|
|
void doLocalBlur(TRasterPT<Pix> rin, TRasterPT<Pix> rcontrol, TRasterPT<Pix> rout,
|
|
double blur, const TPoint &displacement)
|
|
{
|
|
assert(rin->getLx() == rcontrol->getLx() && rin->getLy() == rcontrol->getLy());
|
|
|
|
double blurFactor = blur / Grey::maxChannelValue;
|
|
|
|
int x, y, inLx, inLy, outLx, outLy, wrapIn, wrapOut, wrapC;
|
|
|
|
inLx = rin->getLx();
|
|
inLy = rin->getLy();
|
|
wrapIn = rin->getWrap();
|
|
outLx = rout->getLx();
|
|
outLy = rout->getLy();
|
|
wrapOut = rout->getWrap();
|
|
|
|
// Convert the control raster to grey values (this avoids the overhead of performing the pixel-to-value
|
|
// conversion twice, once per line filtering)
|
|
TRasterPT<Grey> rcontrolGrey(rcontrol->getLx(), rcontrol->getLy());
|
|
TRop::convert(rcontrolGrey, rcontrol);
|
|
|
|
wrapC = rcontrolGrey->getWrap();
|
|
|
|
Pix *lineIn, *bufIn;
|
|
Grey *lineC, *bufC;
|
|
|
|
// Filter rin. The output filtering is still stored in rin.
|
|
|
|
rin->lock();
|
|
rcontrolGrey->lock();
|
|
|
|
bufIn = rin->pixels(0);
|
|
bufC = rcontrolGrey->pixels(0);
|
|
|
|
{
|
|
Sums sums(inLx);
|
|
|
|
for (y = 0, lineIn = bufIn, lineC = bufC; y < inLy;
|
|
++y, lineIn += wrapIn, lineC += wrapC) {
|
|
// Filter row
|
|
filterLine(lineIn, 1, lineC, 1, lineIn, 1, inLx, blurFactor, sums);
|
|
}
|
|
}
|
|
|
|
{
|
|
Sums sums(inLy);
|
|
|
|
for (x = 0, lineIn = bufIn, lineC = bufC; x < inLx;
|
|
++x, ++lineIn, ++lineC) {
|
|
// Filter column
|
|
filterLine(lineIn, wrapIn, lineC, wrapC, lineIn, wrapIn, inLy, blurFactor, sums);
|
|
}
|
|
}
|
|
|
|
rin->unlock();
|
|
rcontrolGrey->unlock();
|
|
|
|
// Copy the interesting part of rin to rout
|
|
TRect rectOut(rout->getBounds() - displacement);
|
|
TRect rectIn(rin->getBounds() + displacement);
|
|
|
|
TRop::copy(rout->extract(rectIn), rin->extract(rectOut));
|
|
}
|
|
|
|
} //namespace
|
|
|
|
//********************************************************************************
|
|
// LocalBlurFx implementation
|
|
//********************************************************************************
|
|
|
|
class LocalBlurFx : public TStandardRasterFx
|
|
{
|
|
FX_PLUGIN_DECLARATION(LocalBlurFx)
|
|
|
|
protected:
|
|
TRasterFxPort m_up, m_ref;
|
|
TDoubleParamP m_value;
|
|
|
|
public:
|
|
LocalBlurFx()
|
|
: m_value(20)
|
|
{
|
|
m_value->setMeasureName("fxLength");
|
|
addInputPort("Source", m_up);
|
|
addInputPort("Reference", m_ref);
|
|
bindParam(this, "value", m_value);
|
|
m_value->setValueRange(0, (std::numeric_limits<double>::max)());
|
|
}
|
|
|
|
~LocalBlurFx() {}
|
|
|
|
bool canHandle(const TRenderSettings &info, double frame)
|
|
{
|
|
return (isAlmostIsotropic(info.m_affine) || m_value->getValue(frame) == 0);
|
|
}
|
|
|
|
bool doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info)
|
|
{
|
|
if (m_up.isConnected()) {
|
|
bool ret = m_up->doGetBBox(frame, bBox, info);
|
|
|
|
double blur = fabs(m_value->getValue(frame));
|
|
int blurI = tceil(blur);
|
|
bBox = bBox.enlarge(blurI);
|
|
|
|
return ret;
|
|
} else {
|
|
bBox = TRectD();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void enlarge(const TRectD &bbox, TRectD &requestedRect, int blur);
|
|
|
|
void doDryCompute(TRectD &rect, double frame, const TRenderSettings &info);
|
|
void doCompute(TTile &tile, double frame, const TRenderSettings &info);
|
|
|
|
int getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info);
|
|
};
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
void LocalBlurFx::enlarge(const TRectD &bbox, TRectD &requestedRect, int blur)
|
|
{
|
|
// See BlurFx: this is a faithful replica
|
|
if (bbox.isEmpty() || requestedRect.isEmpty()) {
|
|
requestedRect.empty();
|
|
return;
|
|
}
|
|
|
|
TRectD enlargedBBox(bbox.enlarge(blur));
|
|
TRectD enlargedOut(requestedRect.enlarge(blur));
|
|
|
|
TPointD originalP00(requestedRect.getP00());
|
|
requestedRect = (enlargedOut * bbox) + (enlargedBBox * requestedRect);
|
|
|
|
requestedRect -= originalP00;
|
|
requestedRect.x0 = tfloor(requestedRect.x0);
|
|
requestedRect.y0 = tfloor(requestedRect.y0);
|
|
requestedRect.x1 = tceil(requestedRect.x1);
|
|
requestedRect.y1 = tceil(requestedRect.y1);
|
|
requestedRect += originalP00;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
void LocalBlurFx::doDryCompute(TRectD &rectOut, double frame, const TRenderSettings &info)
|
|
{
|
|
// Mimics the doCompute() without actual computation.
|
|
|
|
TTile refTile;
|
|
bool isUp = m_up.isConnected();
|
|
bool isDown = m_ref.isConnected();
|
|
|
|
if (!isUp)
|
|
return;
|
|
|
|
if (!isDown) {
|
|
m_up->dryCompute(rectOut, frame, info);
|
|
return;
|
|
}
|
|
|
|
double blur = fabs(m_value->getValue(frame) * sqrt(fabs(info.m_affine.det())));
|
|
int blurI = tceil(blur);
|
|
|
|
TRectD bboxIn;
|
|
if (!m_up->getBBox(frame, bboxIn, info) || rectOut.isEmpty())
|
|
return;
|
|
|
|
TRectD rectIn(rectOut);
|
|
enlarge(bboxIn, rectIn, blurI);
|
|
|
|
if (rectIn.isEmpty())
|
|
return;
|
|
|
|
m_up->dryCompute(rectOut, frame, info);
|
|
m_ref->dryCompute(rectOut, frame, info);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
void LocalBlurFx::doCompute(TTile &tile, double frame, const TRenderSettings &info)
|
|
{
|
|
TTile refTile;
|
|
bool isUp = m_up.isConnected();
|
|
bool isDown = m_ref.isConnected();
|
|
|
|
if (!isUp)
|
|
return;
|
|
|
|
if (!isDown) {
|
|
m_up->compute(tile, frame, info);
|
|
return;
|
|
}
|
|
|
|
// Generic case
|
|
assert(isAlmostIsotropic(info.m_affine));
|
|
double blur = fabs(m_value->getValue(frame) * sqrt(fabs(info.m_affine.det())));
|
|
int blurI = tceil(blur);
|
|
|
|
// Get the requested tile's geometry
|
|
TRectD rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(), tile.getRaster()->getLy()));
|
|
|
|
// Retrieve the input interesting geometry - and ensure that something actually has
|
|
// to be computed
|
|
TRectD bboxIn;
|
|
if (!m_up->getBBox(frame, bboxIn, info) || rectOut.isEmpty())
|
|
return;
|
|
|
|
TRectD rectIn(rectOut);
|
|
enlarge(bboxIn, rectIn, blurI);
|
|
|
|
if (rectIn.isEmpty())
|
|
return;
|
|
|
|
//Finally, allocate and compute the blur argument
|
|
TTile tileIn;
|
|
m_up->allocateAndCompute(
|
|
tileIn, rectIn.getP00(), TDimension(rectIn.getLx(), rectIn.getLy()),
|
|
tile.getRaster(), frame, info);
|
|
|
|
TTile tileRef;
|
|
m_ref->allocateAndCompute(
|
|
tileRef, rectIn.getP00(), TDimension(rectIn.getLx(), rectIn.getLy()),
|
|
tile.getRaster(), frame, info);
|
|
|
|
// Perform Local Blur
|
|
TRasterP inRas(tileIn.getRaster());
|
|
TRasterP refRas(tileRef.getRaster());
|
|
TRasterP outRas(tile.getRaster());
|
|
|
|
TRaster32P in32(inRas);
|
|
TRaster32P ref32(refRas);
|
|
TRaster32P out32(outRas);
|
|
|
|
TPoint displacement(convert(rectIn.getP00() - tile.m_pos)); // inTile position relative to (out)tile
|
|
// The difference already has integer coordinates due to enlarge()
|
|
|
|
if (in32 && ref32 && out32)
|
|
doLocalBlur<TPixelRGBM32, TPixelGR8>(in32, ref32, out32, blur, displacement);
|
|
else {
|
|
TRaster64P in64(inRas);
|
|
TRaster64P ref64(refRas);
|
|
TRaster64P out64(outRas);
|
|
|
|
if (in64 && ref64 && out64)
|
|
doLocalBlur<TPixelRGBM64, TPixelGR16>(in64, ref64, out64, blur, displacement);
|
|
else
|
|
throw TException("LocalBlurFx: unsupported raster type");
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
int LocalBlurFx::getMemoryRequirement(const TRectD &rect, double frame, const TRenderSettings &info)
|
|
{
|
|
double blur = fabs(m_value->getValue(frame) * sqrt(fabs(info.m_affine.det())));
|
|
int blurI = tceil(blur);
|
|
|
|
return 2 * TRasterFx::memorySize(rect.enlarge(blurI), info.m_bpp);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
FX_PLUGIN_IDENTIFIER(LocalBlurFx, "localBlurFx")
|