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