OpenShot Library | libopenshot 0.6.0
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 <array>
20#include <cstdint>
21#include <vector>
22#include <cmath>
23
24using namespace openshot;
25
27ChromaKey::ChromaKey() : fuzz(20.0), halo(10.0), method(CHROMAKEY_BASIC_SOFT) {
28 // Init default color
29 color = Color();
30
31 // Init effect properties
32 init_effect_details();
33}
34
35// Standard constructor, which takes an openshot::Color object, a 'fuzz' factor,
36// an optional halo distance and an optional keying method.
38 color(color), fuzz(fuzz), halo(halo), method(method)
39{
40 // Init effect properties
41 init_effect_details();
42}
43
44// Init effect settings
45void ChromaKey::init_effect_details()
46{
49
51 info.class_name = "ChromaKey";
52 info.name = "Chroma Key (Greenscreen)";
53 info.description = "Replaces the color (or chroma) of the frame with transparency (i.e. keys out the color).";
54 info.has_audio = false;
55 info.has_video = true;
56}
57
58// This method is required for all derived classes of EffectBase, and returns a
59// modified openshot::Frame object
60//
61// Because a frame's QImage is always in Format_RGB8888_Premultiplied, we do not
62// need to muck about with QRgb and its helper functions, qRed, QGreen, QBlue and
63// qAlpha, and indeed doing so will get wrong results on almost every platform
64// when we operate on the pixel buffers instead of calling the pixel methods in
65// QImage. QRgb is always in the form 0xAARRGGBB, but treating the pixel buffer
66// as an array of QRgb will yield values of the form 0xAABBGGRR on little endian
67// systems and 0xRRGGBBAA on big endian systems.
68//
69// We need to operate on the pixel buffers here because doing this all pixel by
70// pixel is be horribly slow, especially with keying methods other than basic.
71// The babl conversion functions are very slow if iterating over pixels and every
72// effort should be made to do babl conversions in blocks of as many pixels as
73// can be done at once.
74//
75// The default keying method tries to ascertain the original pixel color by
76// dividing the red, green and blue channels by the alpha (and multiplying by
77// 255). The other methods do not do this for several reasons:
78//
79// 1. The calculation will not necessarily return the original value, because
80// the premultiplication of alpha using unsigned 8 bit integers loses
81// accuracy at the least significant bit. Even an alpha of 0xfe means that
82// we are left with only 255 values to start with and cannot regain the full
83// 256 values that could have been in the input. At an alpha of 0x7f the
84// entire least significant bit has been lost, and at an alpho of 0x3f the
85// two entire least significant bits have been lost. Chroma keying is very
86// sensitive to these losses of precision so if the alpha has been applied
87// already at anything other than 0xff and 0x00, we are already screwed and
88// this calculation will not help.
89//
90// 2. The calculation used for the default method seems to be wrong anyway as
91// it always rounds down rather than to the nearest whole number.
92//
93// 3. As mentioned above, babl conversion functions are very slow when iterating
94// over individual pixels. We would have to convert the entire input buffer
95// in one go to avoid this. It just does not seem worth it given the loss
96// of accuracy we already have.
97//
98// 4. It is difficult to see how it could make sense to apply chroma keying
99// after other non-chroma-key effects. The purpose is to remove an unwanted
100// background in the input stream, rather than removing some calculated
101// value that is the output of another effect.
102std::shared_ptr<openshot::Frame> ChromaKey::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
103{
104 int threshold = fuzz.GetInt(frame_number);
105 int halothreshold = halo.GetInt(frame_number);
106 long mask_R = color.red.GetInt(frame_number);
107 long mask_G = color.green.GetInt(frame_number);
108 long mask_B = color.blue.GetInt(frame_number);
109
110 // Get source image
111 std::shared_ptr<QImage> image = frame->GetImage();
112
113 int width = image->width();
114 int height = image->height();
115
116 int pixelcount = width * height;
117
118#if USE_BABL
119 if (method != CHROMAKEY_BASIC && method != CHROMAKEY_BASIC_SOFT
120 && method <= CHROMAKEY_LAST_METHOD)
121 {
122 static bool need_init = true;
123
124 if (need_init)
125 {
126 babl_init();
127 need_init = false;
128 }
129
130 Babl const *rgb = babl_format("R'G'B'A u8");
131 Babl const *format = 0;
132 Babl const *fish = 0;
133 std::vector<unsigned char> pixelbuf;
134 int rowwidth = 0;
135
136 switch(method)
137 {
138 case CHROMAKEY_HSVL_H:
139 case CHROMAKEY_HSV_S:
140 case CHROMAKEY_HSV_V:
141 format = babl_format("HSV float");
142 rowwidth = width * sizeof(float) * 3;
143 break;
144
145 case CHROMAKEY_HSL_S:
146 case CHROMAKEY_HSL_L:
147 format = babl_format("HSL float");
148 rowwidth = width * sizeof(float) * 3;
149 break;
150
154 format = babl_format("CIE LCH(ab) float");
155 rowwidth = width * sizeof(float) * 3;
156 break;
157
159 format = babl_format("CIE Lab u8");
160 rowwidth = width * 3;
161 break;
162
163 case CHROMAKEY_YCBCR:
164 format = babl_format("Y'CbCr u8");
165 rowwidth = width * 3;
166 break;
167
168 case CHROMAKEY_BASIC:
169 break;
170 }
171
172 pixelbuf.resize(rowwidth * height);
173
174 if (rgb && format && (fish = babl_fish(rgb, format)) != 0)
175 {
176 int idx = 0;
177 unsigned char mask_in[4];
178 union { float f[4]; unsigned char u[4]; } mask;
179 float const *pf = (float *) pixelbuf.data();
180 unsigned char const *pc = pixelbuf.data();
181
182 mask_in[0] = mask_R;
183 mask_in[1] = mask_G;
184 mask_in[2] = mask_B;
185 mask_in[3] = 255;
186 babl_process(fish, mask_in, &mask, 1);
187
188 if (0) //image->bytesPerLine() == width * 4)
189 {
190 // Because babl_process is expensive to call, but efficient
191 // with long sequences of pixels, attempt to convert the
192 // entire buffer at once if we can
193 babl_process(fish, image->bits(), pixelbuf.data(), pixelcount);
194 }
195 else
196 {
197 unsigned char *rowdata = pixelbuf.data();
198
199 for (int y = 0; y < height; ++y, rowdata += rowwidth)
200 babl_process(fish, image->scanLine(y), rowdata, width);
201 }
202
203 switch(method)
204 {
205 case CHROMAKEY_HSVL_H:
206 for (int y = 0; y < height; ++y)
207 {
208 unsigned char *pixel = image->scanLine(y);
209
210 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
211 {
212 float tmp = fabs(pf[0] - mask.f[0]);
213
214 if (tmp > 0.5)
215 tmp = 1.0 - tmp;
216 tmp *= 500;
217 if (tmp <= threshold)
218 {
219 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
220 }
221 else if (tmp <= threshold + halothreshold)
222 {
223 float alphamult = (tmp - threshold) / halothreshold;
224
225 pixel[0] *= alphamult;
226 pixel[1] *= alphamult;
227 pixel[2] *= alphamult;
228 pixel[3] *= alphamult;
229 }
230 }
231 }
232 break;
233
234 case CHROMAKEY_HSV_S:
235 case CHROMAKEY_HSL_S:
236 for (int y = 0; y < height; ++y)
237 {
238 unsigned char *pixel = image->scanLine(y);
239
240 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
241 {
242 float tmp = fabs(pf[1] - mask.f[1]) * 255;
243
244 if (tmp <= threshold)
245 {
246 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
247 }
248 else if (tmp <= threshold + halothreshold)
249 {
250 float alphamult = (tmp - threshold) / halothreshold;
251
252 pixel[0] *= alphamult;
253 pixel[1] *= alphamult;
254 pixel[2] *= alphamult;
255 pixel[3] *= alphamult;
256 }
257 }
258 }
259 break;
260
261 case CHROMAKEY_HSV_V:
262 case CHROMAKEY_HSL_L:
263 for (int y = 0; y < height; ++y)
264 {
265 unsigned char *pixel = image->scanLine(y);
266
267 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
268 {
269 float tmp = fabs(pf[2] - mask.f[2]) * 255;
270
271 if (tmp <= threshold)
272 {
273 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
274 }
275 else if (tmp <= threshold + halothreshold)
276 {
277 float alphamult = (tmp - threshold) / halothreshold;
278
279 pixel[0] *= alphamult;
280 pixel[1] *= alphamult;
281 pixel[2] *= alphamult;
282 pixel[3] *= alphamult;
283 }
284 }
285 }
286 break;
287
288 case CHROMAKEY_YCBCR:
289 {
290 // sqrt(db^2 + dr^2), with db/dr in [-255, 255]
291 static const std::array<float, 130051> sqrt_lut = [] {
292 std::array<float, 130051> lut{};
293 for (int i = 0; i <= 130050; ++i)
294 lut[i] = std::sqrt(static_cast<float>(i));
295 return lut;
296 }();
297
298 const int threshold_sq = threshold * threshold;
299 const int halo_upper = threshold + halothreshold;
300 const int halo_upper_sq = halo_upper * halo_upper;
301 const float inv_halo = (halothreshold > 0) ? (1.0f / halothreshold) : 0.0f;
302 const unsigned char key_cb = mask.u[1];
303 const unsigned char key_cr = mask.u[2];
304 unsigned char *img_bits = image->bits();
305 const int img_bpl = image->bytesPerLine();
306
307 #pragma omp parallel for schedule(static)
308 for (int y = 0; y < height; ++y)
309 {
310 unsigned char *pixel = img_bits + (y * img_bpl);
311 const unsigned char *pc_row = pixelbuf.data() + (y * rowwidth);
312
313 for (int x = 0; x < width; ++x, pixel += 4)
314 {
315 const unsigned char *pc_px = pc_row + (x * 3);
316 int db = static_cast<int>(pc_px[1]) - key_cb;
317 int dr = static_cast<int>(pc_px[2]) - key_cr;
318 int dist_sq = db * db + dr * dr;
319
320 if (dist_sq <= threshold_sq)
321 {
322 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
323 }
324 else if (halothreshold > 0 && dist_sq <= halo_upper_sq)
325 {
326 float tmp = sqrt_lut[dist_sq];
327 float alphamult = (tmp - threshold) * inv_halo;
328
329 pixel[0] *= alphamult;
330 pixel[1] *= alphamult;
331 pixel[2] *= alphamult;
332 pixel[3] *= alphamult;
333 }
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[0] - mask.f[0]);
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 float tmp = fabs(pf[1] - mask.f[1]);
373
374 if (tmp <= threshold)
375 {
376 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
377 }
378 else if (tmp <= threshold + halothreshold)
379 {
380 float alphamult = (tmp - threshold) / halothreshold;
381
382 pixel[0] *= alphamult;
383 pixel[1] *= alphamult;
384 pixel[2] *= alphamult;
385 pixel[3] *= alphamult;
386 }
387 }
388 }
389 break;
390
392 for (int y = 0; y < height; ++y)
393 {
394 unsigned char *pixel = image->scanLine(y);
395
396 for (int x = 0; x < width; ++x, pixel += 4, pf += 3)
397 {
398 // Hues in LCH(ab) are an angle on a color wheel.
399 // We are tring to find the angular distance
400 // between the two angles. It can never be more
401 // than 180 degrees - if it is, there is a closer
402 // angle that can be calculated by going in the
403 // other diretion, which can be found by
404 // subtracting the angle we have from 360.
405 float tmp = fabs(pf[2] - mask.f[2]);
406
407 if (tmp > 180.0)
408 tmp = 360.0 - tmp;
409 if (tmp <= threshold)
410 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
411 }
412 }
413 break;
414
416 {
417 float KL = 1.0;
418 float KC = 1.0;
419 float KH = 1.0;
420 float pi = 4 * std::atan(1);
421
422 float L1 = ((float) mask.u[0]) / 2.55;
423 float a1 = mask.u[1] - 127;
424 float b1 = mask.u[2] - 127;
425 float C1 = std::sqrt(a1 * a1 + b1 * b1);
426
427 for (int y = 0; y < height; ++y)
428 {
429 unsigned char *pixel = image->scanLine(y);
430
431 for (int x = 0; x < width; ++x, pixel += 4, pc += 3)
432 {
433 float L2 = ((float) pc[0]) / 2.55;
434 int a2 = pc[1] - 127;
435 int b2 = pc[2] - 127;
436 float C2 = std::sqrt(a2 * a2 + b2 * b2);
437
438 float delta_L_prime = L2 - L1;
439 float L_bar = (L1 + L2) / 2;
440 float C_bar = (C1 + C2) / 2;
441
442 float a_prime_multiplier = 1 + 0.5 * (1 - std::sqrt(C_bar / (C_bar + 25)));
443 float a1_prime = a1 * a_prime_multiplier;
444 float a2_prime = a2 * a_prime_multiplier;
445
446 float C1_prime = std::sqrt(a1_prime * a1_prime + b1 * b1);
447 float C2_prime = std::sqrt(a2_prime * a2_prime + b2 * b2);
448 float C_prime_bar = (C1_prime + C2_prime) / 2;
449 float delta_C_prime = C2_prime - C1_prime;
450
451 float h1_prime = std::atan2(b1, a1_prime) * 180 / pi;
452 float h2_prime = std::atan2(b2, a2_prime) * 180 / pi;
453
454 float delta_h_prime = h2_prime - h1_prime;
455 double H_prime_bar = (C1_prime != 0 && C2_prime != 0) ? (h1_prime + h2_prime) / 2 : (h1_prime + h2_prime);
456
457 if (delta_h_prime < -180)
458 {
459 delta_h_prime += 360;
460 if (H_prime_bar < 180)
461 H_prime_bar += 180;
462 else
463 H_prime_bar -= 180;
464 }
465 else if (delta_h_prime > 180)
466 {
467 delta_h_prime -= 360;
468 if (H_prime_bar < 180)
469 H_prime_bar += 180;
470 else
471 H_prime_bar -= 180;
472 }
473
474 float delta_H_prime = 2 * std::sqrt(C1_prime * C2_prime) * std::sin(delta_h_prime * pi / 360);
475
476 float T = 1
477 - 0.17 * std::cos((H_prime_bar - 30) * pi / 180)
478 + 0.24 * std::cos(H_prime_bar * pi / 90)
479 + 0.32 * std::cos((3 * H_prime_bar + 6) * pi / 180)
480 - 0.20 * std::cos((4 * H_prime_bar - 64) * pi / 180);
481
482 float SL = 1 + 0.015 * std::pow(L_bar - 50, 2) / std::sqrt(20 + std::pow(L_bar - 50, 2));
483 float SC = 1 + 0.045 * C_prime_bar;
484 float SH = 1 + 0.015 * C_prime_bar * T;
485 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)));
486 float delta_E = std::sqrt(std::pow(delta_L_prime / KL / SL, 2)
487 + std::pow(delta_C_prime / KC / SC, 2)
488 + std::pow(delta_h_prime / KH / SH, 2)
489 + RT * delta_C_prime / KC / SC * delta_H_prime / KH / SH);
490 if (delta_E <= threshold)
491 {
492 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
493 }
494 else if (delta_E <= threshold + halothreshold)
495 {
496 float alphamult = (delta_E - threshold) / halothreshold;
497
498 pixel[0] *= alphamult;
499 pixel[1] *= alphamult;
500 pixel[2] *= alphamult;
501 pixel[3] *= alphamult;
502 }
503 }
504 }
505 }
506 break;
507 }
508
509 return frame;
510 }
511 }
512#endif
513
514 // Metric upper bound for Color::GetDistance internal term is below 589825.
515 static const std::array<uint16_t, 589826> dist_lut = [] {
516 std::array<uint16_t, 589826> lut{};
517 for (int i = 0; i <= 589825; ++i)
518 lut[i] = static_cast<uint16_t>(std::sqrt(static_cast<float>(i)));
519 return lut;
520 }();
521 static const std::array<float, 256> inv_alpha = [] {
522 std::array<float, 256> lut{};
523 lut[0] = 0.0f;
524 for (int i = 1; i < 256; ++i)
525 lut[i] = 255.0f / static_cast<float>(i);
526 return lut;
527 }();
528
529 if (method == CHROMAKEY_BASIC) {
530 // Legacy BASIC behavior (hard threshold, no halo), optimized with OpenMP.
531 unsigned char *img_bits = image->bits();
532 const int img_bpl = image->bytesPerLine();
533
534 #pragma omp parallel for schedule(static)
535 for (int y = 0; y < height; ++y)
536 {
537 unsigned char * pixel = img_bits + (y * img_bpl);
538 for (int x = 0; x < width; ++x, pixel += 4)
539 {
540 const int A = pixel[3];
541 if (A <= 0)
542 continue;
543
544 // Preserve legacy 8-bit conversion behavior by casting to unsigned char.
545 unsigned char R = 0;
546 unsigned char G = 0;
547 unsigned char B = 0;
548 if (A == 255) {
549 R = pixel[0];
550 G = pixel[1];
551 B = pixel[2];
552 } else {
553 const float inv_a = inv_alpha[A];
554 R = static_cast<unsigned char>(pixel[0] * inv_a);
555 G = static_cast<unsigned char>(pixel[1] * inv_a);
556 B = static_cast<unsigned char>(pixel[2] * inv_a);
557 }
558
559 const int rmean = (static_cast<int>(R) + static_cast<int>(mask_R)) / 2;
560 const int r = static_cast<int>(R) - static_cast<int>(mask_R);
561 const int g = static_cast<int>(G) - static_cast<int>(mask_G);
562 const int b = static_cast<int>(B) - static_cast<int>(mask_B);
563 const int dist_sq = (((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8);
564 const int distance = dist_lut[dist_sq];
565
566 if (distance <= threshold)
567 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
568 }
569 }
570 } else {
571 // BASIC_SOFT: same metric, optional halo feathering, optimized + OpenMP.
572 static const std::array<float, 589826> dist_f_lut = [] {
573 std::array<float, 589826> lut{};
574 for (int i = 0; i <= 589825; ++i)
575 lut[i] = std::sqrt(static_cast<float>(i));
576 return lut;
577 }();
578 const int halo_upper = threshold + halothreshold;
579 const int halo_upper_sq = halo_upper * halo_upper;
580 const float inv_halo = (halothreshold > 0) ? (1.0f / halothreshold) : 0.0f;
581 unsigned char *img_bits = image->bits();
582 const int img_bpl = image->bytesPerLine();
583
584 #pragma omp parallel for schedule(static)
585 for (int y = 0; y < height; ++y)
586 {
587 unsigned char * pixel = img_bits + (y * img_bpl);
588 for (int x = 0; x < width; ++x, pixel += 4)
589 {
590 const int A = pixel[3];
591 if (A <= 0)
592 continue;
593
594 int R = 0;
595 int G = 0;
596 int B = 0;
597 if (A == 255) {
598 R = pixel[0];
599 G = pixel[1];
600 B = pixel[2];
601 } else {
602 const float inv_a = inv_alpha[A];
603 R = std::clamp(static_cast<int>(pixel[0] * inv_a), 0, 255);
604 G = std::clamp(static_cast<int>(pixel[1] * inv_a), 0, 255);
605 B = std::clamp(static_cast<int>(pixel[2] * inv_a), 0, 255);
606 }
607
608 const int rmean = (R + static_cast<int>(mask_R)) / 2;
609 const int r = R - static_cast<int>(mask_R);
610 const int g = G - static_cast<int>(mask_G);
611 const int b = B - static_cast<int>(mask_B);
612 const int dist_sq = (((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8);
613
614 const int distance = dist_lut[dist_sq];
615 if (distance <= threshold) {
616 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
617 }
618 else if (halothreshold > 0 && dist_sq <= halo_upper_sq) {
619 const float distance_f = dist_f_lut[dist_sq];
620 const float alphamult = (distance_f - threshold) * inv_halo;
621 pixel[0] = static_cast<unsigned char>(pixel[0] * alphamult);
622 pixel[1] = static_cast<unsigned char>(pixel[1] * alphamult);
623 pixel[2] = static_cast<unsigned char>(pixel[2] * alphamult);
624 pixel[3] = static_cast<unsigned char>(pixel[3] * alphamult);
625 }
626 }
627 }
628 }
629
630 // return the modified frame
631 return frame;
632}
633
634// Generate JSON string of this object
635std::string ChromaKey::Json() const {
636
637 // Return formatted string
638 return JsonValue().toStyledString();
639}
640
641// Generate Json::Value for this object
642Json::Value ChromaKey::JsonValue() const {
643
644 // Create root json object
645 Json::Value root = EffectBase::JsonValue(); // get parent properties
646 root["type"] = info.class_name;
647 root["color"] = color.JsonValue();
648 root["fuzz"] = fuzz.JsonValue();
649 root["halo"] = halo.JsonValue();
650 root["keymethod"] = method;
651
652 // return JsonValue
653 return root;
654}
655
656// Load JSON string into this object
657void ChromaKey::SetJson(const std::string value) {
658
659 // Parse JSON string into JSON objects
660 try
661 {
662 const Json::Value root = openshot::stringToJson(value);
663 // Set all values that match
664 SetJsonValue(root);
665 }
666 catch (const std::exception& e)
667 {
668 // Error parsing JSON (or missing keys)
669 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
670 }
671}
672
673// Load Json::Value into this object
674void ChromaKey::SetJsonValue(const Json::Value root) {
675
676 // Set parent data
678
679 // Set data from Json (if key is found)
680 if (!root["color"].isNull())
681 color.SetJsonValue(root["color"]);
682 if (!root["fuzz"].isNull())
683 fuzz.SetJsonValue(root["fuzz"]);
684 if (!root["halo"].isNull())
685 halo.SetJsonValue(root["halo"]);
686 if (!root["keymethod"].isNull())
687 method = (ChromaKeyMethod) root["keymethod"].asInt();
688}
689
690// Get all properties for a specific frame
691std::string ChromaKey::PropertiesJSON(int64_t requested_frame) const {
692
693 // Generate JSON properties list
694 Json::Value root = BasePropertiesJSON(requested_frame);
695
696 // Keyframes
697 root["color"] = add_property_json("Key Color", 0.0, "color", "", &color.red, 0, 255, false, requested_frame);
698 root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", &color.red, 0, 255, false, requested_frame);
699 root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", &color.blue, 0, 255, false, requested_frame);
700 root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", &color.green, 0, 255, false, requested_frame);
701 root["fuzz"] = add_property_json("Threshold", fuzz.GetValue(requested_frame), "float", "", &fuzz, 0, 125, false, requested_frame);
702 root["halo"] = add_property_json("Halo", halo.GetValue(requested_frame), "float", "", &halo, 0, 125, false, requested_frame);
703 root["keymethod"] = add_property_json("Key Method", method, "int", "", NULL, 0, CHROMAKEY_LAST_METHOD, false, requested_frame);
704 root["keymethod"]["choices"].append(add_property_choice_json("Basic keying", 0, method));
705 root["keymethod"]["choices"].append(add_property_choice_json("Basic keying (soft)", CHROMAKEY_BASIC_SOFT, method));
706 root["keymethod"]["choices"].append(add_property_choice_json("HSV/HSL hue", 1, method));
707 root["keymethod"]["choices"].append(add_property_choice_json("HSV saturation", 2, method));
708 root["keymethod"]["choices"].append(add_property_choice_json("HSL saturation", 3, method));
709 root["keymethod"]["choices"].append(add_property_choice_json("HSV value", 4, method));
710 root["keymethod"]["choices"].append(add_property_choice_json("HSL luminance", 5, method));
711 root["keymethod"]["choices"].append(add_property_choice_json("LCH luminosity", 6, method));
712 root["keymethod"]["choices"].append(add_property_choice_json("LCH chroma", 7, method));
713 root["keymethod"]["choices"].append(add_property_choice_json("LCH hue", 8, method));
714 root["keymethod"]["choices"].append(add_property_choice_json("CIE Distance", 9, method));
715 root["keymethod"]["choices"].append(add_property_choice_json("Cb,Cr vector", 10, method));
716
717 // Return formatted string
718 return root.toStyledString();
719}
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:27
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:88
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
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:110
Exception for invalid JSON.
Definition Exceptions.h:224
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:159
@ CHROMAKEY_CIE_LCH_C
Difference between CIE LCH(ab) chromas.
Definition Enums.h:167
@ CHROMAKEY_HSL_L
Difference between HSL luminances.
Definition Enums.h:165
@ CHROMAKEY_HSL_S
Difference between HSL saturations.
Definition Enums.h:163
@ CHROMAKEY_HSV_V
Difference between HSV values.
Definition Enums.h:164
@ CHROMAKEY_YCBCR
YCbCr vector difference of CbCr.
Definition Enums.h:170
@ CHROMAKEY_BASIC
Length of difference between RGB vectors.
Definition Enums.h:160
@ CHROMAKEY_CIE_LCH_L
Difference between CIE LCH(ab) luminousities.
Definition Enums.h:166
@ CHROMAKEY_CIE_LCH_H
Difference between CIE LCH(ab) hues.
Definition Enums.h:168
@ CHROMAKEY_HSVL_H
Difference between HSV/HSL hues.
Definition Enums.h:161
@ CHROMAKEY_BASIC_SOFT
BASIC metric + optional halo feathering.
Definition Enums.h:171
@ CHROMAKEY_CIE_DISTANCE
CIEDE2000 perceptual difference.
Definition Enums.h:169
@ CHROMAKEY_HSV_S
Difference between HSV saturations.
Definition Enums.h:162
@ CHROMAKEY_LAST_METHOD
Definition Enums.h:172
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:43
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:44
std::string class_name
The class name of the effect.
Definition EffectBase.h:39
std::string name
The name of the effect.
Definition EffectBase.h:40
std::string description
The description of this effect and what it does.
Definition EffectBase.h:41