OpenShot Library | libopenshot 0.3.1
Mask.cpp
Go to the documentation of this file.
1
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include "Mask.h"
14
15#include "Exceptions.h"
16
17#include "ReaderBase.h"
18#include "ChunkReader.h"
19#include "FFmpegReader.h"
20#include "QtImageReader.h"
21
22#ifdef USE_IMAGEMAGICK
23 #include "ImageReader.h"
24#endif
25
26using namespace openshot;
27
29Mask::Mask() : reader(NULL), replace_image(false), needs_refresh(true) {
30 // Init effect properties
31 init_effect_details();
32}
33
34// Default constructor
35Mask::Mask(ReaderBase *mask_reader, Keyframe mask_brightness, Keyframe mask_contrast) :
36 reader(mask_reader), brightness(mask_brightness), contrast(mask_contrast), replace_image(false), needs_refresh(true)
37{
38 // Init effect properties
39 init_effect_details();
40}
41
42// Init effect settings
43void Mask::init_effect_details()
44{
47
49 info.class_name = "Mask";
50 info.name = "Alpha Mask / Wipe Transition";
51 info.description = "Uses a grayscale mask image to gradually wipe / transition between 2 images.";
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
58std::shared_ptr<openshot::Frame> Mask::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
59 // Get the mask image (from the mask reader)
60 std::shared_ptr<QImage> frame_image = frame->GetImage();
61
62 // Check if mask reader is open
63 #pragma omp critical (open_mask_reader)
64 {
65 if (reader && !reader->IsOpen())
66 reader->Open();
67 }
68
69 // No reader (bail on applying the mask)
70 if (!reader)
71 return frame;
72
73 // Get mask image (if missing or different size than frame image)
74 #pragma omp critical (open_mask_reader)
75 {
76 if (!original_mask || !reader->info.has_single_image || needs_refresh ||
77 (original_mask && original_mask->size() != frame_image->size())) {
78
79 // Only get mask if needed
80 auto mask_without_sizing = std::make_shared<QImage>(
81 *reader->GetFrame(frame_number)->GetImage());
82
83 // Resize mask image to match frame size
84 original_mask = std::make_shared<QImage>(
85 mask_without_sizing->scaled(
86 frame_image->width(), frame_image->height(),
87 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
88 }
89 }
90
91 // Refresh no longer needed
92 needs_refresh = false;
93
94 // Get pixel arrays
95 unsigned char *pixels = (unsigned char *) frame_image->bits();
96 unsigned char *mask_pixels = (unsigned char *) original_mask->bits();
97
98 double contrast_value = (contrast.GetValue(frame_number));
99 double brightness_value = (brightness.GetValue(frame_number));
100
101 // Loop through mask pixels, and apply average gray value to frame alpha channel
102 for (int pixel = 0, byte_index=0; pixel < original_mask->width() * original_mask->height(); pixel++, byte_index+=4)
103 {
104 // Get the RGB values from the pixel
105 int R = mask_pixels[byte_index];
106 int G = mask_pixels[byte_index + 1];
107 int B = mask_pixels[byte_index + 2];
108 int A = mask_pixels[byte_index + 3];
109
110 // Get the average luminosity
111 int gray_value = qGray(R, G, B);
112
113 // Adjust the brightness
114 gray_value += (255 * brightness_value);
115
116 // Adjust the contrast
117 float factor = (20 / std::fmax(0.00001, 20.0 - contrast_value));
118 gray_value = (factor * (gray_value - 128) + 128);
119
120 // Calculate the % change in alpha
121 float alpha_percent = float(constrain(A - gray_value)) / 255.0;
122
123 // Set the alpha channel to the gray value
124 if (replace_image) {
125 // Replace frame pixels with gray value (including alpha channel)
126 pixels[byte_index + 0] = constrain(255 * alpha_percent);
127 pixels[byte_index + 1] = constrain(255 * alpha_percent);
128 pixels[byte_index + 2] = constrain(255 * alpha_percent);
129 pixels[byte_index + 3] = constrain(255 * alpha_percent);
130 } else {
131 // Mulitply new alpha value with all the colors (since we are using a premultiplied
132 // alpha format)
133 pixels[byte_index + 0] *= alpha_percent;
134 pixels[byte_index + 1] *= alpha_percent;
135 pixels[byte_index + 2] *= alpha_percent;
136 pixels[byte_index + 3] *= alpha_percent;
137 }
138
139 }
140
141 // return the modified frame
142 return frame;
143}
144
145// Generate JSON string of this object
146std::string Mask::Json() const {
147
148 // Return formatted string
149 return JsonValue().toStyledString();
150}
151
152// Generate Json::Value for this object
153Json::Value Mask::JsonValue() const {
154
155 // Create root json object
156 Json::Value root = EffectBase::JsonValue(); // get parent properties
157 root["type"] = info.class_name;
158 root["brightness"] = brightness.JsonValue();
159 root["contrast"] = contrast.JsonValue();
160 if (reader)
161 root["reader"] = reader->JsonValue();
162 else
163 root["reader"] = Json::objectValue;
164 root["replace_image"] = replace_image;
165
166 // return JsonValue
167 return root;
168}
169
170// Load JSON string into this object
171void Mask::SetJson(const std::string value) {
172
173 // Parse JSON string into JSON objects
174 try
175 {
176 const Json::Value root = openshot::stringToJson(value);
177 // Set all values that match
178 SetJsonValue(root);
179 }
180 catch (const std::exception& e)
181 {
182 // Error parsing JSON (or missing keys)
183 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
184 }
185}
186
187// Load Json::Value into this object
188void Mask::SetJsonValue(const Json::Value root) {
189
190 // Set parent data
192
193 // Set data from Json (if key is found)
194 if (!root["replace_image"].isNull())
195 replace_image = root["replace_image"].asBool();
196 if (!root["brightness"].isNull())
197 brightness.SetJsonValue(root["brightness"]);
198 if (!root["contrast"].isNull())
199 contrast.SetJsonValue(root["contrast"]);
200 if (!root["reader"].isNull()) // does Json contain a reader?
201 {
202 #pragma omp critical (open_mask_reader)
203 {
204 // This reader has changed, so refresh cached assets
205 needs_refresh = true;
206
207 if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
208 {
209 // Close previous reader (if any)
210 if (reader) {
211 // Close and delete existing reader (if any)
212 reader->Close();
213 delete reader;
214 reader = NULL;
215 }
216
217 // Create new reader (and load properties)
218 std::string type = root["reader"]["type"].asString();
219
220 if (type == "FFmpegReader") {
221
222 // Create new reader
223 reader = new FFmpegReader(root["reader"]["path"].asString());
224 reader->SetJsonValue(root["reader"]);
225
226 #ifdef USE_IMAGEMAGICK
227 } else if (type == "ImageReader") {
228
229 // Create new reader
230 reader = new ImageReader(root["reader"]["path"].asString());
231 reader->SetJsonValue(root["reader"]);
232 #endif
233
234 } else if (type == "QtImageReader") {
235
236 // Create new reader
237 reader = new QtImageReader(root["reader"]["path"].asString());
238 reader->SetJsonValue(root["reader"]);
239
240 } else if (type == "ChunkReader") {
241
242 // Create new reader
243 reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
244 reader->SetJsonValue(root["reader"]);
245
246 }
247 }
248
249 }
250 }
251
252}
253
254// Get all properties for a specific frame
255std::string Mask::PropertiesJSON(int64_t requested_frame) const {
256
257 // Generate JSON properties list
258 Json::Value root;
259 root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
260 root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
261 root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
262 root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
263 root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
264 root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame);
265 root["replace_image"] = add_property_json("Replace Image", replace_image, "int", "", NULL, 0, 1, false, requested_frame);
266
267 // Add replace_image choices (dropdown style)
268 root["replace_image"]["choices"].append(add_property_choice_json("Yes", true, replace_image));
269 root["replace_image"]["choices"].append(add_property_choice_json("No", false, replace_image));
270
271 // Keyframes
272 root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", &brightness, -1.0, 1.0, false, requested_frame);
273 root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", &contrast, 0, 20, false, requested_frame);
274
275 if (reader)
276 root["reader"] = add_property_json("Source", 0.0, "reader", reader->Json(), NULL, 0, 1, false, requested_frame);
277 else
278 root["reader"] = add_property_json("Source", 0.0, "reader", "{}", NULL, 0, 1, false, requested_frame);
279
280 // Set the parent effect which properties this effect will inherit
281 root["parent_effect_id"] = add_property_json("Parent", 0.0, "string", info.parent_effect_id, NULL, -1, -1, false, requested_frame);
282
283 // Return formatted string
284 return root.toStyledString();
285}
Header file for ChunkReader class.
Header file for all Exception classes.
Header file for FFmpegReader class.
Header file for ImageReader class.
Header file for Mask class.
Header file for QtImageReader class.
Header file for ReaderBase class.
This class reads a special chunk-formatted file, which can be easily shared in a distributed environm...
Definition: ChunkReader.h:79
float Start() const
Get start position (in seconds) of clip (trim start of video)
Definition: ClipBase.h:88
float Duration() const
Get the length of this clip (in seconds)
Definition: ClipBase.h:90
virtual float End() const
Get end position (in seconds) of clip (trim end of video)
Definition: ClipBase.h:89
std::string Id() const
Get the Id of this clip object.
Definition: ClipBase.h:85
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
int Layer() const
Get layer of clip on timeline (lower number is covered by higher numbers)
Definition: ClipBase.h:87
float Position() const
Get position on timeline (in seconds)
Definition: ClipBase.h:86
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
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: EffectBase.cpp:77
int constrain(int color_value)
Constrain a color value from 0 to 255.
Definition: EffectBase.cpp:58
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: EffectBase.cpp:112
EffectInfoStruct info
Information about the current effect.
Definition: EffectBase.h:69
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
Definition: FFmpegReader.h:101
This class uses the ImageMagick++ libraries, to open image files, and return openshot::Frame objects ...
Definition: ImageReader.h:56
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
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
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: Mask.cpp:171
Mask()
Blank constructor, useful when using Json to load the effect properties.
Definition: Mask.cpp:29
bool replace_image
Replace the frame image with a grayscale image representing the mask. Great for debugging a mask.
Definition: Mask.h:47
Keyframe brightness
Brightness keyframe to control the wipe / mask effect. A constant value here will prevent animation.
Definition: Mask.h:48
std::string PropertiesJSON(int64_t requested_frame) const override
Definition: Mask.cpp:255
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: Mask.cpp:153
std::string Json() const override
Generate JSON string of this object.
Definition: Mask.cpp:146
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: Mask.cpp:188
Keyframe contrast
Contrast keyframe to control the hardness of the wipe effect / mask.
Definition: Mask.h:49
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: Mask.h:69
This class uses the Qt library, to open image files, and return openshot::Frame objects containing th...
Definition: QtImageReader.h:75
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:76
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:88
virtual std::string Json() const =0
Generate JSON string of this object.
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition: ReaderBase.cpp:162
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ReaderBase.cpp:107
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
virtual void Close()=0
Close the reader (and any resources it was consuming)
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:29
ChunkVersion
This enumeration allows the user to choose which version of the chunk they would like (low,...
Definition: ChunkReader.h:50
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
std::string parent_effect_id
Id of the parent effect (if there is one)
Definition: EffectBase.h:39
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
bool has_single_image
Determines if this file only contains a single image.
Definition: ReaderBase.h:42