17 #include <babl/babl.h>
30 init_effect_details();
36 color(color), fuzz(fuzz), halo(halo), method(method)
39 init_effect_details();
43 void ChromaKey::init_effect_details()
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).";
100 std::shared_ptr<openshot::Frame>
ChromaKey::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
102 int threshold = fuzz.
GetInt(frame_number);
103 int halothreshold = halo.
GetInt(frame_number);
104 long mask_R = color.
red.
GetInt(frame_number);
106 long mask_B = color.
blue.
GetInt(frame_number);
109 std::shared_ptr<QImage> image = frame->GetImage();
111 int width = image->width();
112 int height = image->height();
114 int pixelcount = width * height;
119 static bool need_init =
true;
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;
138 format = babl_format(
"HSV float");
139 rowwidth = width *
sizeof(float) * 3;
144 format = babl_format(
"HSL float");
145 rowwidth = width *
sizeof(float) * 3;
151 format = babl_format(
"CIE LCH(ab) float");
152 rowwidth = width *
sizeof(float) * 3;
156 format = babl_format(
"CIE Lab u8");
157 rowwidth = width * 3;
161 format = babl_format(
"Y'CbCr u8");
162 rowwidth = width * 3;
169 pixelbuf.resize(rowwidth * height);
171 if (rgb && format && (fish = babl_fish(rgb, format)) != 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();
183 babl_process(fish, mask_in, &mask, 1);
190 babl_process(fish, image->bits(), pixelbuf.data(), pixelcount);
194 unsigned char *rowdata = pixelbuf.data();
196 for (
int y = 0; y < height; ++y, rowdata += rowwidth)
197 babl_process(fish, image->scanLine(y), rowdata, width);
203 for (
int y = 0; y < height; ++y)
205 unsigned char *pixel = image->scanLine(y);
207 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
209 float tmp = fabs(pf[0] - mask.f[0]);
214 if (tmp <= threshold)
216 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
218 else if (tmp <= threshold + halothreshold)
220 float alphamult = (tmp - threshold) / halothreshold;
222 pixel[0] *= alphamult;
223 pixel[1] *= alphamult;
224 pixel[2] *= alphamult;
225 pixel[3] *= alphamult;
233 for (
int y = 0; y < height; ++y)
235 unsigned char *pixel = image->scanLine(y);
237 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
239 float tmp = fabs(pf[1] - mask.f[1]) * 255;
241 if (tmp <= threshold)
243 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
245 else if (tmp <= threshold + halothreshold)
247 float alphamult = (tmp - threshold) / halothreshold;
249 pixel[0] *= alphamult;
250 pixel[1] *= alphamult;
251 pixel[2] *= alphamult;
252 pixel[3] *= alphamult;
260 for (
int y = 0; y < height; ++y)
262 unsigned char *pixel = image->scanLine(y);
264 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
266 float tmp = fabs(pf[2] - mask.f[2]) * 255;
268 if (tmp <= threshold)
270 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
272 else if (tmp <= threshold + halothreshold)
274 float alphamult = (tmp - threshold) / halothreshold;
276 pixel[0] *= alphamult;
277 pixel[1] *= alphamult;
278 pixel[2] *= alphamult;
279 pixel[3] *= alphamult;
286 for (
int y = 0; y < height; ++y)
288 unsigned char *pixel = image->scanLine(y);
290 for (
int x = 0; x < width; ++x, pixel += 4, pc += 3)
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);
296 if (tmp <= threshold)
298 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
300 else if (tmp <= threshold + halothreshold)
302 float alphamult = (tmp - threshold) / halothreshold;
304 pixel[0] *= alphamult;
305 pixel[1] *= alphamult;
306 pixel[2] *= alphamult;
307 pixel[3] *= alphamult;
314 for (
int y = 0; y < height; ++y)
316 unsigned char *pixel = image->scanLine(y);
318 for (
int x = 0; x < width; ++x, pixel += 4, pf += 3)
320 float tmp = fabs(pf[0] - mask.f[0]);
322 if (tmp <= threshold)
324 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
326 else if (tmp <= threshold + halothreshold)
328 float alphamult = (tmp - threshold) / halothreshold;
330 pixel[0] *= alphamult;
331 pixel[1] *= alphamult;
332 pixel[2] *= alphamult;
333 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[1] - mask.f[1]);
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)
379 float tmp = fabs(pf[2] - mask.f[2]);
383 if (tmp <= threshold)
384 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
394 float pi = 4 * std::atan(1);
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);
401 for (
int y = 0; y < height; ++y)
403 unsigned char *pixel = image->scanLine(y);
405 for (
int x = 0; x < width; ++x, pixel += 4, pc += 3)
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);
412 float delta_L_prime = L2 - L1;
413 float L_bar = (L1 + L2) / 2;
414 float C_bar = (C1 + C2) / 2;
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;
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;
425 float h1_prime = std::atan2(b1, a1_prime) * 180 / pi;
426 float h2_prime = std::atan2(b2, a2_prime) * 180 / pi;
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);
431 if (delta_h_prime < -180)
433 delta_h_prime += 360;
434 if (H_prime_bar < 180)
439 else if (delta_h_prime > 180)
441 delta_h_prime -= 360;
442 if (H_prime_bar < 180)
448 float delta_H_prime = 2 * std::sqrt(C1_prime * C2_prime) * std::sin(delta_h_prime * pi / 360);
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);
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)
466 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
468 else if (delta_E <= threshold + halothreshold)
470 float alphamult = (delta_E - threshold) / halothreshold;
472 pixel[0] *= alphamult;
473 pixel[1] *= alphamult;
474 pixel[2] *= alphamult;
475 pixel[3] *= alphamult;
489 for (
int y = 0; y < height; ++y)
491 unsigned char * pixel = image->scanLine(y);
493 for (
int x = 0; x < width; ++x, pixel += 4)
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;
501 long distance =
Color::GetDistance((
long)R, (
long)G, (
long)B, mask_R, mask_G, mask_B);
503 if (distance <= threshold) {
507 pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
532 root[
"keymethod"] = method;
548 catch (
const std::exception& e)
551 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
562 if (!root[
"color"].isNull())
564 if (!root[
"fuzz"].isNull())
566 if (!root[
"halo"].isNull())
568 if (!root[
"keymethod"].isNull())
577 root[
"id"] =
add_property_json(
"ID", 0.0,
"string",
Id(), NULL, -1, -1,
true, requested_frame);
578 root[
"position"] =
add_property_json(
"Position",
Position(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
580 root[
"start"] =
add_property_json(
"Start",
Start(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
581 root[
"end"] =
add_property_json(
"End",
End(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
582 root[
"duration"] =
add_property_json(
"Duration",
Duration(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
true, requested_frame);
585 root[
"color"] =
add_property_json(
"Key Color", 0.0,
"color",
"", &color.
red, 0, 255,
false, requested_frame);
589 root[
"fuzz"] =
add_property_json(
"Threshold", fuzz.
GetValue(requested_frame),
"float",
"", &fuzz, 0, 125,
false, requested_frame);
590 root[
"halo"] =
add_property_json(
"Halo", halo.
GetValue(requested_frame),
"float",
"", &halo, 0, 125,
false, requested_frame);
608 return root.toStyledString();
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.
std::string Json() const override
Generate JSON string of this object.
std::string PropertiesJSON(int64_t requested_frame) const override
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...
Json::Value JsonValue() const override
Generate Json::Value for this object.
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
float Start() const
Get start position (in seconds) of clip (trim start of video)
float Duration() const
Get the length of this clip (in seconds)
virtual float End() const
Get end position (in seconds) of clip (trim end of video)
std::string Id() const
Get the Id of this clip object.
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties)
int Layer() const
Get layer of clip on timeline (lower number is covered by higher numbers)
float Position() const
Get position on timeline (in seconds)
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.
This class represents a color (used on the timeline and clips)
openshot::Keyframe blue
Curve representing the red value (0 - 255)
openshot::Keyframe red
Curve representing the red value (0 - 255)
openshot::Keyframe green
Curve representing the green value (0 - 255)
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
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...
Json::Value JsonValue() const
Generate Json::Value for this object.
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
EffectInfoStruct info
Information about the current effect.
Exception for invalid JSON.
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
int GetInt(int64_t index) const
Get the rounded INT value at a specific index.
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
double GetValue(int64_t index) const
Get the value at a specific index.
Json::Value JsonValue() const
Generate Json::Value for this object.
This namespace is the default namespace for all code in the openshot library.
ChromaKeyMethod
This enumeration determines the algorithm used by the ChromaKey filter.
@ CHROMAKEY_CIE_LCH_C
Difference between CIE LCH(ab) chromas.
@ CHROMAKEY_HSL_L
Difference between HSL luminances.
@ CHROMAKEY_HSL_S
Difference between HSL saturations.
@ CHROMAKEY_HSV_V
Difference between HSV values.
@ CHROMAKEY_YCBCR
YCbCr vector difference of CbCr.
@ CHROMAKEY_BASIC
Length of difference between RGB vectors.
@ CHROMAKEY_CIE_LCH_L
Difference between CIE LCH(ab) luminousities.
@ CHROMAKEY_CIE_LCH_H
Difference between CIE LCH(ab) hues.
@ CHROMAKEY_HSVL_H
Difference between HSV/HSL hues.
@ CHROMAKEY_CIE_DISTANCE
CIEDE2000 perceptual difference.
@ CHROMAKEY_HSV_S
Difference between HSV saturations.
const Json::Value stringToJson(const std::string value)
bool has_video
Determines if this effect manipulates the image of a frame.
std::string parent_effect_id
Id of the parent effect (if there is one)
bool has_audio
Determines if this effect manipulates the audio of a frame.
std::string class_name
The class name of the effect.
std::string name
The name of the effect.
std::string description
The description of this effect and what it does.