Skip to content

Commit 6666702

Browse files
authored
Merge pull request #423 from ferdnyc/optimized-blur
Optimized, parallelized Blur effect
2 parents f9b4fe5 + fd663c4 commit 6666702

File tree

2 files changed

+61
-145
lines changed

2 files changed

+61
-145
lines changed

include/effects/Blur.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ namespace openshot
6767
void init_effect_details();
6868

6969
/// Internal blur methods (inspired and credited to http://blog.ivank.net/fastest-gaussian-blur.html)
70-
int* initBoxes(float sigma, int n);
7170
void boxBlurH(unsigned char *scl, unsigned char *tcl, int w, int h, int r);
7271
void boxBlurT(unsigned char *scl, unsigned char *tcl, int w, int h, int r);
7372

src/effects/Blur.cpp

Lines changed: 61 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -74,179 +74,96 @@ std::shared_ptr<Frame> Blur::GetFrame(std::shared_ptr<Frame> frame, int64_t fram
7474
float sigma_value = sigma.GetValue(frame_number);
7575
int iteration_value = iterations.GetInt(frame_number);
7676

77+
int w = frame_image->width();
78+
int h = frame_image->height();
7779

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];
80+
// Grab two copies of the image pixel data
81+
QImage image_copy = frame_image->copy();
82+
std::shared_ptr<QImage> frame_image_2 = std::make_shared<QImage>(image_copy);
11183

11284
// Loop through each iteration
113-
for (int iteration = 0; iteration < iteration_value; iteration++)
85+
for (int iteration = 0; iteration < iteration_value; ++iteration)
11486
{
11587
// HORIZONTAL BLUR (if any)
11688
if (horizontal_radius_value > 0.0) {
117-
// Init boxes for computing blur
118-
int *bxs = initBoxes(sigma_value, horizontal_radius_value);
119-
12089
// 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];
90+
boxBlurH(frame_image->bits(), frame_image_2->bits(), w, h, horizontal_radius_value);
91+
92+
// Swap output image back to input
93+
frame_image.swap(frame_image_2);
13494
}
13595

13696
// VERTICAL BLUR (if any)
13797
if (vertical_radius_value > 0.0) {
138-
// Init boxes for computing blur
139-
int *bxs = initBoxes(sigma_value, vertical_radius_value);
140-
14198
// 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-
}
99+
boxBlurT(frame_image->bits(), frame_image_2->bits(), w, h, vertical_radius_value);
157100

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;
101+
// Swap output image back to input
102+
frame_image.swap(frame_image_2);
103+
}
172104
}
173105

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-
184106
// return the modified frame
185107
return frame;
186108
}
187109

188110
// 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)
111+
// Modified to process all four channels in a pixel array
205112
void Blur::boxBlurH(unsigned char *scl, unsigned char *tcl, int w, int h, int r) {
206113
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);
114+
115+
#pragma omp parallel for shared (scl, tcl)
116+
for (int i = 0; i < h; ++i) {
117+
for (int ch = 0; ch < 4; ++ch) {
118+
int ti = i * w, li = ti, ri = ti + r;
119+
int fv = scl[ti * 4 + ch], lv = scl[(ti + w - 1) * 4 + ch], val = (r + 1) * fv;
120+
for (int j = 0; j < r; ++j) {
121+
val += scl[(ti + j) * 4 + ch];
122+
}
123+
for (int j = 0; j <= r; ++j) {
124+
val += scl[ri++ * 4 + ch] - fv;
125+
tcl[ti++ * 4 + ch] = round(val * iarr);
126+
}
127+
for (int j = r + 1; j < w - r; ++j) {
128+
val += scl[ri++ * 4 + ch] - scl[li++ * 4 + ch];
129+
tcl[ti++ * 4 + ch] = round(val * iarr);
130+
}
131+
for (int j = w - r; j < w; ++j) {
132+
val += lv - scl[li++ * 4 + ch];
133+
tcl[ti++ * 4 + ch] = round(val * iarr);
134+
}
222135
}
223136
}
224137
}
225138

226139
void Blur::boxBlurT(unsigned char *scl, unsigned char *tcl, int w, int h, int r) {
227140
float iarr = 1.0 / (r + r + 1);
141+
142+
#pragma omp parallel for shared (scl, tcl)
228143
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;
144+
for (int ch = 0; ch < 4; ++ch) {
145+
int ti = i, li = ti, ri = ti + r * w;
146+
int fv = scl[ti * 4 + ch], lv = scl[(ti + w * (h - 1)) * 4 + ch], val = (r + 1) * fv;
147+
for (int j = 0; j < r; j++) val += scl[(ti + j * w) * 4 + ch];
148+
for (int j = 0; j <= r; j++) {
149+
val += scl[ri * 4 + ch] - fv;
150+
tcl[ti * 4 + ch] = round(val * iarr);
151+
ri += w;
152+
ti += w;
153+
}
154+
for (int j = r + 1; j < h - r; j++) {
155+
val += scl[ri * 4 + ch] - scl[li * 4 + ch];
156+
tcl[ti * 4 + ch] = round(val * iarr);
157+
li += w;
158+
ri += w;
159+
ti += w;
160+
}
161+
for (int j = h - r; j < h; j++) {
162+
val += lv - scl[li * 4 + ch];
163+
tcl[ti * 4 + ch] = round(val * iarr);
164+
li += w;
165+
ti += w;
166+
}
250167
}
251168
}
252169
}

0 commit comments

Comments
 (0)