OpenShot Library | libopenshot  0.2.4
Blur.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @brief Source file for Blur effect 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/Blur.h"
32 
33 using namespace openshot;
34 
35 /// Blank constructor, useful when using Json to load the effect properties
36 Blur::Blur() : horizontal_radius(6.0), vertical_radius(6.0), sigma(3.0), iterations(3.0) {
37  // Init effect properties
38  init_effect_details();
39 }
40 
41 // Default constructor
42 Blur::Blur(Keyframe new_horizontal_radius, Keyframe new_vertical_radius, Keyframe new_sigma, Keyframe new_iterations) :
43  horizontal_radius(new_horizontal_radius), vertical_radius(new_vertical_radius),
44  sigma(new_sigma), iterations(new_iterations)
45 {
46  // Init effect properties
47  init_effect_details();
48 }
49 
50 // Init effect settings
51 void Blur::init_effect_details()
52 {
53  /// Initialize the values of the EffectInfo struct.
55 
56  /// Set the effect info
57  info.class_name = "Blur";
58  info.name = "Blur";
59  info.description = "Adjust the blur of the frame's image.";
60  info.has_audio = false;
61  info.has_video = true;
62 }
63 
64 // This method is required for all derived classes of EffectBase, and returns a
65 // modified openshot::Frame object
66 std::shared_ptr<Frame> Blur::GetFrame(std::shared_ptr<Frame> frame, int64_t frame_number)
67 {
68  // Get the frame's image
69  std::shared_ptr<QImage> frame_image = frame->GetImage();
70 
71  // Get the current blur radius
72  int horizontal_radius_value = horizontal_radius.GetValue(frame_number);
73  int vertical_radius_value = vertical_radius.GetValue(frame_number);
74  float sigma_value = sigma.GetValue(frame_number);
75  int iteration_value = iterations.GetInt(frame_number);
76 
77 
78  // Declare arrays for each color channel
79  unsigned char *red = new unsigned char[frame_image->width() * frame_image->height()]();
80  unsigned char *green = new unsigned char[frame_image->width() * frame_image->height()]();
81  unsigned char *blue = new unsigned char[frame_image->width() * frame_image->height()]();
82  unsigned char *alpha = new unsigned char[frame_image->width() * frame_image->height()]();
83  // Create empty target RGBA arrays (for the results of our blur)
84  unsigned char *blur_red = new unsigned char[frame_image->width() * frame_image->height()]();
85  unsigned char *blur_green = new unsigned char[frame_image->width() * frame_image->height()]();
86  unsigned char *blur_blue = new unsigned char[frame_image->width() * frame_image->height()]();
87  unsigned char *blur_alpha = new unsigned char[frame_image->width() * frame_image->height()]();
88 
89  // Loop through pixels and split RGBA channels into separate arrays
90  unsigned char *pixels = (unsigned char *) frame_image->bits();
91  for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4)
92  {
93  // Get the RGBA values from each pixel
94  unsigned char R = pixels[byte_index];
95  unsigned char G = pixels[byte_index + 1];
96  unsigned char B = pixels[byte_index + 2];
97  unsigned char A = pixels[byte_index + 3];
98 
99  // Split channels into their own arrays
100  red[pixel] = R;
101  green[pixel] = G;
102  blue[pixel] = B;
103  alpha[pixel] = A;
104  }
105 
106  // Init target RGBA arrays
107  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_red[i] = red[i];
108  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_green[i] = green[i];
109  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_blue[i] = blue[i];
110  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_alpha[i] = alpha[i];
111 
112  // Loop through each iteration
113  for (int iteration = 0; iteration < iteration_value; iteration++)
114  {
115  // HORIZONTAL BLUR (if any)
116  if (horizontal_radius_value > 0.0) {
117  // Init boxes for computing blur
118  int *bxs = initBoxes(sigma_value, horizontal_radius_value);
119 
120  // Apply horizontal blur to target RGBA channels
121  boxBlurH(red, blur_red, frame_image->width(), frame_image->height(), horizontal_radius_value);
122  boxBlurH(green, blur_green, frame_image->width(), frame_image->height(), horizontal_radius_value);
123  boxBlurH(blue, blur_blue, frame_image->width(), frame_image->height(), horizontal_radius_value);
124  boxBlurH(alpha, blur_alpha, frame_image->width(), frame_image->height(), horizontal_radius_value);
125 
126  // Remove boxes
127  delete[] bxs;
128 
129  // Copy blur_<chan> back to <chan> for vertical blur or next iteration
130  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) red[i] = blur_red[i];
131  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) green[i] = blur_green[i];
132  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blue[i] = blur_blue[i];
133  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) alpha[i] = blur_alpha[i];
134  }
135 
136  // VERTICAL BLUR (if any)
137  if (vertical_radius_value > 0.0) {
138  // Init boxes for computing blur
139  int *bxs = initBoxes(sigma_value, vertical_radius_value);
140 
141  // Apply vertical blur to target RGBA channels
142  boxBlurT(red, blur_red, frame_image->width(), frame_image->height(), vertical_radius_value);
143  boxBlurT(green, blur_green, frame_image->width(), frame_image->height(), vertical_radius_value);
144  boxBlurT(blue, blur_blue, frame_image->width(), frame_image->height(), vertical_radius_value);
145  boxBlurT(alpha, blur_alpha, frame_image->width(), frame_image->height(), vertical_radius_value);
146 
147  // Remove boxes
148  delete[] bxs;
149 
150  // Copy blur_<chan> back to <chan> for vertical blur or next iteration
151  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) red[i] = blur_red[i];
152  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) green[i] = blur_green[i];
153  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blue[i] = blur_blue[i];
154  for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) alpha[i] = blur_alpha[i];
155  }
156  }
157 
158  // Copy RGBA channels back to original image
159  for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4)
160  {
161  // Get the RGB values from the pixel
162  unsigned char R = blur_red[pixel];
163  unsigned char G = blur_green[pixel];
164  unsigned char B = blur_blue[pixel];
165  unsigned char A = blur_alpha[pixel];
166 
167  // Split channels into their own arrays
168  pixels[byte_index] = R;
169  pixels[byte_index + 1] = G;
170  pixels[byte_index + 2] = B;
171  pixels[byte_index + 3] = A;
172  }
173 
174  // Delete channel arrays
175  delete[] red;
176  delete[] green;
177  delete[] blue;
178  delete[] alpha;
179  delete[] blur_red;
180  delete[] blur_green;
181  delete[] blur_blue;
182  delete[] blur_alpha;
183 
184  // return the modified frame
185  return frame;
186 }
187 
188 // Credit: http://blog.ivank.net/fastest-gaussian-blur.html (MIT License)
189 int* Blur::initBoxes(float sigma, int n) // standard deviation, number of boxes
190 {
191  float wIdeal = sqrt((12.0 * sigma * sigma / n) + 1.0); // Ideal averaging filter width
192  int wl = floor(wIdeal);
193  if (wl % 2 == 0) wl--;
194  int wu = wl + 2;
195 
196  float mIdeal = (12.0 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4.0 * wl - 4);
197  int m = round(mIdeal);
198 
199  int *sizes = new int[n]();
200  for (int i = 0; i < n; i++) sizes[i] = i < m ? wl : wu;
201  return sizes;
202 }
203 
204 // Credit: http://blog.ivank.net/fastest-gaussian-blur.html (MIT License)
205 void Blur::boxBlurH(unsigned char *scl, unsigned char *tcl, int w, int h, int r) {
206  float iarr = 1.0 / (r + r + 1);
207  for (int i = 0; i < h; i++) {
208  int ti = i * w, li = ti, ri = ti + r;
209  int fv = scl[ti], lv = scl[ti + w - 1], val = (r + 1) * fv;
210  for (int j = 0; j < r; j++) val += scl[ti + j];
211  for (int j = 0; j <= r; j++) {
212  val += scl[ri++] - fv;
213  tcl[ti++] = round(val * iarr);
214  }
215  for (int j = r + 1; j < w - r; j++) {
216  val += scl[ri++] - scl[li++];
217  tcl[ti++] = round(val * iarr);
218  }
219  for (int j = w - r; j < w; j++) {
220  val += lv - scl[li++];
221  tcl[ti++] = round(val * iarr);
222  }
223  }
224 }
225 
226 void Blur::boxBlurT(unsigned char *scl, unsigned char *tcl, int w, int h, int r) {
227  float iarr = 1.0 / (r + r + 1);
228  for (int i = 0; i < w; i++) {
229  int ti = i, li = ti, ri = ti + r * w;
230  int fv = scl[ti], lv = scl[ti + w * (h - 1)], val = (r + 1) * fv;
231  for (int j = 0; j < r; j++) val += scl[ti + j * w];
232  for (int j = 0; j <= r; j++) {
233  val += scl[ri] - fv;
234  tcl[ti] = round(val * iarr);
235  ri += w;
236  ti += w;
237  }
238  for (int j = r + 1; j < h - r; j++) {
239  val += scl[ri] - scl[li];
240  tcl[ti] = round(val * iarr);
241  li += w;
242  ri += w;
243  ti += w;
244  }
245  for (int j = h - r; j < h; j++) {
246  val += lv - scl[li];
247  tcl[ti] = round(val * iarr);
248  li += w;
249  ti += w;
250  }
251  }
252 }
253 
254 // Generate JSON string of this object
255 std::string Blur::Json() {
256 
257  // Return formatted string
258  return JsonValue().toStyledString();
259 }
260 
261 // Generate Json::JsonValue for this object
262 Json::Value Blur::JsonValue() {
263 
264  // Create root json object
265  Json::Value root = EffectBase::JsonValue(); // get parent properties
266  root["type"] = info.class_name;
267  root["horizontal_radius"] = horizontal_radius.JsonValue();
268  root["vertical_radius"] = vertical_radius.JsonValue();
269  root["sigma"] = sigma.JsonValue();
270  root["iterations"] = iterations.JsonValue();
271 
272  // return JsonValue
273  return root;
274 }
275 
276 // Load JSON string into this object
277 void Blur::SetJson(std::string value) {
278 
279  // Parse JSON string into JSON objects
280  Json::Value root;
281  Json::CharReaderBuilder rbuilder;
282  Json::CharReader* reader(rbuilder.newCharReader());
283 
284  std::string errors;
285  bool success = reader->parse( value.c_str(),
286  value.c_str() + value.size(), &root, &errors );
287  delete reader;
288 
289  if (!success)
290  // Raise exception
291  throw InvalidJSON("JSON could not be parsed (or is invalid)");
292 
293  try
294  {
295  // Set all values that match
296  SetJsonValue(root);
297  }
298  catch (const std::exception& e)
299  {
300  // Error parsing JSON (or missing keys)
301  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
302  }
303 }
304 
305 // Load Json::JsonValue into this object
306 void Blur::SetJsonValue(Json::Value root) {
307 
308  // Set parent data
310 
311  // Set data from Json (if key is found)
312  if (!root["horizontal_radius"].isNull())
313  horizontal_radius.SetJsonValue(root["horizontal_radius"]);
314  if (!root["vertical_radius"].isNull())
315  vertical_radius.SetJsonValue(root["vertical_radius"]);
316  if (!root["sigma"].isNull())
317  sigma.SetJsonValue(root["sigma"]);
318  if (!root["iterations"].isNull())
319  iterations.SetJsonValue(root["iterations"]);
320 }
321 
322 // Get all properties for a specific frame
323 std::string Blur::PropertiesJSON(int64_t requested_frame) {
324 
325  // Generate JSON properties list
326  Json::Value root;
327  root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
328  root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
329  root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
330  root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
331  root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
332  root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 1000 * 60 * 30, true, requested_frame);
333 
334  // Keyframes
335  root["horizontal_radius"] = add_property_json("Horizontal Radius", horizontal_radius.GetValue(requested_frame), "float", "", &horizontal_radius, 0, 100, false, requested_frame);
336  root["vertical_radius"] = add_property_json("Vertical Radius", vertical_radius.GetValue(requested_frame), "float", "", &vertical_radius, 0, 100, false, requested_frame);
337  root["sigma"] = add_property_json("Sigma", sigma.GetValue(requested_frame), "float", "", &sigma, 0, 100, false, requested_frame);
338  root["iterations"] = add_property_json("Iterations", iterations.GetValue(requested_frame), "float", "", &iterations, 0, 100, false, requested_frame);
339 
340  // Return formatted string
341  return root.toStyledString();
342 }
std::string PropertiesJSON(int64_t requested_frame)
Definition: Blur.cpp:323
Json::Value JsonValue()
Generate Json::JsonValue for this object.
Definition: Blur.cpp:262
float End()
Get end position (in seconds) of clip (trim end of video)
Definition: ClipBase.h:80
int Layer()
Get layer of clip on timeline (lower number is covered by higher numbers)
Definition: ClipBase.h:78
Keyframe iterations
Iterations keyframe. The # of blur iterations per pixel. 3 iterations = Gaussian. ...
Definition: Blur.h:79
virtual Json::Value JsonValue()=0
Generate Json::JsonValue for this object.
Definition: EffectBase.cpp:84
int GetInt(int64_t index) const
Get the rounded INT value at a specific index.
Definition: KeyFrame.cpp:286
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Definition: KeyFrame.cpp:374
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition: EffectBase.h:56
std::string Id()
Get basic properties.
Definition: ClipBase.h:76
float Position()
Get position on timeline (in seconds)
Definition: ClipBase.h:77
Keyframe vertical_radius
Vertical blur radius keyframe. The size of the vertical blur operation in pixels. ...
Definition: Blur.h:77
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Definition: Blur.cpp:306
virtual void SetJsonValue(Json::Value root)=0
Load Json::JsonValue into this object.
Definition: EffectBase.cpp:129
std::string class_name
The class name of the effect.
Definition: EffectBase.h:52
Blur()
Blank constructor, useful when using Json to load the effect properties.
Definition: Blur.cpp:36
std::string name
The name of the effect.
Definition: EffectBase.h:53
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: Blur.cpp:66
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame)
Generate JSON for a property.
Definition: ClipBase.cpp:68
This namespace is the default namespace for all code in the openshot library.
Json::Value JsonValue() const
Generate Json::JsonValue for this object.
Definition: KeyFrame.cpp:329
std::string description
The description of this effect and what it does.
Definition: EffectBase.h:54
bool has_video
Determines if this effect manipulates the image of a frame.
Definition: EffectBase.h:55
std::string Json()
Get and Set JSON methods.
Definition: Blur.cpp:255
void SetJson(std::string value)
Load JSON string into this object.
Definition: Blur.cpp:277
Exception for invalid JSON.
Definition: Exceptions.h:205
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:262
A Keyframe is a collection of Point instances, which is used to vary a number or property over time...
Definition: KeyFrame.h:64
float Duration()
Get the length of this clip (in seconds)
Definition: ClipBase.h:81
Keyframe sigma
Sigma keyframe. The amount of spread in the blur operation. Should be larger than radius...
Definition: Blur.h:78
float Start()
Get start position (in seconds) of clip (trim start of video)
Definition: ClipBase.h:79
Keyframe horizontal_radius
Horizontal blur radius keyframe. The size of the horizontal blur operation in pixels.
Definition: Blur.h:76
EffectInfoStruct info
Information about the current effect.
Definition: EffectBase.h:73