Blender  V2.93
COM_KeyingNode.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2012, Blender Foundation.
17  */
18 
19 #include "COM_KeyingNode.h"
20 
21 #include "COM_ExecutionSystem.h"
22 
26 #include "COM_KeyingOperation.h"
27 
28 #include "COM_MathBaseOperation.h"
29 
30 #include "COM_ConvertOperation.h"
31 #include "COM_SetValueOperation.h"
32 
34 
36 
39 
40 namespace blender::compositor {
41 
42 KeyingNode::KeyingNode(bNode *editorNode) : Node(editorNode)
43 {
44  /* pass */
45 }
46 
48  NodeInput *inputImage,
49  int size) const
50 {
51  ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation();
52  convertRGBToYCCOperation->setMode(BLI_YCC_ITU_BT709);
53  converter.addOperation(convertRGBToYCCOperation);
54 
55  converter.mapInputSocket(inputImage, convertRGBToYCCOperation->getInputSocket(0));
56 
57  CombineChannelsOperation *combineOperation = new CombineChannelsOperation();
58  converter.addOperation(combineOperation);
59 
60  for (int channel = 0; channel < 4; channel++) {
61  SeparateChannelOperation *separateOperation = new SeparateChannelOperation();
62  separateOperation->setChannel(channel);
63  converter.addOperation(separateOperation);
64 
65  converter.addLink(convertRGBToYCCOperation->getOutputSocket(0),
66  separateOperation->getInputSocket(0));
67 
68  if (ELEM(channel, 0, 3)) {
69  converter.addLink(separateOperation->getOutputSocket(0),
70  combineOperation->getInputSocket(channel));
71  }
72  else {
73  KeyingBlurOperation *blurXOperation = new KeyingBlurOperation();
74  blurXOperation->setSize(size);
76  converter.addOperation(blurXOperation);
77 
78  KeyingBlurOperation *blurYOperation = new KeyingBlurOperation();
79  blurYOperation->setSize(size);
81  converter.addOperation(blurYOperation);
82 
83  converter.addLink(separateOperation->getOutputSocket(), blurXOperation->getInputSocket(0));
84  converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0));
85  converter.addLink(blurYOperation->getOutputSocket(0),
86  combineOperation->getInputSocket(channel));
87  }
88  }
89 
90  ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation();
91  convertYCCToRGBOperation->setMode(BLI_YCC_ITU_BT709);
92  converter.addOperation(convertYCCToRGBOperation);
93 
94  converter.addLink(combineOperation->getOutputSocket(0),
95  convertYCCToRGBOperation->getInputSocket(0));
96 
97  return convertYCCToRGBOperation->getOutputSocket(0);
98 }
99 
101  NodeOperationOutput *postBlurInput,
102  int size) const
103 {
104  KeyingBlurOperation *blurXOperation = new KeyingBlurOperation();
105  blurXOperation->setSize(size);
106  blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X);
107  converter.addOperation(blurXOperation);
108 
109  KeyingBlurOperation *blurYOperation = new KeyingBlurOperation();
110  blurYOperation->setSize(size);
111  blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y);
112  converter.addOperation(blurYOperation);
113 
114  converter.addLink(postBlurInput, blurXOperation->getInputSocket(0));
115  converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0));
116 
117  return blurYOperation->getOutputSocket();
118 }
119 
121  NodeOperationOutput *dilateErodeInput,
122  int distance) const
123 {
124  DilateDistanceOperation *dilateErodeOperation;
125  if (distance > 0) {
126  dilateErodeOperation = new DilateDistanceOperation();
127  dilateErodeOperation->setDistance(distance);
128  }
129  else {
130  dilateErodeOperation = new ErodeDistanceOperation();
131  dilateErodeOperation->setDistance(-distance);
132  }
133  converter.addOperation(dilateErodeOperation);
134 
135  converter.addLink(dilateErodeInput, dilateErodeOperation->getInputSocket(0));
136 
137  return dilateErodeOperation->getOutputSocket(0);
138 }
139 
141  const CompositorContext &context,
142  NodeOperationOutput *featherInput,
143  int falloff,
144  int distance) const
145 {
146  /* this uses a modified gaussian blur function otherwise its far too slow */
147  eCompositorQuality quality = context.getQuality();
148 
149  /* initialize node data */
151  memset(&data, 0, sizeof(NodeBlurData));
152  data.filtertype = R_FILTER_GAUSS;
153  if (distance > 0) {
154  data.sizex = data.sizey = distance;
155  }
156  else {
157  data.sizex = data.sizey = -distance;
158  }
159 
161  operationx->setData(&data);
162  operationx->setQuality(quality);
163  operationx->setSize(1.0f);
164  operationx->setSubtract(distance < 0);
165  operationx->setFalloff(falloff);
166  converter.addOperation(operationx);
167 
169  operationy->setData(&data);
170  operationy->setQuality(quality);
171  operationy->setSize(1.0f);
172  operationy->setSubtract(distance < 0);
173  operationy->setFalloff(falloff);
174  converter.addOperation(operationy);
175 
176  converter.addLink(featherInput, operationx->getInputSocket(0));
177  converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0));
178 
179  return operationy->getOutputSocket();
180 }
181 
183  NodeOperationOutput *despillInput,
184  NodeInput *inputScreen,
185  float factor,
186  float colorBalance) const
187 {
188  KeyingDespillOperation *despillOperation = new KeyingDespillOperation();
189  despillOperation->setDespillFactor(factor);
190  despillOperation->setColorBalance(colorBalance);
191  converter.addOperation(despillOperation);
192 
193  converter.addLink(despillInput, despillOperation->getInputSocket(0));
194  converter.mapInputSocket(inputScreen, despillOperation->getInputSocket(1));
195 
196  return despillOperation->getOutputSocket(0);
197 }
198 
200  NodeOperationOutput *clipInput,
201  int kernelRadius,
202  float kernelTolerance,
203  float clipBlack,
204  float clipWhite,
205  bool edgeMatte) const
206 {
207  KeyingClipOperation *clipOperation = new KeyingClipOperation();
208  clipOperation->setKernelRadius(kernelRadius);
209  clipOperation->setKernelTolerance(kernelTolerance);
210  clipOperation->setClipBlack(clipBlack);
211  clipOperation->setClipWhite(clipWhite);
212  clipOperation->setIsEdgeMatte(edgeMatte);
213  converter.addOperation(clipOperation);
214 
215  converter.addLink(clipInput, clipOperation->getInputSocket(0));
216 
217  return clipOperation->getOutputSocket(0);
218 }
219 
221  const CompositorContext &context) const
222 {
223  bNode *editorNode = this->getbNode();
224  NodeKeyingData *keying_data = (NodeKeyingData *)editorNode->storage;
225 
226  NodeInput *inputImage = this->getInputSocket(0);
227  NodeInput *inputScreen = this->getInputSocket(1);
228  NodeInput *inputGarbageMatte = this->getInputSocket(2);
229  NodeInput *inputCoreMatte = this->getInputSocket(3);
230  NodeOutput *outputImage = this->getOutputSocket(0);
231  NodeOutput *outputMatte = this->getOutputSocket(1);
232  NodeOutput *outputEdges = this->getOutputSocket(2);
233  NodeOperationOutput *postprocessedMatte = nullptr, *postprocessedImage = nullptr,
234  *edgesMatte = nullptr;
235 
236  /* keying operation */
237  KeyingOperation *keyingOperation = new KeyingOperation();
238  keyingOperation->setScreenBalance(keying_data->screen_balance);
239  converter.addOperation(keyingOperation);
240 
241  converter.mapInputSocket(inputScreen, keyingOperation->getInputSocket(1));
242 
243  if (keying_data->blur_pre) {
244  /* Chroma pre-blur operation for input of keying operation. */
245  NodeOperationOutput *preBlurredImage = setupPreBlur(
246  converter, inputImage, keying_data->blur_pre);
247  converter.addLink(preBlurredImage, keyingOperation->getInputSocket(0));
248  }
249  else {
250  converter.mapInputSocket(inputImage, keyingOperation->getInputSocket(0));
251  }
252 
253  postprocessedMatte = keyingOperation->getOutputSocket();
254 
255  /* black / white clipping */
256  if (keying_data->clip_black > 0.0f || keying_data->clip_white < 1.0f) {
257  postprocessedMatte = setupClip(converter,
258  postprocessedMatte,
259  keying_data->edge_kernel_radius,
260  keying_data->edge_kernel_tolerance,
261  keying_data->clip_black,
262  keying_data->clip_white,
263  false);
264  }
265 
266  /* output edge matte */
267  edgesMatte = setupClip(converter,
268  postprocessedMatte,
269  keying_data->edge_kernel_radius,
270  keying_data->edge_kernel_tolerance,
271  keying_data->clip_black,
272  keying_data->clip_white,
273  true);
274 
275  /* apply garbage matte */
276  if (inputGarbageMatte->isLinked()) {
277  SetValueOperation *valueOperation = new SetValueOperation();
278  valueOperation->setValue(1.0f);
279  converter.addOperation(valueOperation);
280 
281  MathSubtractOperation *subtractOperation = new MathSubtractOperation();
282  converter.addOperation(subtractOperation);
283 
284  MathMinimumOperation *minOperation = new MathMinimumOperation();
285  converter.addOperation(minOperation);
286 
287  converter.addLink(valueOperation->getOutputSocket(), subtractOperation->getInputSocket(0));
288  converter.mapInputSocket(inputGarbageMatte, subtractOperation->getInputSocket(1));
289 
290  converter.addLink(subtractOperation->getOutputSocket(), minOperation->getInputSocket(0));
291  converter.addLink(postprocessedMatte, minOperation->getInputSocket(1));
292 
293  postprocessedMatte = minOperation->getOutputSocket();
294  }
295 
296  /* apply core matte */
297  if (inputCoreMatte->isLinked()) {
298  MathMaximumOperation *maxOperation = new MathMaximumOperation();
299  converter.addOperation(maxOperation);
300 
301  converter.mapInputSocket(inputCoreMatte, maxOperation->getInputSocket(0));
302  converter.addLink(postprocessedMatte, maxOperation->getInputSocket(1));
303 
304  postprocessedMatte = maxOperation->getOutputSocket();
305  }
306 
307  /* apply blur on matte if needed */
308  if (keying_data->blur_post) {
309  postprocessedMatte = setupPostBlur(converter, postprocessedMatte, keying_data->blur_post);
310  }
311 
312  /* matte dilate/erode */
313  if (keying_data->dilate_distance != 0) {
314  postprocessedMatte = setupDilateErode(
315  converter, postprocessedMatte, keying_data->dilate_distance);
316  }
317 
318  /* matte feather */
319  if (keying_data->feather_distance != 0) {
320  postprocessedMatte = setupFeather(converter,
321  context,
322  postprocessedMatte,
323  keying_data->feather_falloff,
324  keying_data->feather_distance);
325  }
326 
327  /* set alpha channel to output image */
329  converter.addOperation(alphaOperation);
330 
331  converter.mapInputSocket(inputImage, alphaOperation->getInputSocket(0));
332  converter.addLink(postprocessedMatte, alphaOperation->getInputSocket(1));
333 
334  postprocessedImage = alphaOperation->getOutputSocket();
335 
336  /* despill output image */
337  if (keying_data->despill_factor > 0.0f) {
338  postprocessedImage = setupDespill(converter,
339  postprocessedImage,
340  inputScreen,
341  keying_data->despill_factor,
342  keying_data->despill_balance);
343  }
344 
345  /* connect result to output sockets */
346  converter.mapOutputSocket(outputImage, postprocessedImage);
347  converter.mapOutputSocket(outputMatte, postprocessedMatte);
348 
349  if (edgesMatte) {
350  converter.mapOutputSocket(outputEdges, edgesMatte);
351  }
352 }
353 
354 } // namespace blender::compositor
#define BLI_YCC_ITU_BT709
#define ELEM(...)
#define R_FILTER_GAUSS
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
void setData(const NodeBlurData *data)
Overall context of the compositor.
NodeOperationOutput * setupDespill(NodeConverter &converter, NodeOperationOutput *despillInput, NodeInput *inputScreen, float factor, float colorBalance) const
void convertToOperations(NodeConverter &converter, const CompositorContext &context) const override
convert node to operation
NodeOperationOutput * setupPostBlur(NodeConverter &converter, NodeOperationOutput *postBlurInput, int size) const
NodeOperationOutput * setupClip(NodeConverter &converter, NodeOperationOutput *clipInput, int kernelRadius, float kernelTolerance, float clipBlack, float clipWhite, bool edgeMatte) const
NodeOperationOutput * setupDilateErode(NodeConverter &converter, NodeOperationOutput *dilateErodeInput, int distance) const
NodeOperationOutput * setupPreBlur(NodeConverter &converter, NodeInput *inputImage, int size) const
NodeOperationOutput * setupFeather(NodeConverter &converter, const CompositorContext &context, NodeOperationOutput *featherInput, int falloff, int distance) const
void mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket)
void addOperation(NodeOperation *operation)
void mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket)
void addLink(NodeOperationOutput *from, NodeOperationInput *to)
NodeInput are sockets that can receive data/input.
Definition: COM_Node.h:210
NodeOperationInput * getInputSocket(unsigned int index)
NodeOperationOutput * getOutputSocket(unsigned int index=0)
NodeOutput are sockets that can send data/input.
Definition: COM_Node.h:258
NodeOutput * getOutputSocket(const unsigned int index=0) const
Definition: COM_Node.cc:108
bNode * getbNode() const
get the reference to the SDNA bNode struct
Definition: COM_Node.h:82
NodeInput * getInputSocket(const unsigned int index) const
Definition: COM_Node.cc:113
void setQuality(eCompositorQuality quality)
eCompositorQuality
Possible quality settings.
Definition: COM_Enums.h:32
float edge_kernel_tolerance
void * storage
ccl_device_inline float distance(const float2 &a, const float2 &b)