OpenShot Library | libopenshot 0.3.3
Loading...
Searching...
No Matches
ChromaKey.cpp
Go to the documentation of this file.
1
10// Copyright (c) 2008-2019 OpenShot Studios, LLC
11//
12// SPDX-License-Identifier: LGPL-3.0-or-later
13
14#include "ChromaKey.h"
15#include "Exceptions.h"
16#if USE_BABL
17#include <babl/babl.h>
18#endif
19#include <vector>
20#include <cmath>
21
22using namespace openshot;
23
25ChromaKey::ChromaKey() : fuzz(5.0), halo(0), method(CHROMAKEY_BASIC) {
26 // Init default color
27 color = Color();
28
29 // Init effect properties
30 init_effect_details();
31}
32
33// Standard constructor, which takes an openshot::Color object, a 'fuzz' factor,
34// an optional halo distance and an optional keying method.
36 color(color), fuzz(fuzz), halo(halo), method(method)
37{
38 // Init effect properties
39 init_effect_details();
40}
41
42// Init effect settings
43void ChromaKey::init_effect_details()
44{
47
49 info.class_name = "ChromaKey";
50 info.name = "Chroma Key (Greenscreen)";
51 info.description = "Replaces the color (or chroma) of the frame with transparency (i.e. keys out the color).";
52 info.has_audio = false;
53 info.has_video = true;
54}
55
56// This method is required for all derived classes of EffectBase, and returns a
57// modified openshot::Frame object
58//
59// Because a frame's QImage is always in Format_RGB8888_Premultiplied, we do not
60// need to muck about with QRgb and its helper functions, qRed, QGreen, QBlue and
61// qAlpha, and indeed doing so will get wrong results on almost every platform
62// when we operate on the pixel buffers instead of calling the pixel methods in
63// QImage. QRgb is always in the form 0xAARRGGBB, but treating the pixel buffer
64// as an array of QRgb will yield values of the form 0xAABBGGRR on little endian
65// systems and 0xRRGGBBAA on big endian systems.
66//
67// We need to operate on the pixel buffers here because doing this all pixel by
68// pixel is be horribly slow, especially with keying methods other than basic.
69// The babl conversion functions are very slow if iterating over pixels and every
70// effort should be made to do babl conversions in blocks of as many pixels as
71// can be done at once.
72//
73// The default keying method tries to ascertain the original pixel color by
74// dividing the red, green and blue channels by the alpha (and multiplying by
75// 255). The other methods do not do this for several reasons:
76//
77// 1. The calculation will not necessarily return the original value, because
78// the premultiplication of alpha using unsigned 8 bit integers loses
79// accuracy at the least significant bit. Even an alpha of 0xfe means that
80// we are left with only 255 values to start with and cannot regain the full
81// 256 values that could have been in the input. At an alpha of 0x7f the
82// entire least significant bit has been lost, and at an alpho of 0x3f the
83// two entire least significant bits have been lost. Chroma keying is very
84// sensitive to these losses of precision so if the alpha has been applied
85// already at anything other than 0xff and 0x00, we are already screwed and
86// this calculation will not help.
87//
88// 2. The calculation used for the default method seems to be wrong anyway as
89// it always rounds down rather than to the nearest whole number.
90//
91// 3. As mentioned above, babl conversion functions are very slow when iterating
92// over individual pixels. We would have to convert the entire input buffer
93// in one go to avoid this. It just does not seem worth it given the loss
94// of accuracy we already have.
95//
96// 4. It is difficult to see how it could make sense to apply chroma keying
97// after other non-chroma-key effects. The purpose is to remove an unwanted
98// background in the input stream, rather than removing some calculated
99// value that is the output of another effect.
100std::shared_ptr<openshot::Frame> ChromaKey::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
101{
102 int threshold = fuzz.GetInt(frame_number);
103 int halothreshold = halo.GetInt(frame_number);
104 long mask_R = color.red.GetInt(frame_number);
105 long mask_G = color.green.GetInt(frame_number);
106 long mask_B = color.blue.GetInt(frame_number);
107
108 // Get source image
109 std::shared_ptr<QImage> image = frame->GetImage();
110
111 int width = image->width();
112 int height = image->height();
113
114 int pixelcount = width * height;
115
116#if USE_BABL
117 if (method > CHROMAKEY_BASIC && method <= CHROMAKEY_LAST_METHOD)
118 {
119 static bool need_init = true;
120
121 if (need_init)
122 {
123 babl_init();
124 need_init = false;
125 }
126
127 Babl const *rgb = babl_format("R'G'B'A u8");
128 Babl const *format = 0;
129 Babl const *fish = 0;
130 std::vector<unsigned char> pixelbuf;
131 int rowwidth = 0;
132
133 switch(method)
134 {
135 case CHROMAKEY_HSVL_H:
136 case CHROMAKEY_HSV_S:
137 case CHROMAKEY_HSV_V:
138 format = babl_format("HSV float");
139 rowwidth = width * sizeof(float) * 3;
140 break;
141
142 case CHROMAKEY_HSL_S:
143 case CHROMAKEY_HSL_L:
144 format = babl_format("HSL float");
145 rowwidth = width * sizeof(float) * 3;
146 break;
147
151 format = babl_format("CIE LCH(ab) float");
152 rowwidth = width * sizeof(float) * 3;
153 break;
154
156 format = babl_format("CIE Lab u8");
157 rowwidth = width * 3;
158 break;
159
160 case CHROMAKEY_YCBCR:
161 format = babl_format("Y'CbCr u8");
162 rowwidth = width * 3;
163 break;
164
165 case CHROMAKEY_BASIC:
166 break;
167 }
168
169 pixelbuf.resize(rowwidth * height);
170
171 if (rgb && format && (fish = babl_fish(rgb, format)) != 0)
172 {
173 int idx = 0;
174 unsigned char mask_in[4];
175 union { float f[4]; unsigned char u[4]; } mask;
176 float const *pf = (float *) pixelbuf.data();
177 unsigned char const *pc = pixelbuf.data();
178
179 mask_in[0] = mask_R;
180 mask_in[1] = mask_G;
181 mask_in[2] = mask_B;
182 mask_in[3] = 255;
183 babl_process(fish, mask_in, &mask, 1);
184
185 if (0) //image->bytesPerLine() == width * 4)
186 {
187 // Because babl_process is expensive to call, but efficient
188 // with long sequences of pixels, attempt to convert the
189 // entire buffer at once if we can
190 babl_process(fish, image->bits(), pixelbuf.data(), pixelcount);
191 }
192 else
193 {
194 unsigned char *rowdata = pixelbuf.data();
195
196 for (int y = 0; y < height; ++y, rowdata += rowwidth)
197 babl_process(fish, image->scanLine(y), rowdata, width);
198 }
199
200 switch(method)
201 {
202 case CHROMAKEY_HSVL_H:
203 for (int y = 0; y < height; ++y)
204 {
205 unsigned char *pixel = image->scanLine(y);
206
207 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
208 {
209 float tmp = fabs(pf[0] - mask.f[0]);
210
211 if (tmp > 0.5)
212 tmp = 1.0 - tmp;
213 tmp *= 500;
214 if (tmp <= threshold)
215 {
216 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
217 }
218 else if (tmp <= threshold + halothreshold)
219 {
220 float alphamult = (tmp - threshold) / halothreshold;
221
222 pixel[0] *= alphamult;
223 pixel[1] *= alphamult;
224 pixel[2] *= alphamult;
225 pixel[3] *= alphamult;
226 }
227 }
228 }
229 break;
230
231 case CHROMAKEY_HSV_S:
232 case CHROMAKEY_HSL_S:
233 for (int y = 0; y < height; ++y)
234 {
235 unsigned char *pixel = image->scanLine(y);
236
237 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
238 {
239 float tmp = fabs(pf[1] - mask.f[1]) * 255;
240
241 if (tmp <= threshold)
242 {
243 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
244 }
245 else if (tmp <= threshold + halothreshold)
246 {
247 float alphamult = (tmp - threshold) / halothreshold;
248
249 pixel[0] *= alphamult;
250 pixel[1] *= alphamult;
251 pixel[2] *= alphamult;
252 pixel[3] *= alphamult;
253 }
254 }
255 }
256 break;
257
258 case CHROMAKEY_HSV_V:
259 case CHROMAKEY_HSL_L:
260 for (int y = 0; y < height; ++y)
261 {
262 unsigned char *pixel = image->scanLine(y);
263
264 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
265 {
266 float tmp = fabs(pf[2] - mask.f[2]) * 255;
267
268 if (tmp <= threshold)
269 {
270 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
271 }
272 else if (tmp <= threshold + halothreshold)
273 {
274 float alphamult = (tmp - threshold) / halothreshold;
275
276 pixel[0] *= alphamult;
277 pixel[1] *= alphamult;
278 pixel[2] *= alphamult;
279 pixel[3] *= alphamult;
280 }
281 }
282 }
283 break;
284
285 case CHROMAKEY_YCBCR:
286 for (int y = 0; y < height; ++y)
287 {
288 unsigned char *pixel = image->scanLine(y);
289
290 for (int x = 0; x < width; ++x, pixel += 4, pc += 3)
291 {
292 int db = (int) pc[1] - mask.u[1];
293 int dr = (int) pc[2] - mask.u[2];
294 float tmp = sqrt(db * db + dr * dr);
295
296 if (tmp <= threshold)
297 {
298 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
299 }
300 else if (tmp <= threshold + halothreshold)
301 {
302 float alphamult = (tmp - threshold) / halothreshold;
303
304 pixel[0] *= alphamult;
305 pixel[1] *= alphamult;
306 pixel[2] *= alphamult;
307 pixel[3] *= alphamult;
308 }
309 }
310 }
311 break;
312
314 for (int y = 0; y < height; ++y)
315 {
316 unsigned char *pixel = image->scanLine(y);
317
318 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
319 {
320 float tmp = fabs(pf[0] - mask.f[0]);
321
322 if (tmp <= threshold)
323 {
324 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
325 }
326 else if (tmp <= threshold + halothreshold)
327 {
328 float alphamult = (tmp - threshold) / halothreshold;
329
330 pixel[0] *= alphamult;
331 pixel[1] *= alphamult;
332 pixel[2] *= alphamult;
333 pixel[3] *= alphamult;
334 }
335 }
336 }
337 break;
338
340 for (int y = 0; y < height; ++y)
341 {
342 unsigned char *pixel = image->scanLine(y);
343
344 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
345 {
346 float tmp = fabs(pf[1] - mask.f[1]);
347
348 if (tmp <= threshold)
349 {
350 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
351 }
352 else if (tmp <= threshold + halothreshold)
353 {
354 float alphamult = (tmp - threshold) / halothreshold;
355
356 pixel[0] *= alphamult;
357 pixel[1] *= alphamult;
358 pixel[2] *= alphamult;
359 pixel[3] *= alphamult;
360 }
361 }
362 }
363 break;
364
366 for (int y = 0; y < height; ++y)
367 {
368 unsigned char *pixel = image->scanLine(y);
369
370 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
371 {
372 // Hues in LCH(ab) are an angle on a color wheel.
373 // We are tring to find the angular distance
374 // between the two angles. It can never be more
375 // than 180 degrees - if it is, there is a closer
376 // angle that can be calculated by going in the
377 // other diretion, which can be found by
378 // subtracting the angle we have from 360.
379 float tmp = fabs(pf[2] - mask.f[2]);
380
381 if (tmp > 180.0)
382 tmp = 360.0 - tmp;
383 if (tmp <= threshold)
384 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
385 }
386 }
387 break;
388
390 {
391 float KL = 1.0;
392 float KC = 1.0;
393 float KH = 1.0;
394 float pi = 4 * std::atan(1);
395
396 float L1 = ((float) mask.u[0]) / 2.55;
397 float a1 = mask.u[1] - 127;
398 float b1 = mask.u[2] - 127;
399 float C1 = std::sqrt(a1 * a1 + b1 * b1);
400
401 for (int y = 0; y < height; ++y)
402 {
403 unsigned char *pixel = image->scanLine(y);
404
405 for (int x = 0; x < width; ++x, pixel += 4, pc += 3)
406 {
407 float L2 = ((float) pc[0]) / 2.55;
408 int a2 = pc[1] - 127;
409 int b2 = pc[2] - 127;
410 float C2 = std::sqrt(a2 * a2 + b2 * b2);
411
412 float delta_L_prime = L2 - L1;
413 float L_bar = (L1 + L2) / 2;
414 float C_bar = (C1 + C2) / 2;
415
416 float a_prime_multiplier = 1 + 0.5 * (1 - std::sqrt(C_bar / (C_bar + 25)));
417 float a1_prime = a1 * a_prime_multiplier;
418 float a2_prime = a2 * a_prime_multiplier;
419
420 float C1_prime = std::sqrt(a1_prime * a1_prime + b1 * b1);
421 float C2_prime = std::sqrt(a2_prime * a2_prime + b2 * b2);
422 float C_prime_bar = (C1_prime + C2_prime) / 2;
423 float delta_C_prime = C2_prime - C1_prime;
424
425 float h1_prime = std::atan2(b1, a1_prime) * 180 / pi;
426 float h2_prime = std::atan2(b2, a2_prime) * 180 / pi;
427
428 float delta_h_prime = h2_prime - h1_prime;
429 double H_prime_bar = (C1_prime != 0 && C2_prime != 0) ? (h1_prime + h2_prime) / 2 : (h1_prime + h2_prime);
430
431 if (delta_h_prime < -180)
432 {
433 delta_h_prime += 360;
434 if (H_prime_bar < 180)
435 H_prime_bar += 180;
436 else
437 H_prime_bar -= 180;
438 }
439 else if (delta_h_prime > 180)
440 {
441 delta_h_prime -= 360;
442 if (H_prime_bar < 180)
443 H_prime_bar += 180;
444 else
445 H_prime_bar -= 180;
446 }
447
448 float delta_H_prime = 2 * std::sqrt(C1_prime * C2_prime) * std::sin(delta_h_prime * pi / 360);
449
450 float T = 1
451 - 0.17 * std::cos((H_prime_bar - 30) * pi / 180)
452 + 0.24 * std::cos(H_prime_bar * pi / 90)
453 + 0.32 * std::cos((3 * H_prime_bar + 6) * pi / 180)
454 - 0.20 * std::cos((4 * H_prime_bar - 64) * pi / 180);
455
456 float SL = 1 + 0.015 * std::pow(L_bar - 50, 2) / std::sqrt(20 + std::pow(L_bar - 50, 2));
457 float SC = 1 + 0.045 * C_prime_bar;
458 float SH = 1 + 0.015 * C_prime_bar * T;
459 float RT = -2 * std::sqrt(C_prime_bar / (C_prime_bar + 25)) * std::sin(pi / 3 * std::exp(-std::pow((H_prime_bar - 275) / 25, 2)));
460 float delta_E = std::sqrt(std::pow(delta_L_prime / KL / SL, 2)
461 + std::pow(delta_C_prime / KC / SC, 2)
462 + std::pow(delta_h_prime / KH / SH, 2)
463 + RT * delta_C_prime / KC / SC * delta_H_prime / KH / SH);
464 if (delta_E <= threshold)
465 {
466 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
467 }
468 else if (delta_E <= threshold + halothreshold)
469 {
470 float alphamult = (delta_E - threshold) / halothreshold;
471
472 pixel[0] *= alphamult;
473 pixel[1] *= alphamult;
474 pixel[2] *= alphamult;
475 pixel[3] *= alphamult;
476 }
477 }
478 }
479 }
480 break;
481 }
482
483 return frame;
484 }
485 }
486#endif
487
488 // Loop through pixels
489 for (int y = 0; y < height; ++y)
490 {
491 unsigned char * pixel = image->scanLine(y);
492
493 for (int x = 0; x < width; ++x, pixel += 4)
494 {
495 float A = pixel[3];
496 unsigned char R = (pixel[0] / A) * 255.0;
497 unsigned char G = (pixel[1] / A) * 255.0;
498 unsigned char B = (pixel[2] / A) * 255.0;
499
500 // Get distance between mask color and pixel color
501 long distance = Color::GetDistance((long)R, (long)G, (long)B, mask_R, mask_G, mask_B);
502
503 if (distance <= threshold) {
504 // MATCHED - Make pixel transparent
505 // Due to premultiplied alpha, we must also zero out
506 // the individual color channels (or else artifacts are left behind)
507 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
508 }
509 }
510 }
511
512 // return the modified frame
513 return frame;
514}
515
516// Generate JSON string of this object
517std::string ChromaKey::Json() const {
518
519 // Return formatted string
520 return JsonValue().toStyledString();
521}
522
523// Generate Json::Value for this object
524Json::Value ChromaKey::JsonValue() const {
525
526 // Create root json object
527 Json::Value root = EffectBase::JsonValue(); // get parent properties
528 root["type"] = info.class_name;
529 root["color"] = color.JsonValue();
530 root["fuzz"] = fuzz.JsonValue();
531 root["halo"] = halo.JsonValue();
532 root["keymethod"] = method;
533
534 // return JsonValue
535 return root;
536}
537
538// Load JSON string into this object
539void ChromaKey::SetJson(const std::string value) {
540
541 // Parse JSON string into JSON objects
542 try
543 {
544 const Json::Value root = openshot::stringToJson(value);
545 // Set all values that match
546 SetJsonValue(root);
547 }
548 catch (const std::exception& e)
549 {
550 // Error parsing JSON (or missing keys)
551 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
552 }
553}
554
555// Load Json::Value into this object
556void ChromaKey::SetJsonValue(const Json::Value root) {
557
558 // Set parent data
560
561 // Set data from Json (if key is found)
562 if (!root["color"].isNull())
563 color.SetJsonValue(root["color"]);
564 if (!root["fuzz"].isNull())
565 fuzz.SetJsonValue(root["fuzz"]);
566 if (!root["halo"].isNull())
567 halo.SetJsonValue(root["halo"]);
568 if (!root["keymethod"].isNull())
569 method = (ChromaKeyMethod) root["keymethod"].asInt();
570}
571
572// Get all properties for a specific frame
573std::string ChromaKey::PropertiesJSON(int64_t requested_frame) const {
574
575 // Generate JSON properties list
576 Json::Value root = BasePropertiesJSON(requested_frame);
577
578 // Keyframes
579 root["color"] = add_property_json("Key Color", 0.0, "color", "", &color.red, 0, 255, false, requested_frame);
580 root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", &color.red, 0, 255, false, requested_frame);
581 root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", &color.blue, 0, 255, false, requested_frame);
582 root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", &color.green, 0, 255, false, requested_frame);
583 root["fuzz"] = add_property_json("Threshold", fuzz.GetValue(requested_frame), "float", "", &fuzz, 0, 125, false, requested_frame);
584 root["halo"] = add_property_json("Halo", halo.GetValue(requested_frame), "float", "", &halo, 0, 125, false, requested_frame);
585 root["keymethod"] = add_property_json("Key Method", method, "int", "", NULL, 0, CHROMAKEY_LAST_METHOD, false, requested_frame);
586 root["keymethod"]["choices"].append(add_property_choice_json("Basic keying", 0, method));
587 root["keymethod"]["choices"].append(add_property_choice_json("HSV/HSL hue", 1, method));
588 root["keymethod"]["choices"].append(add_property_choice_json("HSV saturation", 2, method));
589 root["keymethod"]["choices"].append(add_property_choice_json("HSL saturation", 3, method));
590 root["keymethod"]["choices"].append(add_property_choice_json("HSV value", 4, method));
591 root["keymethod"]["choices"].append(add_property_choice_json("HSL luminance", 5, method));
592 root["keymethod"]["choices"].append(add_property_choice_json("LCH luminosity", 6, method));
593 root["keymethod"]["choices"].append(add_property_choice_json("LCH chroma", 7, method));
594 root["keymethod"]["choices"].append(add_property_choice_json("LCH hue", 8, method));
595 root["keymethod"]["choices"].append(add_property_choice_json("CIE Distance", 9, method));
596 root["keymethod"]["choices"].append(add_property_choice_json("Cb,Cr vector", 10, method));
597
598 // Return formatted string
599 return root.toStyledString();
600}
Header file for ChromaKey class.
Header file for all Exception classes.
void SetJson(const std::string value) override
Load JSON string into this object.
ChromaKey()
Blank constructor, useful when using Json to load the effect properties.
Definition ChromaKey.cpp:25
std::string Json() const override
Generate JSON string of this object.
std::string PropertiesJSON(int64_t requested_frame) const override
Json::Value JsonValue() const override
Generate Json::Value for this object.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number) override
This method is required for all derived classes of ClipBase, and returns a new openshot::Frame object...
Definition ChromaKey.h:87
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties)
Definition ClipBase.cpp:132
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const
Generate JSON for a property.
Definition ClipBase.cpp:96
This class represents a color (used on the timeline and clips)
Definition Color.h:27
openshot::Keyframe blue
Curve representing the red value (0 - 255)
Definition Color.h:32
openshot::Keyframe red
Curve representing the red value (0 - 255)
Definition Color.h:30
openshot::Keyframe green
Curve representing the green value (0 - 255)
Definition Color.h:31
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition Color.cpp:117
static long GetDistance(long R1, long G1, long B1, long R2, long G2, long B2)
Get the distance between 2 RGB pairs. (0=identical colors, 10=very close colors, 760=very different c...
Definition Color.cpp:69
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition Color.cpp:86
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
EffectInfoStruct info
Information about the current effect.
Definition EffectBase.h:69
Exception for invalid JSON.
Definition Exceptions.h:218
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition KeyFrame.h:53
int GetInt(int64_t index) const
Get the rounded INT value at a specific index.
Definition KeyFrame.cpp:282
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition KeyFrame.cpp:372
double GetValue(int64_t index) const
Get the value at a specific index.
Definition KeyFrame.cpp:258
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition KeyFrame.cpp:339
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
ChromaKeyMethod
This enumeration determines the algorithm used by the ChromaKey filter.
Definition Enums.h:121
@ CHROMAKEY_CIE_LCH_C
Difference between CIE LCH(ab) chromas.
Definition Enums.h:129
@ CHROMAKEY_HSL_L
Difference between HSL luminances.
Definition Enums.h:127
@ CHROMAKEY_HSL_S
Difference between HSL saturations.
Definition Enums.h:125
@ CHROMAKEY_HSV_V
Difference between HSV values.
Definition Enums.h:126
@ CHROMAKEY_YCBCR
YCbCr vector difference of CbCr.
Definition Enums.h:132
@ CHROMAKEY_BASIC
Length of difference between RGB vectors.
Definition Enums.h:122
@ CHROMAKEY_CIE_LCH_L
Difference between CIE LCH(ab) luminousities.
Definition Enums.h:128
@ CHROMAKEY_CIE_LCH_H
Difference between CIE LCH(ab) hues.
Definition Enums.h:130
@ CHROMAKEY_HSVL_H
Difference between HSV/HSL hues.
Definition Enums.h:123
@ CHROMAKEY_CIE_DISTANCE
CIEDE2000 perceptual difference.
Definition Enums.h:131
@ CHROMAKEY_HSV_S
Difference between HSV saturations.
Definition Enums.h:124
@ CHROMAKEY_LAST_METHOD
Definition Enums.h:133
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
bool has_video
Determines if this effect manipulates the image of a frame.
Definition EffectBase.h:40
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:41
std::string class_name
The class name of the effect.
Definition EffectBase.h:36
std::string name
The name of the effect.
Definition EffectBase.h:37
std::string description
The description of this effect and what it does.
Definition EffectBase.h:38