17 #include <babl/babl.h>
32 init_effect_details();
38 color(color), fuzz(fuzz), halo(halo), method(method)
41 init_effect_details();
45 void ChromaKey::init_effect_details()
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).";
102 std::shared_ptr<openshot::Frame>
ChromaKey::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
104 int threshold = fuzz.
GetInt(frame_number);
105 int halothreshold = halo.
GetInt(frame_number);
106 long mask_R = color.
red.
GetInt(frame_number);
108 long mask_B = color.
blue.
GetInt(frame_number);
111 std::shared_ptr<QImage> image = frame->GetImage();
113 int width = image->width();
114 int height = image->height();
116 int pixelcount = width * height;
122 static bool need_init =
true;
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;
141 format = babl_format(
"HSV float");
142 rowwidth = width *
sizeof(float) * 3;
147 format = babl_format(
"HSL float");
148 rowwidth = width *
sizeof(float) * 3;
154 format = babl_format(
"CIE LCH(ab) float");
155 rowwidth = width *
sizeof(float) * 3;
159 format = babl_format(
"CIE Lab u8");
160 rowwidth = width * 3;
164 format = babl_format(
"Y'CbCr u8");
165 rowwidth = width * 3;
172 pixelbuf.resize(rowwidth * height);
174 if (rgb && format && (fish = babl_fish(rgb, format)) != 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();
186 babl_process(fish, mask_in, &mask, 1);
193 babl_process(fish, image->bits(), pixelbuf.data(), pixelcount);
197 unsigned char *rowdata = pixelbuf.data();
199 for (
int y = 0; y < height; ++y, rowdata += rowwidth)
200 babl_process(fish, image->scanLine(y), rowdata, width);
206 for (
int y = 0; y < height; ++y)
208 unsigned char *pixel = image->scanLine(y);
210 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
212 float tmp = fabs(pf[0] - mask.f[0]);
217 if (tmp <= threshold)
219 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
221 else if (tmp <= threshold + halothreshold)
223 float alphamult = (tmp - threshold) / halothreshold;
225 pixel[0] *= alphamult;
226 pixel[1] *= alphamult;
227 pixel[2] *= alphamult;
228 pixel[3] *= alphamult;
236 for (
int y = 0; y < height; ++y)
238 unsigned char *pixel = image->scanLine(y);
240 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
242 float tmp = fabs(pf[1] - mask.f[1]) * 255;
244 if (tmp <= threshold)
246 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
248 else if (tmp <= threshold + halothreshold)
250 float alphamult = (tmp - threshold) / halothreshold;
252 pixel[0] *= alphamult;
253 pixel[1] *= alphamult;
254 pixel[2] *= alphamult;
255 pixel[3] *= alphamult;
263 for (
int y = 0; y < height; ++y)
265 unsigned char *pixel = image->scanLine(y);
267 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
269 float tmp = fabs(pf[2] - mask.f[2]) * 255;
271 if (tmp <= threshold)
273 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
275 else if (tmp <= threshold + halothreshold)
277 float alphamult = (tmp - threshold) / halothreshold;
279 pixel[0] *= alphamult;
280 pixel[1] *= alphamult;
281 pixel[2] *= alphamult;
282 pixel[3] *= alphamult;
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));
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();
307 #pragma omp parallel for schedule(static)
308 for (
int y = 0; y < height; ++y)
310 unsigned char *pixel = img_bits + (y * img_bpl);
311 const unsigned char *pc_row = pixelbuf.data() + (y * rowwidth);
313 for (
int x = 0; x < width; ++x, pixel += 4)
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;
320 if (dist_sq <= threshold_sq)
322 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
324 else if (halothreshold > 0 && dist_sq <= halo_upper_sq)
326 float tmp = sqrt_lut[dist_sq];
327 float alphamult = (tmp - threshold) * inv_halo;
329 pixel[0] *= alphamult;
330 pixel[1] *= alphamult;
331 pixel[2] *= alphamult;
332 pixel[3] *= alphamult;
340 for (
int y = 0; y < height; ++y)
342 unsigned char *pixel = image->scanLine(y);
344 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
346 float tmp = fabs(pf[0] - mask.f[0]);
348 if (tmp <= threshold)
350 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
352 else if (tmp <= threshold + halothreshold)
354 float alphamult = (tmp - threshold) / halothreshold;
356 pixel[0] *= alphamult;
357 pixel[1] *= alphamult;
358 pixel[2] *= alphamult;
359 pixel[3] *= alphamult;
366 for (
int y = 0; y < height; ++y)
368 unsigned char *pixel = image->scanLine(y);
370 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
372 float tmp = fabs(pf[1] - mask.f[1]);
374 if (tmp <= threshold)
376 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
378 else if (tmp <= threshold + halothreshold)
380 float alphamult = (tmp - threshold) / halothreshold;
382 pixel[0] *= alphamult;
383 pixel[1] *= alphamult;
384 pixel[2] *= alphamult;
385 pixel[3] *= alphamult;
392 for (
int y = 0; y < height; ++y)
394 unsigned char *pixel = image->scanLine(y);
396 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
405 float tmp = fabs(pf[2] - mask.f[2]);
409 if (tmp <= threshold)
410 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
420 float pi = 4 * std::atan(1);
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);
427 for (
int y = 0; y < height; ++y)
429 unsigned char *pixel = image->scanLine(y);
431 for (
int x = 0; x < width; ++x, pixel += 4, pc += 3)
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);
438 float delta_L_prime = L2 - L1;
439 float L_bar = (L1 + L2) / 2;
440 float C_bar = (C1 + C2) / 2;
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;
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;
451 float h1_prime = std::atan2(b1, a1_prime) * 180 / pi;
452 float h2_prime = std::atan2(b2, a2_prime) * 180 / pi;
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);
457 if (delta_h_prime < -180)
459 delta_h_prime += 360;
460 if (H_prime_bar < 180)
465 else if (delta_h_prime > 180)
467 delta_h_prime -= 360;
468 if (H_prime_bar < 180)
474 float delta_H_prime = 2 * std::sqrt(C1_prime * C2_prime) * std::sin(delta_h_prime * pi / 360);
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);
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)
492 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
494 else if (delta_E <= threshold + halothreshold)
496 float alphamult = (delta_E - threshold) / halothreshold;
498 pixel[0] *= alphamult;
499 pixel[1] *= alphamult;
500 pixel[2] *= alphamult;
501 pixel[3] *= alphamult;
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)));
521 static const std::array<float, 256> inv_alpha = [] {
522 std::array<float, 256> lut{};
524 for (
int i = 1; i < 256; ++i)
525 lut[i] = 255.0f /
static_cast<float>(i);
531 unsigned char *img_bits = image->bits();
532 const int img_bpl = image->bytesPerLine();
534 #pragma omp parallel for schedule(static)
535 for (
int y = 0; y < height; ++y)
537 unsigned char * pixel = img_bits + (y * img_bpl);
538 for (
int x = 0; x < width; ++x, pixel += 4)
540 const int A = pixel[3];
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);
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];
566 if (distance <= threshold)
567 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
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));
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();
584 #pragma omp parallel for schedule(static)
585 for (
int y = 0; y < height; ++y)
587 unsigned char * pixel = img_bits + (y * img_bpl);
588 for (
int x = 0; x < width; ++x, pixel += 4)
590 const int A = pixel[3];
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);
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);
614 const int distance = dist_lut[dist_sq];
615 if (distance <= threshold) {
616 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
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);
650 root[
"keymethod"] = method;
666 catch (
const std::exception& e)
669 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
680 if (!root[
"color"].isNull())
682 if (!root[
"fuzz"].isNull())
684 if (!root[
"halo"].isNull())
686 if (!root[
"keymethod"].isNull())
697 root[
"color"] =
add_property_json(
"Key Color", 0.0,
"color",
"", &color.
red, 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);
718 return root.toStyledString();