oder [C#]: Fast Gaußian Blur

  • VB.NET

Es gibt 35 Antworten in diesem Thema. Der letzte Beitrag () ist von Artentus.

    oder [C#]: Fast Gaußian Blur

    Hi Leute,

    Ich suche nach einem Filter für Bitmaps (Source, Binary oder Native mit COM-Aufruf) für einen schnellen Gaußschen Weichzeichner.
    Es ist wichtig, dass dieser Filter Bilder schnell verarbeiten kann, welche größer als 8 Megapixel groß sind.

    Bisher habe ich diverse Codes in .NET und C# probiert - auch mit LockBits und C++-Codes, welche über die GPU laufen....
    Jedoch erziele ich nicht die gewünschten Ergebnisse.

    Kennt jemand vlt. eine gute Library mit API-Aufruf oder Ähnliches? :D

    Vielen Dank und viele Grüße,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.

    Wincrash schrieb:

    über die GPU laufen
    Schneller gehts nicht. Eigentlich sollte die Grafikkarte das aber locker über 1000 mal in der Sekunde schaffen, das Problem liegt dann wohl eher im Übergang zischen managed und unamanaged Kontext und in der Bitmap-Klasse.
    Hm....

    was empfiehlst du mir also?
    Soll ich nochmal nach dem C++-Code kramen und den versuchen in meinem Projekt unterzubringen?

    Viele Grüße,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.
    Die Ultra-Workaround-Methode mit GDI+:
    Lade ein Bild, verkleinere es und zeichne es anschließend wieder in der originalen Größe. Du musst etwas mit dem Interpolation Mode und dem Skalierungsfaktor rumspielen. Ist wahrscheinlich kein "originaler" Gaußscher Weichzeichner, jedoch ist der Effekt sehr ähnlich und läuft dafür recht fix.


    Hier kannst du eventuell auch noch ein paar Infos rausziehen:
    [VB.NET] [Sammelthread] Knobel-Aufgaben, knifflige Algorithmen, elegante Lösungen (bzw hier)
    Von meinem iPhone gesendet
    @Artentus: Es geht eigentlich darum zwar nichts in Echtzeit zu verarbeiten, jedoch für bestimmte visuelle Effekte ein großes Bild unscharf werden lassen.

    Ich weiß, dass ich nach viel frage, aber der Effekt sollte nicht länger als 500ms brauchen und auch einen Radius von über 100 "blurren" können :)

    @nikeee13: Ich schaus mir mal an

    Vielen Dank und Viele Grüße,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.
    @nikeee13: Vielen Dank für deinen Link, aber dieser hat mir (jetzt zumindest) nicht direkt geholfen.
    Ich habe die Methode getestet, welche du mir empfohlen hast, jedoch ist mein Ergebnis nicht glatt verwischt (ich glaube, du weißt, was ich meine :D )

    Viele Grüße,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.
    Ich denke, dass ein Gaußscher Weichzeichner bis zu 100 so gut wie unmöglich ist. Wenn es aber um das unscharf werden lassen geht (Animation?), dann fällt mir ein möglicher Trick ein: Du erhöhst immer wieder den Radius um 1, wobei du das bisherige Ergebnis und die neu dazugekommenen Pixel wieder von neu verrechnest. Dadurch müsstest du jedes Mal nur nen kleinen Teil neu berechnen. Du musst aber dann wissen, bis wie viel du gehen willst (Radius), damit du berechnen kannst, wie stark der nächste Pixel mit einbezogen werden soll.

    Wincrash schrieb:

    Es ist wichtig, dass dieser Filter Bilder schnell verarbeiten kann, welche größer als 8 Megapixel groß sind.

    Hi,

    ich spiele gerade ein bisschen an Deinem Problem herum, weil es mich auch interessiert.
    Sehr schön wäre es, wenn Du uns einen typischen Vertreter dieser Bilder zur Verfügung stellen könntest!?

    LG,
    Bruno
    @diylab: hier ist so ein Bild oder dieses Bild :D


    @Gonger96: Hm ... bei Matrizen liegt nicht direkt meine stärke^^ - Hättest du da vlt. Tutorials oder Ähnliches, welche in die Richtung gehen? :D
    @nafets3646: Ist zwar eine gute Idee, aber leider zu langsam für meine Zwecke:/

    Viele Grüße,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Wincrash“ ()

    @diylab: Naah - das ist nicht schlimm, wenn du's nicht schaffst :)
    Ich habe auch ewig lang rumprobiert - und es waren bei mir nie unter 4 Sekunden :P
    Dann hab' ich im Internet rumgesucht, bin aber wahnsinnig geworden, weil ich nie das richtige gefunden habe....

    Mach dir deswegen bloß keinen Stress,
    Vielen Dank, viele liebe Grüße und einen guten Abend noch,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.
    ~push~

    @diylab: sry dass ich drängle, aber hast du inzwischen einige (bessere) Ergebnisse erzielen können? :D

    Vielen Dank und viele Grüße,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.
    @Artentus: Ich bin zwar kein Fan von BoxBlur - im schlimmsten Falle ist das aber auch ok ^^
    @J-F: Das schaue ich mir mal an - ich sage dir auf jeden Fall mal später bescheid, inwieweit mir dein Link geholfen hat :D

    @Gonger96: Vielen Dank - ich werde mich mal damit auseinander setzen (auf den ersten Blick sieht das BoxBlur-Ähnlich aus:) )

    Edit:
    Ich habe mir den Link von J-F angeschaut und den Code auch in C# gefunden:
    Spoiler anzeigen

    C#-Quellcode

    1. public static Bitmap FastBlur(Bitmap SourceImage, int radius)
    2. {
    3. var rct = new Rectangle(0, 0, SourceImage.Width, SourceImage.Height);
    4. var dest = new int[rct.Width * rct.Height];
    5. var source = new int[rct.Width * rct.Height];
    6. var bits = SourceImage.LockBits(rct, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
    7. Marshal.Copy(bits.Scan0, source, 0, source.Length);
    8. SourceImage.UnlockBits(bits);
    9. if (radius < 1) return SourceImage;
    10. int w = rct.Width;
    11. int h = rct.Height;
    12. int wm = w - 1;
    13. int hm = h - 1;
    14. int wh = w * h;
    15. int div = radius + radius + 1;
    16. var r = new int[wh];
    17. var g = new int[wh];
    18. var b = new int[wh];
    19. int rsum, gsum, bsum, x, y, i, p1, p2, yi;
    20. var vmin = new int[max(w, h)];
    21. var vmax = new int[max(w, h)];
    22. var dv = new int[256 * div];
    23. for (i = 0; i < 256 * div; i++)
    24. {
    25. dv[i] = (i / div);
    26. }
    27. int yw = yi = 0;
    28. for (y = 0; y < h; y++)
    29. { // blur horizontal
    30. rsum = gsum = bsum = 0;
    31. for (i = -radius; i <= radius; i++)
    32. {
    33. int p = source[yi + min(wm, max(i, 0))];
    34. rsum += (p & 0xff0000) >> 16;
    35. gsum += (p & 0x00ff00) >> 8;
    36. bsum += p & 0x0000ff;
    37. }
    38. for (x = 0; x < w; x++)
    39. {
    40. r[yi] = dv[rsum];
    41. g[yi] = dv[gsum];
    42. b[yi] = dv[bsum];
    43. if (y == 0)
    44. {
    45. vmin[x] = min(x + radius + 1, wm);
    46. vmax[x] = max(x - radius, 0);
    47. }
    48. p1 = source[yw + vmin[x]];
    49. p2 = source[yw + vmax[x]];
    50. rsum += ((p1 & 0xff0000) - (p2 & 0xff0000)) >> 16;
    51. gsum += ((p1 & 0x00ff00) - (p2 & 0x00ff00)) >> 8;
    52. bsum += (p1 & 0x0000ff) - (p2 & 0x0000ff);
    53. yi++;
    54. }
    55. yw += w;
    56. }
    57. for (x = 0; x < w; x++)
    58. { // blur vertical
    59. rsum = gsum = bsum = 0;
    60. int yp = -radius * w;
    61. for (i = -radius; i <= radius; i++)
    62. {
    63. yi = max(0, yp) + x;
    64. rsum += r[yi];
    65. gsum += g[yi];
    66. bsum += b[yi];
    67. yp += w;
    68. }
    69. yi = x;
    70. for (y = 0; y < h; y++)
    71. {
    72. dest[yi] = (int)(0xff000000u | (uint)(dv[rsum] << 16) | (uint)(dv[gsum] << 8) | (uint)dv[bsum]);
    73. if (x == 0)
    74. {
    75. vmin[y] = min(y + radius + 1, hm) * w;
    76. vmax[y] = max(y - radius, 0) * w;
    77. }
    78. p1 = x + vmin[y];
    79. p2 = x + vmax[y];
    80. rsum += r[p1] - r[p2];
    81. gsum += g[p1] - g[p2];
    82. bsum += b[p1] - b[p2];
    83. yi += w;
    84. }
    85. }
    86. var bits2 = SourceImage.LockBits(rct, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
    87. Marshal.Copy(dest, 0, bits2.Scan0, dest.Length);
    88. SourceImage.UnlockBits(bits);
    89. return SourceImage;
    90. }
    91. private static int min(int a, int b) { return Math.Min(a, b); }
    92. private static int max(int a, int b) { return Math.Max(a, b); }

    Das Problem ist nur, dass das Bild nicht geblurred wird .... Ich geh' mal auf Fehlersuche ^^

    Vielen Dank und viele Grüße,
    wincrash
    (\_/) Das ist Hase.
    (O.o) Kopiere Hase in deine Signatur
    (> <) und hilf ihm so auf seinem Weg zur Weltherrschaft.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Wincrash“ ()

    @diylab
    @Wincrash
    Ihr habts mit .Net geschafft, solche Ergebnisse bei 100 Pixel Radius zu erreichen?
    Also ich habs mit Parallel.For und LockBits mal probiert und das dauert Minuten (und das nur für nen BoxBlur). Oo

    Spoiler anzeigen
    public static unsafe Bitmap ApplyBoxBlur(this Bitmap sourceImage, int radius)
    {
    int width = sourceImage.Width;
    int height = sourceImage.Height;

    var destinationImage = new Bitmap(width, height);

    var lockRect = new Rectangle(0, 0, width, height);
    BitmapData sourceData = sourceImage.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    BitmapData destinationData = destinationImage.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    Parallel.For(0, width * height, (int index) =>
    {
    int x;
    int y = Math.DivRem(index, width, out x);

    int alpha = 0;
    int red = 0;
    int green = 0;
    int blue = 0;

    int fromY = Math.Max(y - radius, 0);
    int toY = Math.Min(y + radius, height - 1);
    int fromX = Math.Max(x - radius, 0);
    int toX = Math.Min(x + radius, width - 1);
    for (int sourceY = fromY; sourceY < toY; sourceY++)
    {
    byte* color = (byte*)sourceData.Scan0 + sourceY * sourceData.Stride + fromX * 4;

    for (int sourceX = fromX; sourceX < toX; sourceX++)
    {
    alpha += color[3];
    red += color[2];
    green += color[1];
    blue += color[0];

    color += 4;
    }
    }

    int pixelCount = (toY - fromY) * (toX - fromX);
    blue /= pixelCount;
    green /= pixelCount;
    red /= pixelCount;
    alpha /= pixelCount;

    byte* destinationPointer = (byte*)destinationData.Scan0 + index * 4;
    destinationPointer[0] = (byte)blue;
    destinationPointer[1] = (byte)green;
    destinationPointer[2] = (byte)red;
    destinationPointer[3] = (byte)alpha;
    });

    sourceImage.UnlockBits(sourceData);
    destinationImage.UnlockBits(destinationData);

    return destinationImage;
    }