OpenShot Library | libopenshot  0.1.9
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 
127  // VERTICAL BLUR (if any)
128  if (vertical_radius_value > 0.0) {
129  // Init boxes for computing blur
130  int *bxs = initBoxes(sigma_value, vertical_radius_value);
131 
132  // Apply vertical blur to target RGBA channels
133  boxBlurT(red, blur_red, frame_image->width(), frame_image->height(), vertical_radius_value);
134  boxBlurT(green, blur_green, frame_image->width(), frame_image->height(), vertical_radius_value);
135  boxBlurT(blue, blur_blue, frame_image->width(), frame_image->height(), vertical_radius_value);
136  boxBlurT(alpha, blur_alpha, frame_image->width(), frame_image->height(), vertical_radius_value);
137 
138  // Remove boxes
139  delete[] bxs;
140  }
141  }
142 
143  // Copy RGBA channels back to original image
144  for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4)
145  {
146  // Get the RGB values from the pixel
147  unsigned char R = blur_red[pixel];
148  unsigned char G = blur_green[pixel];
149  unsigned char B = blur_blue[pixel];
150  unsigned char A = blur_alpha[pixel];
151 
152  // Split channels into their own arrays
153  pixels[byte_index] = R;
154  pixels[byte_index + 1] = G;
155  pixels[byte_index + 2] = B;
156  pixels[byte_index + 3] = A;
157  }
158 
159  // Delete channel arrays
160  delete[] red;
161  delete[] green;
162  delete[] blue;
163  delete[] alpha;
164  delete[] blur_red;
165  delete[] blur_green;
166  delete[] blur_blue;
167  delete[] blur_alpha;
168 
169  // return the modified frame
170  return frame;
171 }
172 
173 // Credit: http://blog.ivank.net/fastest-gaussian-blur.html (MIT License)
174 int* Blur::initBoxes(float sigma, int n) // standard deviation, number of boxes
175 {
176  float wIdeal = sqrt((12.0 * sigma * sigma / n) + 1.0); // Ideal averaging filter width
177  int wl = floor(wIdeal);
178  if (wl % 2 == 0) wl--;
179  int wu = wl + 2;
180 
181  float mIdeal = (12.0 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4.0 * wl - 4);
182  int m = round(mIdeal);
183 
184  int *sizes = new int[n]();
185  for (int i = 0; i < n; i++) sizes[i] = i < m ? wl : wu;
186  return sizes;
187 }
188 
189 // Credit: http://blog.ivank.net/fastest-gaussian-blur.html (MIT License)
190 void Blur::boxBlurH(unsigned char *scl, unsigned char *tcl, int w, int h, int r) {
191  float iarr = 1.0 / (r + r + 1);
192  for (int i = 0; i < h; i++) {
193  int ti = i * w, li = ti, ri = ti + r;
194  int fv = scl[ti], lv = scl[ti + w - 1], val = (r + 1) * fv;
195  for (int j = 0; j < r; j++) val += scl[ti + j];
196  for (int j = 0; j <= r; j++) {
197  val += scl[ri++] - fv;
198  tcl[ti++] = round(val * iarr);
199  }
200  for (int j = r + 1; j < w - r; j++) {
201  val += scl[ri++] - scl[li++];
202  tcl[ti++] = round(val * iarr);
203  }
204  for (int j = w - r; j < w; j++) {
205  val += lv - scl[li++];
206  tcl[ti++] = round(val * iarr);
207  }
208  }
209 }
210 
211 void Blur::boxBlurT(unsigned char *scl, unsigned char *tcl, int w, int h, int r) {
212  float iarr = 1.0 / (r + r + 1);
213  for (int i = 0; i < w; i++) {
214  int ti = i, li = ti, ri = ti + r * w;
215  int fv = scl[ti], lv = scl[ti + w * (h - 1)], val = (r + 1) * fv;
216  for (int j = 0; j < r; j++) val += scl[ti + j * w];
217  for (int j = 0; j <= r; j++) {
218  val += scl[ri] - fv;
219  tcl[ti] = round(val * iarr);
220  ri += w;
221  ti += w;
222  }
223  for (int j = r + 1; j < h - r; j++) {
224  val += scl[ri] - scl[li];
225  tcl[ti] = round(val * iarr);
226  li += w;
227  ri += w;
228  ti += w;
229  }
230  for (int j = h - r; j < h; j++) {
231  val += lv - scl[li];
232  tcl[ti] = round(val * iarr);
233  li += w;
234  ti += w;
235  }
236  }
237 }
238 
239 // Generate JSON string of this object
240 string Blur::Json() {
241 
242  // Return formatted string
243  return JsonValue().toStyledString();
244 }
245 
246 // Generate Json::JsonValue for this object
247 Json::Value Blur::JsonValue() {
248 
249  // Create root json object
250  Json::Value root = EffectBase::JsonValue(); // get parent properties
251  root["type"] = info.class_name;
252  root["horizontal_radius"] = horizontal_radius.JsonValue();
253  root["vertical_radius"] = vertical_radius.JsonValue();
254  root["sigma"] = sigma.JsonValue();
255  root["iterations"] = iterations.JsonValue();
256 
257  // return JsonValue
258  return root;
259 }
260 
261 // Load JSON string into this object
262 void Blur::SetJson(string value) {
263 
264  // Parse JSON string into JSON objects
265  Json::Value root;
266  Json::Reader reader;
267  bool success = reader.parse( value, root );
268  if (!success)
269  // Raise exception
270  throw InvalidJSON("JSON could not be parsed (or is invalid)", "");
271 
272  try
273  {
274  // Set all values that match
275  SetJsonValue(root);
276  }
277  catch (exception e)
278  {
279  // Error parsing JSON (or missing keys)
280  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", "");
281  }
282 }
283 
284 // Load Json::JsonValue into this object
285 void Blur::SetJsonValue(Json::Value root) {
286 
287  // Set parent data
289 
290  // Set data from Json (if key is found)
291  if (!root["horizontal_radius"].isNull())
292  horizontal_radius.SetJsonValue(root["horizontal_radius"]);
293  else if (!root["vertical_radius"].isNull())
294  vertical_radius.SetJsonValue(root["vertical_radius"]);
295  else if (!root["sigma"].isNull())
296  sigma.SetJsonValue(root["sigma"]);
297  else if (!root["iterations"].isNull())
298  iterations.SetJsonValue(root["iterations"]);
299 }
300 
301 // Get all properties for a specific frame
302 string Blur::PropertiesJSON(int64_t requested_frame) {
303 
304  // Generate JSON properties list
305  Json::Value root;
306  root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
307  root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
308  root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
309  root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
310  root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
311  root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 1000 * 60 * 30, true, requested_frame);
312 
313  // Keyframes
314  root["horizontal_radius"] = add_property_json("Horizontal Radius", horizontal_radius.GetValue(requested_frame), "float", "", &horizontal_radius, 0, 100, false, requested_frame);
315  root["vertical_radius"] = add_property_json("Vertical Radius", vertical_radius.GetValue(requested_frame), "float", "", &vertical_radius, 0, 100, false, requested_frame);
316  root["sigma"] = add_property_json("Sigma", sigma.GetValue(requested_frame), "float", "", &sigma, 0, 100, false, requested_frame);
317  root["iterations"] = add_property_json("Iterations", iterations.GetValue(requested_frame), "float", "", &iterations, 0, 100, false, requested_frame);
318 
319  // Return formatted string
320  return root.toStyledString();
321 }
322 
string Json()
Get and Set JSON methods.
Definition: Blur.cpp:240
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:247
string PropertiesJSON(int64_t requested_frame)
Definition: Blur.cpp:302
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:69
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:285
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:109
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:262
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
This namespace is the default namespace for all code in the openshot library.
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