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