Kaying node from tomato branch

Merge keying node from tomato branch into trunk.

It was considered stable and helpful by Mango team and it'll help
studio pipeline, because nodes would stop disappearing when opening
files in current trunk.

Full information about keying nodes could be found there:
http://wiki.blender.org/index.php/User:Nazg-gul/Keying
This commit is contained in:
Sergey Sharybin 2012-06-14 12:19:13 +00:00
commit 1f19bacf8e
22 changed files with 1134 additions and 19 deletions

@ -659,6 +659,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMaterial *mat);
#define CMP_NODE_OUTPUT_MULTI_FILE__DEPRECATED 267 /* DEPRECATED multi file node has been merged into regular CMP_NODE_OUTPUT_FILE */
#define CMP_NODE_MASK 268
#define CMP_NODE_KEYINGSCREEN 269
#define CMP_NODE_KEYING 270
#define CMP_NODE_GLARE 301
#define CMP_NODE_TONEMAP 302

@ -1901,6 +1901,7 @@ static void registerCompositNodes(bNodeTreeType *ttype)
register_node_type_cmp_luma_matte(ttype);
register_node_type_cmp_doubleedgemask(ttype);
register_node_type_cmp_keyingscreen(ttype);
register_node_type_cmp_keying(ttype);
register_node_type_cmp_translate(ttype);
register_node_type_cmp_rotate(ttype);

@ -330,6 +330,17 @@ set(SRC
operations/COM_KeyingScreenOperation.cpp
operations/COM_KeyingScreenOperation.h
nodes/COM_KeyingNode.cpp
nodes/COM_KeyingNode.h
operations/COM_KeyingOperation.cpp
operations/COM_KeyingOperation.h
operations/COM_KeyingBlurOperation.cpp
operations/COM_KeyingBlurOperation.h
operations/COM_KeyingDespillOperation.cpp
operations/COM_KeyingDespillOperation.h
operations/COM_KeyingClipOperation.cpp
operations/COM_KeyingClipOperation.h
operations/COM_ColorSpillOperation.cpp
operations/COM_ColorSpillOperation.h
operations/COM_RenderLayersBaseProg.cpp

@ -113,6 +113,7 @@
#include "COM_CropNode.h"
#include "COM_MaskNode.h"
#include "COM_KeyingScreenNode.h"
#include "COM_KeyingNode.h"
Node *Converter::convert(bNode *bNode)
{
@ -355,6 +356,9 @@ case CMP_NODE_OUTPUT_FILE:
case CMP_NODE_KEYINGSCREEN:
node = new KeyingScreenNode(bNode);
break;
case CMP_NODE_KEYING:
node = new KeyingNode(bNode);
break;
/* not inplemented yet */
default:
node = new MuteNode(bNode);

@ -0,0 +1,227 @@
/*
* 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, OutputSocket *inputScreen, float factor)
{
KeyingDespillOperation *despillOperation = new KeyingDespillOperation();
despillOperation->setDespillFactor(factor);
addLink(graph, despillInput, despillOperation->getInputSocket(0));
addLink(graph, inputScreen, despillOperation->getInputSocket(1));
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,
keyingOperation->getInputSocket(1)->getConnection()->getFromSocket(),
keying_data->despill_factor);
}
/* connect result to output sockets */
outputImage->relinkConnections(postprocessedImage);
outputMatte->relinkConnections(postprocessedMatte);
outputEdges->relinkConnections(edgesMatte);
graph->addOperation(alphaOperation);
}

@ -0,0 +1,42 @@
/*
* 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_Node.h"
/**
* @brief KeyingNode
* @ingroup Node
*/
class KeyingNode : public Node {
protected:
OutputSocket *setupPreBlur(ExecutionSystem *graph, InputSocket *inputImage, int size, OutputSocket **originalImage);
OutputSocket *setupPostBlur(ExecutionSystem *graph, OutputSocket *postBLurInput, int size);
OutputSocket *setupDilateErode(ExecutionSystem *graph, OutputSocket *dilateErodeInput, int distance);
OutputSocket *setupDespill(ExecutionSystem *graph, OutputSocket *despillInput, OutputSocket *inputSrceen, float factor);
OutputSocket *setupClip(ExecutionSystem *graph, OutputSocket *clipInput, int kernelRadius, float kernelTolerance,
float clipBlack, float clipWhite, bool edgeMatte);
public:
KeyingNode(bNode *editorNode);
void convertToOperations(ExecutionSystem *graph, CompositorContext *context);
};

@ -0,0 +1,88 @@
/*
* 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_KeyingBlurOperation.h"
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
KeyingBlurOperation::KeyingBlurOperation(): NodeOperation()
{
this->addInputSocket(COM_DT_VALUE);
this->addOutputSocket(COM_DT_VALUE);
this->size = 0.0f;
this->setComplex(true);
}
void *KeyingBlurOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
{
void *buffer = getInputOperation(0)->initializeTileData(rect, memoryBuffers);
return buffer;
}
void KeyingBlurOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
{
MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
float *buffer = inputBuffer->getBuffer();
int bufferWidth = inputBuffer->getWidth();
int bufferHeight = inputBuffer->getHeight();
int i, j, count = 0;
float average = 0.0f;
for (i = -this->size + 1; i < this->size; i++) {
for (j = -this->size + 1; j < this->size; j++) {
int cx = x + j, cy = y + i;
if (cx >= 0 && cx < bufferWidth && cy >= 0 && cy < bufferHeight) {
int bufferIndex = (cy * bufferWidth + cx) * 4;
average += buffer[bufferIndex];
count++;
}
}
}
average /= (float) count;
color[0] = average;
}
bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
rcti newInput;
newInput.xmin = 0;
newInput.ymin = 0;
newInput.xmax = this->getWidth();
newInput.ymax = this->getHeight();
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}

@ -0,0 +1,48 @@
/*
* 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
*/
#ifndef _COM_KeyingBlurOperation_h
#define _COM_KeyingBlurOperation_h
#include "COM_NodeOperation.h"
/**
* Class with implementation of bluring for keying node
*/
class KeyingBlurOperation : public NodeOperation {
protected:
int size;
public:
KeyingBlurOperation();
void setSize(float value) {this->size = value;}
void *initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers);
void executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data);
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output);
};
#endif

@ -0,0 +1,123 @@
/*
* 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_KeyingClipOperation.h"
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
KeyingClipOperation::KeyingClipOperation(): NodeOperation()
{
this->addInputSocket(COM_DT_VALUE);
this->addOutputSocket(COM_DT_VALUE);
this->kernelRadius = 3;
this->kernelTolerance = 0.1f;
this->clipBlack = 0.0f;
this->clipWhite = 1.0f;
this->isEdgeMatte = false;
this->setComplex(true);
}
void *KeyingClipOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
{
void *buffer = getInputOperation(0)->initializeTileData(rect, memoryBuffers);
return buffer;
}
void KeyingClipOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
{
const int delta = this->kernelRadius;
const float tolerance = this->kernelTolerance;
MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
float *buffer = inputBuffer->getBuffer();
int bufferWidth = inputBuffer->getWidth();
int bufferHeight = inputBuffer->getHeight();
int i, j, count = 0, totalCount = 0;
float value = buffer[(y * bufferWidth + x) * 4];
bool ok = false;
for (i = -delta + 1; i < delta; i++) {
for (j = -delta + 1; j < delta; j++) {
int cx = x + j, cy = y + i;
if (i == 0 && j == 0)
continue;
if (cx >= 0 && cx < bufferWidth && cy >= 0 && cy < bufferHeight) {
int bufferIndex = (cy * bufferWidth + cx) * 4;
float currentValue = buffer[bufferIndex];
if (fabsf(currentValue - value) < tolerance) {
count++;
}
totalCount++;
}
}
}
ok = count >= (float) totalCount * 0.9f;
if (this->isEdgeMatte) {
if (ok)
color[0] = 0.0f;
else
color[0] = 1.0f;
}
else {
color[0] = value;
if (ok) {
if (color[0] < this->clipBlack)
color[0] = 0.0f;
else if (color[0] >= this->clipWhite)
color[0] = 1.0f;
else
color[0] = (color[0] - this->clipBlack) / (this->clipWhite - this->clipBlack);
}
}
}
bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
rcti newInput;
newInput.xmin = 0;
newInput.ymin = 0;
newInput.xmax = this->getWidth();
newInput.ymax = this->getHeight();
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}

@ -0,0 +1,59 @@
/*
* 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
*/
#ifndef _COM_KeyingClipOperation_h
#define _COM_KeyingClipOperation_h
#include "COM_NodeOperation.h"
/**
* Class with implementation of black/white clipping for keying node
*/
class KeyingClipOperation : public NodeOperation {
protected:
float clipBlack;
float clipWhite;
int kernelRadius;
float kernelTolerance;
bool isEdgeMatte;
public:
KeyingClipOperation();
void setClipBlack(float value) {this->clipBlack = value;}
void setClipWhite(float value) {this->clipWhite = value;}
void setKernelRadius(int value) {this->kernelRadius = value;}
void setKernelTolerance(float value) {this->kernelTolerance = value;}
void setIsEdgeMatte(bool value) {this->isEdgeMatte = value;}
void *initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers);
void executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data);
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output);
};
#endif

@ -0,0 +1,89 @@
/*
* 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_KeyingDespillOperation.h"
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
static int get_pixel_primary_channel(float *pixel)
{
float max_value = MAX3(pixel[0], pixel[1], pixel[2]);
if (max_value == pixel[0])
return 0;
else if (max_value == pixel[1])
return 1;
return 2;
}
KeyingDespillOperation::KeyingDespillOperation(): NodeOperation()
{
this->addInputSocket(COM_DT_COLOR);
this->addInputSocket(COM_DT_COLOR);
this->addOutputSocket(COM_DT_COLOR);
this->despillFactor = 0.5f;
this->pixelReader = NULL;
this->screenReader = NULL;
}
void KeyingDespillOperation::initExecution()
{
this->pixelReader = this->getInputSocketReader(0);
this->screenReader = this->getInputSocketReader(1);
}
void KeyingDespillOperation::deinitExecution()
{
this->pixelReader = NULL;
this->screenReader = NULL;
}
void KeyingDespillOperation::executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[])
{
float pixelColor[4];
float screenColor[4];
this->pixelReader->read(pixelColor, x, y, sampler, inputBuffers);
this->screenReader->read(screenColor, x, y, sampler, inputBuffers);
int screen_primary_channel = get_pixel_primary_channel(screenColor);
float average_value, amount;
average_value = (pixelColor[0] + pixelColor[1] + pixelColor[2] - pixelColor[screen_primary_channel]) / 2.0f;
amount = pixelColor[screen_primary_channel] - average_value;
color[0] = pixelColor[0];
color[1] = pixelColor[1];
color[2] = pixelColor[2];
color[3] = pixelColor[3];
if (this->despillFactor * amount > 0) {
color[screen_primary_channel] = pixelColor[screen_primary_channel] - this->despillFactor * amount;
}
}

@ -0,0 +1,49 @@
/*
* 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
*/
#ifndef _COM_KeyingDespillOperation_h
#define _COM_KeyingDespillOperation_h
#include "COM_NodeOperation.h"
/**
* Class with implementation of keying despill node
*/
class KeyingDespillOperation : public NodeOperation {
protected:
SocketReader *pixelReader;
SocketReader *screenReader;
float despillFactor;
public:
KeyingDespillOperation();
void initExecution();
void deinitExecution();
void setDespillFactor(float value) {this->despillFactor = value;}
void executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[]);
};
#endif

@ -0,0 +1,115 @@
/*
* 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_KeyingOperation.h"
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
static int get_pixel_primary_channel(float pixelColor[4])
{
float max_value = MAX3(pixelColor[0], pixelColor[1], pixelColor[2]);
if (max_value == pixelColor[0])
return 0;
else if (max_value == pixelColor[1])
return 1;
return 2;
}
static float get_pixel_saturation(float pixelColor[4], float screen_balance, int primary_channel)
{
int other_1 = (primary_channel + 1) % 3;
int other_2 = (primary_channel + 2) % 3;
float min = MIN2(pixelColor[other_1], pixelColor[other_2]);
float max = MAX2(pixelColor[other_1], pixelColor[other_2]);
float val = screen_balance * min + (1.0f - screen_balance) * max;
return (pixelColor[primary_channel] - val) * fabsf(1.0f - val);
}
KeyingOperation::KeyingOperation(): NodeOperation()
{
this->addInputSocket(COM_DT_COLOR);
this->addInputSocket(COM_DT_COLOR);
this->addOutputSocket(COM_DT_VALUE);
this->screenBalance = 0.5f;
this->pixelReader = NULL;
this->screenReader = NULL;
}
void KeyingOperation::initExecution()
{
this->pixelReader = this->getInputSocketReader(0);
this->screenReader = this->getInputSocketReader(1);
}
void KeyingOperation::deinitExecution()
{
this->pixelReader = NULL;
this->screenReader = NULL;
}
void KeyingOperation::executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[])
{
float pixelColor[4];
float screenColor[4];
this->pixelReader->read(pixelColor, x, y, sampler, inputBuffers);
this->screenReader->read(screenColor, x, y, sampler, inputBuffers);
int primary_channel = get_pixel_primary_channel(screenColor);
float saturation = get_pixel_saturation(pixelColor, this->screenBalance, primary_channel);
float screen_saturation = get_pixel_saturation(screenColor, this->screenBalance, primary_channel);
if (saturation < 0) {
color[0] = 1.0f;
}
else if (saturation >= screen_saturation) {
color[0] = 0.0f;
}
else {
float distance = 1.0f - saturation / screen_saturation;
color[0] = distance;
}
}
bool KeyingOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
rcti newInput;
newInput.xmin = 0;
newInput.ymin = 0;
newInput.xmax = this->getWidth();
newInput.ymax = this->getHeight();
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}

@ -0,0 +1,56 @@
/*
* 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
*/
#ifndef _COM_KeyingOperation_h
#define _COM_KeyingOperation_h
#include <string.h>
#include "COM_NodeOperation.h"
#include "BLI_listbase.h"
/**
* Class with implementation of keying node
*/
class KeyingOperation : public NodeOperation {
protected:
SocketReader *pixelReader;
SocketReader *screenReader;
float screenBalance;
public:
KeyingOperation();
void initExecution();
void deinitExecution();
void setScreenBalance(float value) {this->screenBalance = value;}
void executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[]);
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output);
};
#endif

@ -59,6 +59,23 @@
#include "clip_intern.h" // own include
#if 0
static int ED_space_clip_dopesheet_poll(bContext *C)
{
SpaceClip *sc = CTX_wm_space_clip(C);
if (sc && sc->clip) {
if (sc->view == SC_VIEW_DOPESHEET) {
ARegion *ar = CTX_wm_region(C);
return ar->regiontype == RGN_TYPE_PREVIEW;
}
}
return FALSE;
}
#endif
/********************** select channel operator *********************/
static int dopesheet_select_channel_poll(bContext *C)

@ -2443,6 +2443,21 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
}
}
static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
/* bNode *node= ptr->data; */ /* UNUSED */
uiItemR(layout, ptr, "blur_pre", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "screen_balance", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "despill_factor", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "edge_kernel_radius", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "edge_kernel_tolerance", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "clip_black", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "clip_white", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "dilate_distance", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "blur_post", 0, NULL, ICON_NONE);
}
/* only once called */
static void node_composit_set_butfunc(bNodeType *ntype)
{
@ -2638,6 +2653,9 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_KEYINGSCREEN:
ntype->uifunc = node_composit_buts_keyingscreen;
break;
case CMP_NODE_KEYING:
ntype->uifunc = node_composit_buts_keying;
break;
default:
ntype->uifunc = NULL;
}

@ -632,6 +632,16 @@ typedef struct NodeKeyingScreenData {
char tracking_object[64];
} NodeKeyingScreenData;
typedef struct NodeKeyingData {
float screen_balance;
float despill_factor;
int edge_kernel_radius;
float edge_kernel_tolerance;
float clip_black, clip_white;
int dilate_distance;
int blur_pre, blur_post;
} NodeKeyingData;
/* frame node flags */
#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */
#define NODE_FRAME_RESIZEABLE 2 /* test flag, if frame can be resized by user */

@ -3101,25 +3101,6 @@ static void def_cmp_mask(StructRNA *srna)
RNA_def_property_ui_text(prop, "Mask", "");
}
static void def_cmp_keyingscreen(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "clip", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
RNA_def_struct_sdna_from(srna, "NodeKeyingScreenData", "storage");
prop = RNA_def_property(srna, "tracking_object", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "tracking_object");
RNA_def_property_ui_text(prop, "Tracking Object", "");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
}
static void dev_cmd_transform(StructRNA *srna)
{
PropertyRNA *prop;
@ -3526,6 +3507,85 @@ static void def_cmp_viewer(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_keyingscreen(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "clip", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
RNA_def_struct_sdna_from(srna, "NodeKeyingScreenData", "storage");
prop = RNA_def_property(srna, "tracking_object", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "tracking_object");
RNA_def_property_ui_text(prop, "Tracking Object", "");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
}
static void def_cmp_keying(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeKeyingData", "storage");
prop = RNA_def_property(srna, "screen_balance", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "screen_balance");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Screen Balance", "Balance between two non-primary channels primary channel is comparing against");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "despill_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "despill_factor");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Despill", "Factor of despilling screen color from image");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "clip_black", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "clip_black");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Clip Black", "Value of on-scaled matte pixel which considers as fully background pixel");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "clip_white", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "clip_white");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Clip White", "Value of on-scaled matte pixel which considers as fully foreground pixel");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "blur_pre", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "blur_pre");
RNA_def_property_range(prop, 0, 2048);
RNA_def_property_ui_text(prop, "Pre Blur", "Chroma pre-blur size which applies before running keyer");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "blur_post", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "blur_post");
RNA_def_property_range(prop, 0, 2048);
RNA_def_property_ui_text(prop, "Post Blur", "Matte blur size which applies after clipping and dilate/eroding");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "dilate_distance", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "dilate_distance");
RNA_def_property_range(prop, -100, 100);
RNA_def_property_ui_text(prop, "Dilate/Erode", "Matte dilate/erode side");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "edge_kernel_radius", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "edge_kernel_radius");
RNA_def_property_range(prop, -100, 100);
RNA_def_property_ui_text(prop, "Edge Kernel Radius", "Radius of kernel used to detect whether pixel belongs to edge");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "edge_kernel_tolerance", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "edge_kernel_tolerance");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Edge Kernel Tolerance", "Tolerance to pixels inside kernel which are treating as belonging to the same plane");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
}
/* -- Texture Nodes --------------------------------------------------------- */

@ -169,6 +169,7 @@ DefNode( CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITC
DefNode( CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "ColorCorrection", "" )
DefNode( CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" )
DefNode( CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "KeyingScreen", "" )
DefNode( CompositorNode, CMP_NODE_KEYING, def_cmp_keying, "KEYING", Keying, "Keying", "" )
DefNode( TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )

@ -77,6 +77,7 @@ set(SRC
composite/nodes/node_composite_image.c
composite/nodes/node_composite_invert.c
composite/nodes/node_composite_keyingscreen.c
composite/nodes/node_composite_keying.c
composite/nodes/node_composite_lensdist.c
composite/nodes/node_composite_levels.c
composite/nodes/node_composite_lummaMatte.c

@ -106,6 +106,7 @@ void register_node_type_cmp_color_spill(struct bNodeTreeType *ttype);
void register_node_type_cmp_luma_matte(struct bNodeTreeType *ttype);
void register_node_type_cmp_doubleedgemask(struct bNodeTreeType *ttype);
void register_node_type_cmp_keyingscreen(struct bNodeTreeType *ttype);
void register_node_type_cmp_keying(struct bNodeTreeType *ttype);
void register_node_type_cmp_translate(struct bNodeTreeType *ttype);
void register_node_type_cmp_rotate(struct bNodeTreeType *ttype);

@ -0,0 +1,94 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* The Original Code is Copyright (C) 2011 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/nodes/composite/nodes/node_composite_keying.c
* \ingroup cmpnodes
*/
#include "BLF_translation.h"
#include "DNA_movieclip_types.h"
#include "BKE_movieclip.h"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_color.h"
#include "BLI_voronoi.h"
#include "node_composite_util.h"
/* **************** Translate ******************** */
static bNodeSocketTemplate cmp_node_keying_in[] = {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 1, "Key Color", 1.0f, 1.0f, 1.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketTemplate cmp_node_keying_out[] = {
{ SOCK_RGBA, 0, "Image"},
{ SOCK_FLOAT, 0, "Matte"},
{ SOCK_FLOAT, 0, "Edges"},
{ -1, 0, "" }
};
static void exec(void *UNUSED(data), bNode *UNUSED(node), bNodeStack **UNUSED(in), bNodeStack **UNUSED(out))
{
}
static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode* node, bNodeTemplate *UNUSED(ntemp))
{
NodeKeyingData *data;
data = MEM_callocN(sizeof(NodeKeyingData), "node keying data");
data->screen_balance = 0.5f;
data->despill_factor = 1.0f;
data->edge_kernel_radius = 3;
data->edge_kernel_tolerance = 0.1f;
data->clip_white = 1.0f;
data->clip_black = 0.0f;
data->clip_white = 1.0f;
node->storage = data;
}
void register_node_type_cmp_keying(bNodeTreeType *ttype)
{
static bNodeType ntype;
node_type_base(ttype, &ntype, CMP_NODE_KEYING, "Keying", NODE_CLASS_MATTE, NODE_OPTIONS);
node_type_socket_templates(&ntype, cmp_node_keying_in, cmp_node_keying_out);
node_type_size(&ntype, 140, 100, 320);
node_type_init(&ntype, node_composit_init_keying);
node_type_storage(&ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
node_type_exec(&ntype, exec);
nodeRegisterType(ttype, &ntype);
}