|
|
Home » U++ Library support » U++ Library : Other (not classified elsewhere) » [Proposal] Blur algorithm (fast box blur with gaussian approximation)
[Proposal] Blur algorithm (fast box blur with gaussian approximation) [message #53640] |
Sat, 18 April 2020 00:51 |
Oblivion
Messages: 1143 Registered: August 2007
|
Senior Contributor |
|
|
Hello,
Blur seems to be "missing" from the image utility functions.
I've adapted Ivan Kutskir's fast blur implementation to U++.
The only missing thing is alpha channel blurring. I commented out the alpha channel blurring in the below code. (it leaves artifacts at the margins of output image)
Image Blur(const Image& img, int radius)
{
// This code is adapted from Ivan Kutskir's fast blur implementation, published under MIT license.
// See: http://blog.ivank.net/fastest-gaussian-blur.html
// An implementation of well known fast box and gaussian blur
// approximation algorithms by Wojciech Jarosz and Peter Kovesi.
// See: https://elynxsdk.free.fr/ext-docs/Blur/Fast_box_blur.pdf
// See: https://www.peterkovesi.com/papers/FastGaussianSmoothing.pdf
auto GetBoxes = [](int sigma, int n) -> Vector<int>
{
double wl = ffloor(sqrt((12 * sqr(sigma) / n) + 1));
if(fmod(wl, 2) == 0) wl--;
double wu = wl + 2;
double m = fround((12 * sqr(sigma) - n * sqr(wl) - 4 * n * wl - 3 * n) / (-4 * wl - 4));
Vector<int> sizes;
for (int i = 0; i < n; i++)
sizes.Add(i < m ? wl : wu);
return pick(sizes);
};
auto ApplyBoxBlur = [] (const Image& src, int r) -> Image
{
double avg = 1.0 / (r + r + 1);
Size sz = src.GetSize();
ImageBuffer tmp(sz);
ImageBuffer out(sz);
Premultiply(tmp);
Premultiply(out);
for(int i = 0; i < sz.cy; i++) {
int ti = 0;
int li = ti;
int ri = r;
const RGBA& fv = src[i][0];
const RGBA& lv = src[i][sz.cx - 1];
dword rsum = fv.r * (r + 1);
dword gsum = fv.g * (r + 1);
dword bsum = fv.b * (r + 1);
// dword asum = fv.a * (r + 1);
for(int j = 0; j < r; j++) {
const RGBA& p = src[i][j];
rsum += p.r;
gsum += p.g;
bsum += p.b;
// asum += p.a;
}
for(int j = 0; j <= r; j++) {
const RGBA& p = src[i][ri++];
RGBA& q = tmp[i][ti++];
q.r = (rsum += p.r - fv.r) * avg;
q.g = (gsum += p.g - fv.g) * avg;
q.b = (bsum += p.b - fv.b) * avg;
// q.a = (asum += p.a - fv.a) * avg;
}
for(int j = r + 1; j < sz.cx - r; j++) {
const RGBA& p = src[i][ri++];
const RGBA& q = src[i][li++];
RGBA& t = tmp[i][ti++];
t.r = (rsum += p.r - q.r) * avg;
t.g = (gsum += p.g - q.g) * avg;
t.b = (bsum += p.b - q.b) * avg;
// t.a = (asum += p.a - q.a) * avg;
}
for(int j = sz.cx - r; j < sz.cx ; j++) {
const RGBA& p = src[i][li++];
RGBA& q = tmp[i][ti++];
q.r = (rsum += lv.r - p.r) * avg;
q.g = (gsum += lv.g - p.g) * avg;
q.b = (bsum += lv.b - p.b) * avg;
// q.a = (bsum += lv.a - p.a) * avg;
}
}
for(int i = 0; i < sz.cx; i++) {
int ti = 0;
int li = ti;
int ri = r;
const RGBA& fv = tmp[ti][i];
const RGBA& lv = tmp[sz.cy - 1][i];
dword rsum = fv.r * (r + 1);
dword gsum = fv.g * (r + 1);
dword bsum = fv.b * (r + 1);
// dword asum = fv.a * (r + 1);
for(int j = 0; j < r; j++) {
const RGBA& p = tmp[j][i];
rsum += p.r;
gsum += p.g;
bsum += p.b;
// asum += p.a;
}
for(int j = 0; j <= r; j++) {
const RGBA& p = tmp[ri++][i];
RGBA& q = out[ti++][i];
q.r = (rsum += p.r - fv.r) * avg;
q.g = (gsum += p.g - fv.g) * avg;
q.b = (bsum += p.b - fv.b) * avg;
// q.a = (asum += p.a - fv.a) * avg;
}
for(int j = r + 1; j < sz.cy - r; j++) {
const RGBA& p = tmp[ri++][i];
const RGBA& q = tmp[li++][i];
RGBA& t = out[ti++][i];
t.r = (rsum += p.r - q.r) * avg;
t.g = (gsum += p.g - q.g) * avg;
t.b = (bsum += p.b - q.b) * avg;
// t.a = (asum += p.a - q.a) * avg;
}
for(int j = sz.cy - r; j < sz.cy; j++) {
const RGBA& p = tmp[li++][i];
RGBA& q = out[ti++][i];
q.r = (rsum += lv.r - p.r) * avg;
q.g = (gsum += lv.g - p.g) * avg;
q.b = (bsum += lv.b - p.b) * avg;
// q.a = (asum += lv.a - p.a) * avg;
}
}
out.SetHotSpots(src);
out.SetResolution(src.GetResolution());
return out;
};
if(radius < 1 || IsNull(img))
return img;
Vector<int> boxes = GetBoxes(radius, 3);
Image pass1 = ApplyBoxBlur(img, (boxes[0] - 1) / 2);
Image pass2 = ApplyBoxBlur(pass1, (boxes[1] - 1) / 2);
Image output = ApplyBoxBlur(pass2, (boxes[2] - 1) / 2);
return pick(output);
}
If you think that it needs more refining, let me know, or feel free to modify it as it suits your needs.
Note: It should be easy to add a MT variant, using CoDo if needed.
Best regards,
Oblivion
Github page: https://github.com/ismail-yilmaz
upp-components: https://github.com/ismail-yilmaz/upp-components
Bobcat the terminal emulator: https://github.com/ismail-yilmaz/Bobcat
[Updated on: Sat, 18 April 2020 10:27] Report message to a moderator
|
|
|
|
|
|
Re: [Proposal] Blur algorithm (fast box blur with gaussian approximation) [message #53657 is a reply to message #53655] |
Sun, 19 April 2020 10:13 |
Oblivion
Messages: 1143 Registered: August 2007
|
Senior Contributor |
|
|
Hello Mirek,
The problem wasn't in the blur code after all.
It turned out that background of the image viewer I wrote to test the code wasn't correctly initialized/cleaned/painted.
So the images with premultiplied alpha channel were interacting with the "unclean" background, resulting in artifacts
Not to mention I accidentally added a blue channel's sum to the alpha channel in the first code (It was an exhausting week, sorry. )
Now I can confirm that below code works for images with premultiplied alpha channel.
Image Blur(const Image& img, int radius)
{
// This code is adapted from Ivan Kutskir's fast blur implementation, published under MIT license.
// See: http://blog.ivank.net/fastest-gaussian-blur.html
// An implementation of well known fast box and gaussian blur
// approximation algorithms by Wojciech Jarosz and Peter Kovesi.
// See: https://elynxsdk.free.fr/ext-docs/Blur/Fast_box_blur.pdf
// See: https://www.peterkovesi.com/papers/FastGaussianSmoothing.pdf
auto GetBoxes = [](int sigma, int n) -> Vector<int>
{
double wl = ffloor(sqrt((12 * sqr(sigma) / n) + 1));
if(fmod(wl, 2) == 0) wl--;
double wu = wl + 2;
double m = fround((12 * sqr(sigma) - n * sqr(wl) - 4 * n * wl - 3 * n) / (-4 * wl - 4));
Vector<int> sizes;
for (int i = 0; i < n; i++)
sizes.Add(i < m ? wl : wu);
return pick(sizes);
};
auto ApplyBoxBlur = [] (const Image& src, int r) -> Image
{
double avg = 1.0 / (r + r + 1);
Size sz = src.GetSize();
ImageBuffer tmp(sz);
ImageBuffer out(sz);
for(int i = 0; i < sz.cy; i++) {
int ti = 0;
int li = ti;
int ri = r;
const RGBA& fv = src[i][0];
const RGBA& lv = src[i][sz.cx - 1];
dword rsum = fv.r * (r + 1);
dword gsum = fv.g * (r + 1);
dword bsum = fv.b * (r + 1);
dword asum = fv.a * (r + 1);
for(int j = 0; j < r; j++) {
const RGBA& p = src[i][j];
rsum += p.r;
gsum += p.g;
bsum += p.b;
asum += p.a;
}
for(int j = 0; j <= r; j++) {
const RGBA& p = src[i][ri++];
RGBA& q = tmp[i][ti++];
q.r = (rsum += p.r - fv.r) * avg;
q.g = (gsum += p.g - fv.g) * avg;
q.b = (bsum += p.b - fv.b) * avg;
q.a = (asum += p.a - fv.a) * avg;
}
for(int j = r + 1; j < sz.cx - r; j++) {
const RGBA& p = src[i][ri++];
const RGBA& q = src[i][li++];
RGBA& t = tmp[i][ti++];
t.r = (rsum += p.r - q.r) * avg;
t.g = (gsum += p.g - q.g) * avg;
t.b = (bsum += p.b - q.b) * avg;
t.a = (asum += p.a - q.a) * avg;
}
for(int j = sz.cx - r; j < sz.cx ; j++) {
const RGBA& p = src[i][li++];
RGBA& q = tmp[i][ti++];
q.r = (rsum += lv.r - p.r) * avg;
q.g = (gsum += lv.g - p.g) * avg;
q.b = (bsum += lv.b - p.b) * avg;
q.a = (asum += lv.a - p.a) * avg;
}
}
for(int i = 0; i < sz.cx; i++) {
int ti = 0;
int li = ti;
int ri = r;
const RGBA& fv = tmp[ti][i];
const RGBA& lv = tmp[sz.cy - 1][i];
dword rsum = fv.r * (r + 1);
dword gsum = fv.g * (r + 1);
dword bsum = fv.b * (r + 1);
dword asum = fv.a * (r + 1);
for(int j = 0; j < r; j++) {
const RGBA& p = tmp[j][i];
rsum += p.r;
gsum += p.g;
bsum += p.b;
asum += p.a;
}
for(int j = 0; j <= r; j++) {
const RGBA& p = tmp[ri++][i];
RGBA& q = out[ti++][i];
q.r = (rsum += p.r - fv.r) * avg;
q.g = (gsum += p.g - fv.g) * avg;
q.b = (bsum += p.b - fv.b) * avg;
q.a = (asum += p.a - fv.a) * avg;
}
for(int j = r + 1; j < sz.cy - r; j++) {
const RGBA& p = tmp[ri++][i];
const RGBA& q = tmp[li++][i];
RGBA& t = out[ti++][i];
t.r = (rsum += p.r - q.r) * avg;
t.g = (gsum += p.g - q.g) * avg;
t.b = (bsum += p.b - q.b) * avg;
t.a = (asum += p.a - q.a) * avg;
}
for(int j = sz.cy - r; j < sz.cy; j++) {
const RGBA& p = tmp[li++][i];
RGBA& q = out[ti++][i];
q.r = (rsum += lv.r - p.r) * avg;
q.g = (gsum += lv.g - p.g) * avg;
q.b = (bsum += lv.b - p.b) * avg;
q.a = (asum += lv.a - p.a) * avg;
}
}
out.SetHotSpots(src);
out.SetResolution(src.GetResolution());
return out;
};
if(radius < 1 || IsNull(img))
return img;
Vector<int> boxes = GetBoxes(radius, 3);
Image pass1 = ApplyBoxBlur(img, (boxes[0] - 1) / 2);
Image pass2 = ApplyBoxBlur(pass1, (boxes[1] - 1) / 2);
Image output = ApplyBoxBlur(pass2, (boxes[2] - 1) / 2);
return pick(output);
}
Best regards,
Oblivion
Github page: https://github.com/ismail-yilmaz
upp-components: https://github.com/ismail-yilmaz/upp-components
Bobcat the terminal emulator: https://github.com/ismail-yilmaz/Bobcat
[Updated on: Sun, 19 April 2020 10:32] Report message to a moderator
|
|
|
|
Re: [Proposal] Blur algorithm (fast box blur with gaussian approximation) [message #53662 is a reply to message #53660] |
Sun, 19 April 2020 18:06 |
Oblivion
Messages: 1143 Registered: August 2007
|
Senior Contributor |
|
|
Hello Mirek,
Quote:In the end, I have renamed to GaussianBlur and done some code simplification. Also added "co" parameter for parallel processing, although it does not show much perforamnce gains (less than 70% speedup on 2700x).
Thanks!
I've used the upptst/blur code and the supplied image, and other images that I have, on one of my machines with AMD FX6100, six-core processor, and I got ~3x (2.74) speedup (consistently).
While not spectacular, it is nice and noticable.
I'll test it further...
By the way, that CoFor looks like a really handy addition.
Best regards,
Oblivion
Github page: https://github.com/ismail-yilmaz
upp-components: https://github.com/ismail-yilmaz/upp-components
Bobcat the terminal emulator: https://github.com/ismail-yilmaz/Bobcat
[Updated on: Sun, 19 April 2020 18:07] Report message to a moderator
|
|
|
|
|
Goto Forum:
Current Time: Fri Dec 13 23:08:44 CET 2024
Total time taken to generate the page: 0.02121 seconds
|
|
|