- Switch from gaussian-bokeh blur to mean-value which is much faster
and from matte's quality behaves not worse than gaussian-bokeh.
- Added Edge matte output so areas where black/white clipping happens
could be easily controlled.
Currently not sure if it's possible than operation returns two
buffers, so now implemented as separate clipping operation call
with specific options.
- Changed logic of detecting edges - check if most pixels within
kernel are different fits some tolerance interval.
- Added two extra options which controls how edges are detecting:
* Edge Kernel Radius defines radius of kernel within pixel's value
is checking.
* Edge Kernel Tolerance defines maximal difference between value
of current pixel and it's neigbours which are trated as belng to
the same plane )background of foreground)
Unfortunately, value for this two sliders for existing files shall
be tweaked manually. Default values are 3 for radius and 0.1 for
tolerance.
226 lines
8.0 KiB
C++
226 lines
8.0 KiB
C++
/*
|
|
* Copyright 2012, Blender Foundation.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Contributor:
|
|
* Jeroen Bakker
|
|
* Monique Dewanchand
|
|
* Sergey Sharybin
|
|
*/
|
|
|
|
#include "COM_KeyingNode.h"
|
|
|
|
#include "COM_ExecutionSystem.h"
|
|
|
|
#include "COM_KeyingOperation.h"
|
|
#include "COM_KeyingBlurOperation.h"
|
|
#include "COM_KeyingDespillOperation.h"
|
|
#include "COM_KeyingClipOperation.h"
|
|
|
|
#include "COM_SeparateChannelOperation.h"
|
|
#include "COM_CombineChannelsOperation.h"
|
|
#include "COM_ConvertRGBToYCCOperation.h"
|
|
#include "COM_ConvertYCCToRGBOperation.h"
|
|
#include "COM_GaussianBokehBlurOperation.h"
|
|
#include "COM_SetValueOperation.h"
|
|
|
|
#include "COM_DilateErodeOperation.h"
|
|
|
|
#include "COM_SetAlphaOperation.h"
|
|
|
|
KeyingNode::KeyingNode(bNode *editorNode): Node(editorNode)
|
|
{
|
|
}
|
|
|
|
OutputSocket *KeyingNode::setupPreBlur(ExecutionSystem *graph, InputSocket *inputImage, int size, OutputSocket **originalImage)
|
|
{
|
|
ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation();
|
|
convertRGBToYCCOperation->setMode(0); /* ITU 601 */
|
|
|
|
inputImage->relinkConnections(convertRGBToYCCOperation->getInputSocket(0), 0, graph);
|
|
graph->addOperation(convertRGBToYCCOperation);
|
|
|
|
CombineChannelsOperation *combineOperation = new CombineChannelsOperation();
|
|
graph->addOperation(combineOperation);
|
|
|
|
for (int channel = 0; channel < 4; channel++) {
|
|
SeparateChannelOperation *separateOperation = new SeparateChannelOperation();
|
|
separateOperation->setChannel(channel);
|
|
addLink(graph, convertRGBToYCCOperation->getOutputSocket(0), separateOperation->getInputSocket(0));
|
|
graph->addOperation(separateOperation);
|
|
|
|
if (channel == 0 || channel == 3) {
|
|
addLink(graph, separateOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
|
|
}
|
|
else {
|
|
KeyingBlurOperation *blurOperation = new KeyingBlurOperation();
|
|
|
|
blurOperation->setSize(size);
|
|
|
|
addLink(graph, separateOperation->getOutputSocket(0), blurOperation->getInputSocket(0));
|
|
addLink(graph, blurOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
|
|
graph->addOperation(blurOperation);
|
|
}
|
|
}
|
|
|
|
ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation();
|
|
convertYCCToRGBOperation->setMode(0); /* ITU 601 */
|
|
addLink(graph, combineOperation->getOutputSocket(0), convertYCCToRGBOperation->getInputSocket(0));
|
|
graph->addOperation(convertYCCToRGBOperation);
|
|
|
|
*originalImage = convertRGBToYCCOperation->getInputSocket(0)->getConnection()->getFromSocket();
|
|
|
|
return convertYCCToRGBOperation->getOutputSocket(0);
|
|
}
|
|
|
|
OutputSocket *KeyingNode::setupPostBlur(ExecutionSystem *graph, OutputSocket *postBLurInput, int size)
|
|
{
|
|
KeyingBlurOperation *blurOperation = new KeyingBlurOperation();
|
|
|
|
blurOperation->setSize(size);
|
|
|
|
addLink(graph, postBLurInput, blurOperation->getInputSocket(0));
|
|
|
|
graph->addOperation(blurOperation);
|
|
|
|
return blurOperation->getOutputSocket();
|
|
}
|
|
|
|
OutputSocket *KeyingNode::setupDilateErode(ExecutionSystem *graph, OutputSocket *dilateErodeInput, int distance)
|
|
{
|
|
DilateStepOperation *dilateErodeOperation;
|
|
|
|
if (distance > 0) {
|
|
dilateErodeOperation = new DilateStepOperation();
|
|
dilateErodeOperation->setIterations(distance);
|
|
}
|
|
else {
|
|
dilateErodeOperation = new ErodeStepOperation();
|
|
dilateErodeOperation->setIterations(-distance);
|
|
}
|
|
|
|
addLink(graph, dilateErodeInput, dilateErodeOperation->getInputSocket(0));
|
|
|
|
graph->addOperation(dilateErodeOperation);
|
|
|
|
return dilateErodeOperation->getOutputSocket(0);
|
|
}
|
|
|
|
OutputSocket *KeyingNode::setupDespill(ExecutionSystem *graph, OutputSocket *despillInput, InputSocket *inputScreen, float factor)
|
|
{
|
|
KeyingDespillOperation *despillOperation = new KeyingDespillOperation();
|
|
|
|
despillOperation->setDespillFactor(factor);
|
|
|
|
addLink(graph, despillInput, despillOperation->getInputSocket(0));
|
|
inputScreen->relinkConnections(despillOperation->getInputSocket(1), 1, graph);
|
|
|
|
graph->addOperation(despillOperation);
|
|
|
|
return despillOperation->getOutputSocket(0);
|
|
}
|
|
|
|
OutputSocket *KeyingNode::setupClip(ExecutionSystem *graph, OutputSocket *clipInput, int kernelRadius, float kernelTolerance,
|
|
float clipBlack, float clipWhite, bool edgeMatte)
|
|
{
|
|
KeyingClipOperation *clipOperation = new KeyingClipOperation();
|
|
|
|
clipOperation->setKernelRadius(kernelRadius);
|
|
clipOperation->setKernelTolerance(kernelTolerance);
|
|
|
|
clipOperation->setClipBlack(clipBlack);
|
|
clipOperation->setClipWhite(clipWhite);
|
|
clipOperation->setIsEdgeMatte(edgeMatte);
|
|
|
|
addLink(graph, clipInput, clipOperation->getInputSocket(0));
|
|
|
|
graph->addOperation(clipOperation);
|
|
|
|
return clipOperation->getOutputSocket(0);
|
|
}
|
|
|
|
void KeyingNode::convertToOperations(ExecutionSystem *graph, CompositorContext *context)
|
|
{
|
|
InputSocket *inputImage = this->getInputSocket(0);
|
|
InputSocket *inputScreen = this->getInputSocket(1);
|
|
OutputSocket *outputImage = this->getOutputSocket(0);
|
|
OutputSocket *outputMatte = this->getOutputSocket(1);
|
|
OutputSocket *outputEdges = this->getOutputSocket(2);
|
|
OutputSocket *postprocessedMatte, *postprocessedImage, *originalImage, *edgesMatte;
|
|
|
|
bNode *editorNode = this->getbNode();
|
|
NodeKeyingData *keying_data = (NodeKeyingData *) editorNode->storage;
|
|
|
|
/* keying operation */
|
|
KeyingOperation *keyingOperation = new KeyingOperation();
|
|
|
|
keyingOperation->setScreenBalance(keying_data->screen_balance);
|
|
|
|
inputScreen->relinkConnections(keyingOperation->getInputSocket(1), 1, graph);
|
|
|
|
if (keying_data->blur_pre) {
|
|
/* chroma preblur operation for input of keying operation */
|
|
OutputSocket *preBluredImage = setupPreBlur(graph, inputImage, keying_data->blur_pre, &originalImage);
|
|
addLink(graph, preBluredImage, keyingOperation->getInputSocket(0));
|
|
}
|
|
else {
|
|
inputImage->relinkConnections(keyingOperation->getInputSocket(0), 0, graph);
|
|
originalImage = keyingOperation->getInputSocket(0)->getConnection()->getFromSocket();
|
|
}
|
|
|
|
graph->addOperation(keyingOperation);
|
|
|
|
postprocessedMatte = keyingOperation->getOutputSocket();
|
|
|
|
if (keying_data->clip_black > 0.0f || keying_data->clip_white< 1.0f) {
|
|
postprocessedMatte = setupClip(graph, postprocessedMatte,
|
|
keying_data->edge_kernel_radius, keying_data->edge_kernel_tolerance,
|
|
keying_data->clip_black, keying_data->clip_white, false);
|
|
}
|
|
|
|
edgesMatte = setupClip(graph, postprocessedMatte,
|
|
keying_data->edge_kernel_radius, keying_data->edge_kernel_tolerance,
|
|
keying_data->clip_black, keying_data->clip_white, true);
|
|
|
|
/* apply blur on matte if needed */
|
|
if (keying_data->blur_post)
|
|
postprocessedMatte = setupPostBlur(graph, postprocessedMatte, keying_data->blur_post);
|
|
|
|
/* matte dilate/erode */
|
|
if (keying_data->dilate_distance != 0) {
|
|
postprocessedMatte = setupDilateErode(graph, postprocessedMatte, keying_data->dilate_distance);
|
|
}
|
|
|
|
/* set alpha channel to output image */
|
|
SetAlphaOperation *alphaOperation = new SetAlphaOperation();
|
|
addLink(graph, originalImage, alphaOperation->getInputSocket(0));
|
|
addLink(graph, postprocessedMatte, alphaOperation->getInputSocket(1));
|
|
|
|
postprocessedImage = alphaOperation->getOutputSocket();
|
|
|
|
/* despill output image */
|
|
if (keying_data->despill_factor > 0.0f) {
|
|
postprocessedImage = setupDespill(graph, postprocessedImage, inputScreen, keying_data->despill_factor);
|
|
}
|
|
|
|
/* connect result to output sockets */
|
|
outputImage->relinkConnections(postprocessedImage);
|
|
outputMatte->relinkConnections(postprocessedMatte);
|
|
outputEdges->relinkConnections(edgesMatte);
|
|
|
|
graph->addOperation(alphaOperation);
|
|
}
|