etuli
10.05.2006, 21:33
moin,
ich versuche seit Tagen - hin und wieder - meine Umsetzung fuer den Bildfilter zu beschleunigen. Leider mit nur sehr maessigem Erfolg. Bei einem Bild der Dimension 1600x1200 (24 bit), Radius 10 komme ich auf 16 Sekunden mit meiner besten Umsetzung, was aber voellig inakzeptabel ist! :)
Wer den Algorithmus nicht kennt, hier eine Einfuehrung:
m Gegensatz zu den anderen Weichzeichnungsfiltern wirkt der Selektive Gaußsche Weichzeichner nicht auf alle Pixel des Bildes, der Auswahl oder der aktuellen Ebene. Das Filter wirkt nur auf den Pixel, deren Farbe höchstens um einen definierten Deltawert von der Farbe der Nachbarpixel abweicht. Daher werden Kanten im Bild erhalten.
[...] Radius Hier können Sie den Radius in Pixeln angeben, der zum Berechnen des Filters verwendet wird. Dieser Wert beeinflusst massgeblich die Intensität der Wirkung.
Max. Delta Mit dieser Eigenschaft können Sie die maximale Farbdifferenz im Bereich von 0 bis 255 einstellen. Diese Farbdifferenz bestimmt welche Pixel aus der Umgebung weichgezeichnet werden. Dieser Wert beeinflusst massgeblich wie gut Kanten gegen das Weichzeichnen geschützt werden.
Sei 'src' das Eingabebild, 'dst' das Ausgabebild mit
'bytesPerPixel; bit je Pixel jeweils. Sowohl 'src',
als auch 'dst' haben eine Weite von 'width', wie eine
Hoehe von 'height' Pixeln.
maxDelta ist eine Ganzzahl aus {0,1,...,255}.
numrad ist eine Ganzzahl. Ist r der Radius, so gilt:
numrad = floor (|r| + 2)
pixel (i,x,y,b) liefere das b-te Byte des Pixels
an Stelle (x,y) aus Bild i. i ist gleich 'src', oder
'dst'. Bei einem 24 Bit Bild koennte b=0 rot sein,
b=1 gruen, und b=2 blau. Zum Beispiel. Dies spielt
allerdings keine grosse Rolle.
M sei die Koeffizientenmatrix mit 2*numrad-1 Zeilen
und Spalten. M(i,j) sei der Wert in der i-ten Zeile
und der j-ten Spalte.
Siehe unten fuer ein Beispiel der Matrix und den
zugehoerigen Algorithmus zum Erstellen einer
solchen.
Eine Implementierung des Algorithmus findet sich im
Quellcode von Gimp (plug-ings/common/selgauss.c),
welche mir als Ausgangsbasis diente.
foreach 0 <= y < height
foreach 0 <= x < width
foreach 0 <= b < bytesPerPixel
sum = 0;
fact = 0;
foreach -numrad < i < numrad
if (y + i < 0) or (y + i) >= height then
continue;
foreach -numrad < j < numrad
if (x + j < 0) or (x + j) >= width then
continue;
dist = pixel (src, x,y, b) - pixel (src, x+j,y+i, b);
if (dist > maxDelta) or (dist < -maxDelta) then
continue;
coeff = M(i,j);
sum += coeff * pixel (src, x+j,y+i, b);
fact += coeff;
if (0 == fact) then
pixel (dst, x,y, b) = pixel (src, x,y, b);
else
pixel (dst, x,y, b) = floor (sum / fact);
Der fuehrt in seiner innersten Schleife h*w*b*(2*numrad-1)^2 Schritte aus. Dies entspricht bei meiner obigen Eingabe grob 2.079e9 Iterationen. :D Zuviele!
Leider sehe ich auch keinen Ausweg fuer dieses Problem. Vielleicht ja einer von euch.
Der Algo. fuer die Initialisierung der Matrix enspr. ungefaehr jenem von oben angesprochenem Plugin (insb. bzgl. orig. Kommentaren).
int dx,dy;
double sd, c1, c2;
double ** mat;
mat = new double* [num * 2 - 1];
for (int i = 0 ; i < num * 2 - 1 ; i ++) {
mat [i] = new double [num * 2 - 1];
mat [i] += num - 1;
}
mat += num - 1;
/* This formula isn't really correct, but it'll do */
sd = radius / 3.329042969;
c1 = 1.0 / sqrt (2.0 * 3.1415927 * sd);
c2 = -2.0 * (sd * sd);
for (dy = 0 ; dy < num ; dy++) {
for (dx = dy ; dx < num ; dx++) {
double a = c1 * exp ((dx * dx + dy * dy)/ c2);
mat [ dy][ dx] = a;
mat [ dx][ dy] = a;
mat [-dy][ dx] = a;
mat [-dx][ dy] = a;
mat [-dy][-dx] = a;
mat [-dx][-dy] = a;
mat [ dy][-dx] = a;
mat [ dx][-dy] = a;
}
}
Eine Beipielauspraegung fuer numrad = 7 waere:
0.000 0.000 0.000 0.000 0.001 0.001 0.001 0.001 0.001 0.000 0.000 0.000 0.000
0.000 0.000 0.001 0.002 0.003 0.005 0.006 0.005 0.003 0.002 0.001 0.000 0.000
0.000 0.001 0.002 0.006 0.014 0.022 0.025 0.022 0.014 0.006 0.002 0.001 0.000
0.000 0.002 0.006 0.019 0.040 0.064 0.074 0.064 0.040 0.019 0.006 0.002 0.000
0.001 0.003 0.014 0.040 0.087 0.138 0.161 0.138 0.087 0.040 0.014 0.003 0.001
0.001 0.005 0.022 0.064 0.138 0.218 0.255 0.218 0.138 0.064 0.022 0.005 0.001
0.001 0.006 0.025 0.074 0.161 0.255 0.297 0.255 0.161 0.074 0.025 0.006 0.001
0.001 0.005 0.022 0.064 0.138 0.218 0.255 0.218 0.138 0.064 0.022 0.005 0.001
0.001 0.003 0.014 0.040 0.087 0.138 0.161 0.138 0.087 0.040 0.014 0.003 0.001
0.000 0.002 0.006 0.019 0.040 0.064 0.074 0.064 0.040 0.019 0.006 0.002 0.000
0.000 0.001 0.002 0.006 0.014 0.022 0.025 0.022 0.014 0.006 0.002 0.001 0.000
0.000 0.000 0.001 0.002 0.003 0.005 0.006 0.005 0.003 0.002 0.001 0.000 0.000
0.000 0.000 0.000 0.000 0.001 0.001 0.001 0.001 0.001 0.000 0.000 0.000 0.000
Gruss, Stefan
ich versuche seit Tagen - hin und wieder - meine Umsetzung fuer den Bildfilter zu beschleunigen. Leider mit nur sehr maessigem Erfolg. Bei einem Bild der Dimension 1600x1200 (24 bit), Radius 10 komme ich auf 16 Sekunden mit meiner besten Umsetzung, was aber voellig inakzeptabel ist! :)
Wer den Algorithmus nicht kennt, hier eine Einfuehrung:
m Gegensatz zu den anderen Weichzeichnungsfiltern wirkt der Selektive Gaußsche Weichzeichner nicht auf alle Pixel des Bildes, der Auswahl oder der aktuellen Ebene. Das Filter wirkt nur auf den Pixel, deren Farbe höchstens um einen definierten Deltawert von der Farbe der Nachbarpixel abweicht. Daher werden Kanten im Bild erhalten.
[...] Radius Hier können Sie den Radius in Pixeln angeben, der zum Berechnen des Filters verwendet wird. Dieser Wert beeinflusst massgeblich die Intensität der Wirkung.
Max. Delta Mit dieser Eigenschaft können Sie die maximale Farbdifferenz im Bereich von 0 bis 255 einstellen. Diese Farbdifferenz bestimmt welche Pixel aus der Umgebung weichgezeichnet werden. Dieser Wert beeinflusst massgeblich wie gut Kanten gegen das Weichzeichnen geschützt werden.
Sei 'src' das Eingabebild, 'dst' das Ausgabebild mit
'bytesPerPixel; bit je Pixel jeweils. Sowohl 'src',
als auch 'dst' haben eine Weite von 'width', wie eine
Hoehe von 'height' Pixeln.
maxDelta ist eine Ganzzahl aus {0,1,...,255}.
numrad ist eine Ganzzahl. Ist r der Radius, so gilt:
numrad = floor (|r| + 2)
pixel (i,x,y,b) liefere das b-te Byte des Pixels
an Stelle (x,y) aus Bild i. i ist gleich 'src', oder
'dst'. Bei einem 24 Bit Bild koennte b=0 rot sein,
b=1 gruen, und b=2 blau. Zum Beispiel. Dies spielt
allerdings keine grosse Rolle.
M sei die Koeffizientenmatrix mit 2*numrad-1 Zeilen
und Spalten. M(i,j) sei der Wert in der i-ten Zeile
und der j-ten Spalte.
Siehe unten fuer ein Beispiel der Matrix und den
zugehoerigen Algorithmus zum Erstellen einer
solchen.
Eine Implementierung des Algorithmus findet sich im
Quellcode von Gimp (plug-ings/common/selgauss.c),
welche mir als Ausgangsbasis diente.
foreach 0 <= y < height
foreach 0 <= x < width
foreach 0 <= b < bytesPerPixel
sum = 0;
fact = 0;
foreach -numrad < i < numrad
if (y + i < 0) or (y + i) >= height then
continue;
foreach -numrad < j < numrad
if (x + j < 0) or (x + j) >= width then
continue;
dist = pixel (src, x,y, b) - pixel (src, x+j,y+i, b);
if (dist > maxDelta) or (dist < -maxDelta) then
continue;
coeff = M(i,j);
sum += coeff * pixel (src, x+j,y+i, b);
fact += coeff;
if (0 == fact) then
pixel (dst, x,y, b) = pixel (src, x,y, b);
else
pixel (dst, x,y, b) = floor (sum / fact);
Der fuehrt in seiner innersten Schleife h*w*b*(2*numrad-1)^2 Schritte aus. Dies entspricht bei meiner obigen Eingabe grob 2.079e9 Iterationen. :D Zuviele!
Leider sehe ich auch keinen Ausweg fuer dieses Problem. Vielleicht ja einer von euch.
Der Algo. fuer die Initialisierung der Matrix enspr. ungefaehr jenem von oben angesprochenem Plugin (insb. bzgl. orig. Kommentaren).
int dx,dy;
double sd, c1, c2;
double ** mat;
mat = new double* [num * 2 - 1];
for (int i = 0 ; i < num * 2 - 1 ; i ++) {
mat [i] = new double [num * 2 - 1];
mat [i] += num - 1;
}
mat += num - 1;
/* This formula isn't really correct, but it'll do */
sd = radius / 3.329042969;
c1 = 1.0 / sqrt (2.0 * 3.1415927 * sd);
c2 = -2.0 * (sd * sd);
for (dy = 0 ; dy < num ; dy++) {
for (dx = dy ; dx < num ; dx++) {
double a = c1 * exp ((dx * dx + dy * dy)/ c2);
mat [ dy][ dx] = a;
mat [ dx][ dy] = a;
mat [-dy][ dx] = a;
mat [-dx][ dy] = a;
mat [-dy][-dx] = a;
mat [-dx][-dy] = a;
mat [ dy][-dx] = a;
mat [ dx][-dy] = a;
}
}
Eine Beipielauspraegung fuer numrad = 7 waere:
0.000 0.000 0.000 0.000 0.001 0.001 0.001 0.001 0.001 0.000 0.000 0.000 0.000
0.000 0.000 0.001 0.002 0.003 0.005 0.006 0.005 0.003 0.002 0.001 0.000 0.000
0.000 0.001 0.002 0.006 0.014 0.022 0.025 0.022 0.014 0.006 0.002 0.001 0.000
0.000 0.002 0.006 0.019 0.040 0.064 0.074 0.064 0.040 0.019 0.006 0.002 0.000
0.001 0.003 0.014 0.040 0.087 0.138 0.161 0.138 0.087 0.040 0.014 0.003 0.001
0.001 0.005 0.022 0.064 0.138 0.218 0.255 0.218 0.138 0.064 0.022 0.005 0.001
0.001 0.006 0.025 0.074 0.161 0.255 0.297 0.255 0.161 0.074 0.025 0.006 0.001
0.001 0.005 0.022 0.064 0.138 0.218 0.255 0.218 0.138 0.064 0.022 0.005 0.001
0.001 0.003 0.014 0.040 0.087 0.138 0.161 0.138 0.087 0.040 0.014 0.003 0.001
0.000 0.002 0.006 0.019 0.040 0.064 0.074 0.064 0.040 0.019 0.006 0.002 0.000
0.000 0.001 0.002 0.006 0.014 0.022 0.025 0.022 0.014 0.006 0.002 0.001 0.000
0.000 0.000 0.001 0.002 0.003 0.005 0.006 0.005 0.003 0.002 0.001 0.000 0.000
0.000 0.000 0.000 0.000 0.001 0.001 0.001 0.001 0.001 0.000 0.000 0.000 0.000
Gruss, Stefan