• Anmelden

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

1

Mittwoch, 14. Juli 2021, 20:56

Blur/Unschärfe – Verbesserungsvorschläge

Hallo,

derzeit versuche ich, eine im Maker berechnete Pseudo-Tiefenunschärfe (DOF) zu realisieren. Den Algorithmus, eine einfache Variante des Box blur, habe ich mir nach etwas Recherche selbst zusammengeschustert, mit dem Ergebnis bin ich auch absolut glücklich, allerdings – Maker-Veteranen werden es schon ahnen – frisst das ganze eine knackige Menge CPU-Zeit. Ein Ansatz meinerseits war, die ursprüngliche Bitmap erst einmal auf ein Viertel (halbe Breite, halbe Höhe) mittels stretch_blt zu schrumpfen, dann dort blur anzuwenden und das Ergebnis wieder auf die Ausgangsgröße zu ziehen. Auch hier ist das Ergebnis zufriedenstellend, aber ein Geschmäckle durch die doppelt großen Pixel bleibt – blur auf der vollen Auflösung schaut doch ein wenig feiner aus.
Es geht in meinem Fall im Wesentlichen um Pictures mit meist üppigem transparenten Rand (wie angehängt), hier habe ich schon selbst versucht anzusetzen und mit einem Überspringen der gänzlich transparenten Pixel experimentiert (siehe die auskommentierten "speedhacks"); es geht damit deutlich flotter, aber bei meinem, zugegeben simplen Ansatz werden die Kanten der Bilder nicht sauber in die Transparenz verblendet.

Spoiler: Code

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Bitmap
  # usage: RPG::Cache.picture("filename").blur
  def blur
    # create a colormap of the bitmap for faster access
    colormap = Hash.new
    for x in 0..width
      for y in 0..height
        colormap[[x,y]] = get_pixel(x,y)
      end
    end
    # actual box blur
    for x in 0..width
      for y in 0..height
        # sum of colors around position [x,y]
        # speedhack
        #next if colormap[[x,y]].alpha.zero?
        clr = [0,0,0,0]
        for i in -1..1
          for j in -1..1
            pxl = colormap[[x-i,y-j]]
            # compensate for areas outside the image
            pxl = Color.new(0,0,0,0) if pxl.nil?
            # speedhack
            #next if pxl.alpha.zero?
            # squaring each RGB value to approximate linear color space
            clr[0] += pxl.red**2
            clr[1] += pxl.green**2
            clr[2] += pxl.blue**2
            clr[3] += pxl.alpha
          end
        end
        # averaging and applying square root to return to sRGB
        fill_rect(x, y, 1, 1, Color.new( 
        Math.sqrt(clr[0]/9),Math.sqrt(clr[1]/9),Math.sqrt(clr[2]/9),clr[3]/9))
      end
    end
  end  
end
zum Lesen den Text mit der Maus markieren

Das ganze ist, natürlich, nicht als Echtzeit-Effekt gedacht, sonders soll während eines Mapwechsels stattfinden, quasi als Ladezeit; im Projekt selbst benutze ich nicht die ursprüngliche Game.exe, sondern mkxp, ein mittlerweile eingestelltes Projekt, das die Engine des RMXP zeitgenössisch nachbildet und sie dabei, unbeabsichtigt, deutlich flotter macht.

Für Vorschläge, wie man den ursprünglichen Code optimieren könnte, Anregungen oder gar einen ganz anderen, besseren Code, wäre ich ausgesprochen dankbar.

Liebe Grüße, Shabz
»Shabraxxx« hat folgendes Bild angehängt:
  • Eiland.png
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

2

Donnerstag, 15. Juli 2021, 20:49

Endlich wieder ein RGSS-Problem, auf diese Nadel hat meine Armbeuge bestimmt 2 Jahre warten müssen. :golly:

Sag mal in Zeile 6 und 7, müssten da nicht drei Punkte stehen? Die Schleifen sollen doch nur bis (width-1) gehen. Und das erreichst Du durch drei Punkte bei der Range, statt zweien.
Das ist dir vermutlich nicht aufgefallen, weil mkxp bei Pixel-Koordinaten außerhalb der Bitmap einfach Color.new(0,0,0,0) zurück liefert, wie ich meine im Quellcode auf GitHub zu erkennen.

Leider geht es in deinem Setting wohl gar nicht viel schneller. Du kannst ein paar Sachen vielleicht optimieren. Du kannst probieren aus dem color map Hash einen eindimensionalen Array zu machen, aber viel Geschwindigkeit bringt das vermutlich nicht. Oder in Zeile 5 bei Hash.new einen default-Wert zu vergeben, statt unten die compensate-if-Abfrage. Du könntest statt fill_rect, set_pixel benutzen, was schneller ist bei einem einzelnen Pixel, aber wohl nicht viel schneller am Ende.

Nur so aus Spaß, um mal wieder Code zu schreiben: Die Idee ist, wir "padden" unsere Color-Map, dass außen ein schwarzer Rand ist. Das Padding ersetzt deine Kompensation eines negativen Index. Dann machen wir aus der zweidimensionalen Map einen eindimensionalen, sehr langen String, indem wir die Zeilen von oben nach unten uns schnappen und hintereinander packen. Schließlich füllen wir (mit 1 Pixel Abstand zum Rand) unseren langen String mit den Farbwerten:

Ruby Quellcode

5
6
7
8
9
10
11
12
13
14
  byte_array = "\0\0\0\0" * ((width+2) * (height+2)) # padded size (+1 left, +1 right, +1 top, +1 bottom)
  for x in 1...width # starting inside the padding
    for y in 1...height
      pixel = get_pixel(x-1, y-1) # pixel data is taken from un-padded image
      byte_array[x * 4 +0 + y*w] = pixel.blue.chr # bgra, because :)
      byte_array[x * 4 +1 + y*w] = pixel.green.chr
      byte_array[x * 4 +2 + y*w] = pixel.red.chr
      byte_array[x * 4 +3 + y*w] = pixel.alpha.chr
    end
  end

und dann könntest Du in deiner i-j-Schleife per Index darauf zugreifen:

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
      for i in -1..1
        for j in -1..1
          source_x = (1 + x-i) * 4
          source_y = (1 + y-j) * width
          pointer = source_x + source_y
 
          clr[0] += byte_array[pointer+2].ord**2 # reverse bgra back to rgba, why not?
          clr[1] += byte_array[pointer+1].ord**2
          clr[2] += byte_array[pointer+0].ord**2
          clr[3] += byte_array[pointer+3].ord

So ungefährt. Habe es natürlich nicht ausprobiert. :hi:
aber ich glaube auch nicht, dass es dein Geschwindigkeitproblem löst.

Kompilierst Du dir mkxp selbst oder hast Du dir irgendwoher einmal die kompilierten Dateien gezogen und nie wieder angerührt? Ich frage, weil soweit ich sehe mkxp eine Blur-Methode selber in der C++ Implementierung hat, aber scheinbar nicht in Ruby zur Verfügung stellt. Du könntest mal ausprobieren dir selber ein Binding (basierend auf den anderen Binding [MRB_METHOD + mrb_define_method]) zu schreiben und mkxp mal zu kompilieren und es einfach zu testen.
Das wäre sonst auch mein Vorschlag, wenn es fixer gehen soll: es im C++ Quellcode von mkxp zu implementieren, statt im Ruby-Code.

Andere Varianten die mir einfielen würden Win32API-Calls voraussetzen, die mkxp ja nicht kann.
Es gibt von poccil eine DLL mit den RGSS2-Bitmap-Funktionen. Die implementiert auch eine Blur-Funktion als DLL-Aufruf über die WinAPI. Irgendwo in diesem Forum fliegt die DLL bestimmt noch rum, wenn Du sie nicht mehr hast. Ansonsten mal bei HBGames gucken. Und schließlich könntest Du noch händisch in Ruby etwas bauen, wo Du dir die rohen Bits (color data) der Bitmap aus dem Speicher ziehst und direkt darauf arbeitest - aber auch, eine auf RGSS zugeschnittene Lösung, die mit mkxp nicht klappt, da Bitmaps dort anders intern funktionieren.

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

3

Donnerstag, 15. Juli 2021, 22:15

Hey Playm,

manche Dinge ändern sich eben nie. Hau dir den Stoff rein.

Sieh an, das mit den Punkten wusste ich gar nicht, für mich sah eine for-Schleife im Maker schon immer eben so aus. Was genau ist der Unterschied? Nimmt er den letzten Wert dann nicht mehr mit?
Dein Code sieht, wenig verwunderlich, spannender und professioneller aus als meiner, ich habe auch versucht, ihn zu implementieren (mit dem Verstehen habe ich mich erst einmal zurückgehalten). Vorm chr musste der Farbwert noch zum Integer gemacht werden, das habe ich selbst noch hinbekommen, aber was soll die Variable w ab Zeile neun? Ich habe sie, als Schuss ins Blaue, je mit width, 4 oder 1 ersetzt, aber bei allen kommt ein verzerrtes, nicht aber ein verwaschenes Bild heraus.
Die Methode als solche scheint jedoch deutlich flotter zu sein, um 2,5 Sekunden im Vergleich zu 3,4 bei meiner Testgrafik.

Zu deiner Frage; ich nutze die letzte offiziell von Ancurio kompilierte Windows-Variante von 2019. Dinge selbst zu kompilieren oder gar am eigentlichen Quellcode herumzuschrauben ist noch immer über 9000 zu hoch für mich – und das wird wohl auch so bleiben. Aber ja, mkxp kann von Haus aus schon einige ziemlich feine Sachen, das habe sogar ich verstanden.

Dass ich auf die Win32APIs verzichten muss, ist schade, aber der Performancegewinn von mkxp ist einfach phänomenal. Es gibt mkxp-z, eine Fork, die wieder APIs integriert, aber in meinen Versuchen war die Performance für meine Bedürfnisse dort auch deutlich näher am originalen XP.

Gruß, Shabz
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

4

Donnerstag, 15. Juli 2021, 23:25

Zitat

manche Dinge ändern sich eben nie. Hau dir den Stoff rein.
#geilon, wie ein alter Freund von mir zu sagen pflegte

Zitat

Sieh an, das mit den Punkten wusste ich gar nicht, für mich sah eine for-Schleife im Maker schon immer eben so aus.
Als ob. Hundert pro haben wir das schon mal thematisiert. :-P

Ruby Quellcode

1
2
3
for i in 0..3
  print(i)
end

produziert die Folge "0", "1", "2", "3"
während die minimale Änderung

Ruby Quellcode

1
2
3
for i in 0...3
  print(i)
end

produziert die Folge "0", "1", "2"

Zitat

Dein Code sieht, wenig verwunderlich, spannender und professioneller aus als meiner
Das Gras auf meiner Seite des Zauns ist auch bestimmt grüner als deins. ;-) Dein initialer Code sah doch schon sehr cool aus und hat ja auch prima funktioniert. Quadrieren war eine nette Idee von dir für die Farbwerte.

Zitat

aber was soll die Variable w ab Zeile neun? Ich habe sie, als Schuss ins Blaue, je mit width, 4 oder 1 ersetzt, aber bei allen kommt ein verzerrtes, nicht aber ein verwaschenes Bild heraus.

Das "w" hat die Länge einer Zeile im mit padding versehenen Bild bzw. String-Array gespeichert. Die hatte ich aber falsch gespeichert, deswegen war das Bild vermutlich verzerrt. Entsprechend muss auch später bei source_y eine andere Formel verwendet werden.

  • Ruby Quellcode

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
      byte_array = "\0\0\0\0" * ((width+2)*(height+2)) # padded size (+1 left, +1 right, +1 top, +1 bottom)
      row_length = (width+2)*4
      for x in 1...width # starting inside the padding with x=1 instead of x=0 (border pixel)
        for y in 1...height
          pixel = image[x-1,y-1]
          byte_array[x * 4 +0 + y*row_length] = pixel.blue.chr # bgra, because :)
          byte_array[x * 4 +1 + y*row_length] = pixel.green.chr
          byte_array[x * 4 +2 + y*row_length] = pixel.red.chr
          byte_array[x * 4 +3 + y*row_length] = pixel.alpha.chr
        end
      end

  • Ruby Quellcode

    1
    2
    
              source_x = (1+x-i) * 4
              source_y = (1+y-j) * ((width+2) * 4) # row length



Mir was übrigens noch eine Idee gekommen: Dein Speedhack könntest Du noch mal probieren, allerdings ein paar Zeilen tiefer erst:

Ruby Quellcode

1
2
3
4
        # averaging and applying square root to return to sRGB
        next if clr[3] < 144 # if the calculated color has a very low opacity (a/9 < 16) we do not touch this pixel and skip the square root
        fill_rect(x, y, 1, 1, Color.new( 
        Math.sqrt(clr[0]/9),Math.sqrt(clr[1]/9),Math.sqrt(clr[2]/9),clr[3]/9))

vielleicht bringt's ein kleines bisschen was, ohne die Qualität der Grafik zu schmälern

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

5

Freitag, 16. Juli 2021, 09:46

Es war mir gar nicht klar, dass Du überhaupt Gras hast. 420 und so.

Playm, danke mal wieder, auch nach Jahren, für einen feinen Denkanstoß. Dass man die transparenten Pixel links liegen lassen kann, war mir irgendwo klar, aber das erst vor der Wurzel und dem sicher auch CPU-hungrigen fill_rect zu machen, darauf kam ich nicht.
Habe mich nun daran gesetzt und bin mit deinen Ideen auf ein paar kleine, aber wirksame Verbesserungen gekommen – mein Code ist nun tatsächlich dass ich das noch erleben darf ein gutes Stückchen flotter als deiner – das Testbild benötigt bei mir nun bloß noch satte 2,25 Sekunden im Mittel. Die colormap hat nun auch die Bereiche außerhalb der Bitmap nativ dabei, das frisst kaum mehr Zeit, aber ich kann die Abfrage, ob colormap an der Stelle nil sei, weglassen, und das beschleunigt den Prozess deutlich. Stattdessen lasse ich nun einmal zu Beginn der Box-Schleife nachschauen, ob der Pixel transparent ist, ihn dann sofort als Schwarz (0,0,0,0) deklarieren und gehe zum nächsten, wenn dem nicht so ist, wird der Alphawert weitergegeben und die Farbwerte ausgelesen. Dank dir steht nun vor dem eigentlichen Überschreiben des Pixels eine zweite Abfrage, ob auch der unscharfe Pixel vollkommen transparent wäre – wenn ja, muss er es laut Algorithmus auch vorher schon gewesen sein, und wir können uns diesen nächsten Schritt, inklusive Wurzeln, sparen.
Mich selbst stört jetzt nur noch die umständliche Range bei der colormap. Wie könnte ich dem Hash denn einen defaulten Wert zuweisen, um das zu umgehen?

Anbei mein aktueller Code und was ich aus deinem verwurstet habe.

Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class Bitmap
 
  def blur
    # create a colormap of the bitmap for faster access
    colormap = Hash.new
    for x in -1..width + 1
      for y in -1..height + 1
        colormap[[x,y]] = get_pixel(x,y)
      end
    end
    # actual box blur
    for x in 0..width
      for y in 0..height
        # sum of colors around position [x,y]
        clr = [0,0,0,0]
        for i in -1..1
          for j in -1..1
            pxl = colormap[[x-i,y-j]]
            # skip fully transparent pixels
            pxl.alpha.zero? ? next : clr[3] += pxl.alpha
            # squaring each RGB value to approximate linear color space
            clr[0] += pxl.red**2
            clr[1] += pxl.green**2
            clr[2] += pxl.blue**2
          end
        end
        # skip fully transparent pixels
        next if clr[3].zero?
        # averaging and applying square root to return to sRGB
        fill_rect(x, y, 1, 1, Color.new(
        Math.sqrt(clr[0]/9),Math.sqrt(clr[1]/9),Math.sqrt(clr[2]/9),clr[3]/9))
      end
    end
  end
 
  def blur_playm
    # create a colormap of the bitmap for faster access
    byte_array = "\0\0\0\0" * ((width+2)*(height+2)) # padded size (+1 left, +1 right, +1 top, +1 bottom)
    row_length = (width+2)*4
    for x in 1...width # starting inside the padding with x=1 instead of x=0 (border pixel)
      for y in 1...height
        pixel = get_pixel(x-1,y-1)
        byte_array[x * 4 +0 + y*row_length] = pixel.blue.to_i.chr # bgra, because :)
        byte_array[x * 4 +1 + y*row_length] = pixel.green.to_i.chr
        byte_array[x * 4 +2 + y*row_length] = pixel.red.to_i.chr
        byte_array[x * 4 +3 + y*row_length] = pixel.alpha.to_i.chr
      end
    end
    # actual box blur
    for x in 0..width
      for y in 0..height
        # sum of colors around position [x,y]
        clr = [0,0,0,0]
        for i in -1..1
          for j in -1..1
          source_x = (1+x-i) * 4
          source_y = (1+y-j) * ((width+2) * 4) # row length
            pointer = source_x + source_y
            next if byte_array[pointer].nil?
            next if byte_array[pointer+3].ord.zero?
            clr[0] += byte_array[pointer+2].ord**2 # reverse bgra back to rgba, why not?
            clr[1] += byte_array[pointer+1].ord**2
            clr[2] += byte_array[pointer].ord**2
            clr[3] += byte_array[pointer+3].ord
          end
        end
        # averaging and applying square root to return to sRGB
        next if clr[3].zero?
        fill_rect(x, y, 1, 1, Color.new( 
        Math.sqrt(clr[0]/9),Math.sqrt(clr[1]/9),Math.sqrt(clr[2]/9),clr[3]/9))
      end
    end
  end
 
end
zum Lesen den Text mit der Maus markieren

Gruß, Shabz
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

6

Freitag, 16. Juli 2021, 13:26

Zitat

Es war mir gar nicht klar, dass Du überhaupt Gras hast.

Blaze it, Brudi :boss:

Zitat

und dem sicher auch CPU-hungrigen fill_rect

Ich glaube immer noch nicht daran, dass ein fill_rect mit Rechteckgröße von 1 schneller sein soll, als ein set_pixel. :kA:

Zitat

Mich selbst stört jetzt nur noch die umständliche Range bei der colormap. Wie könnte ich dem Hash denn einen defaulten Wert zuweisen, um das zu umgehen?

Ach Shabz, steht doch alles in Scientia. Vergangenheits-Playm regelt.

Ruby Quellcode

5
6
7
8
9
10
11
12
13
    colormap = Hash.new( Color.new(0,0,0,0) )
    for x in 0...width
      for y in 0...height
        colormap[[x,y]] = get_pixel(x,y)
      end
    end
 
    print( colormap[[-1,-1]] ) # gibt einen schwarzen Pixel zurück
    print( colormap[[width+1,height+1]] ) # gibt auch einen schwarzen Pixel zurück


Zitat

Playm, danke mal wieder, auch nach Jahren, für einen feinen Denkanstoß.
Als wäre keine Sekunde seit damals vergangen :kumpel:

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

7

Freitag, 16. Juli 2021, 15:46

Auf Playm ist Verlass, in wirklich jeder Zeitlinie.

Tatsächlich scheint das einer der vielen Quirks von mkxp zu sein, dort geht fill_rect ein gutes Stück schneller als set_pixel, in der nativen Game.exe ist es, wie von dir vorhergesagt, genau anders herum – allerdings sind dort beide Varianten deutlich langsamer als irgendeine unter mkxp.
Das hier ist nun mein vermutlich finaler Code, einzig clr als Array zu realisieren schaut für mich immer noch ein bisschen dilettantisch aus, aber da Du da bisher nichts zu gesagt hast, kann ich mich auch täuschen.

Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Bitmap
  #----------------------------------------------------------------------------
  # ** Blur
  #----------------------------------------------------------------------------
  def blur
    # create a colormap of the bitmap for faster access
    colormap = Hash.new(Color.new(0,0,0,0))
    for x in 0...width
      for y in 0...height
        colormap[[x,y]] = get_pixel(x,y)
      end
    end
    # actual box blur
    for x in 0..width
      for y in 0..height
        # sum of colors around position [x,y]
        clr = [0,0,0,0]
        for i in -1..1
          for j in -1..1
            pxl = colormap[[x-i,y-j]]
            # skip fully transparent pixels
            pxl.alpha.zero? ? next : clr[3] += pxl.alpha
            # squaring each RGB value to approximate linear color space
            clr[0] += pxl.red**2
            clr[1] += pxl.green**2
            clr[2] += pxl.blue**2
          end
        end
        # skip fully transparent pixels
        next if clr[3].zero?
        # averaging and applying square root to return to sRGB
        fill_rect(x, y, 1, 1, Color.new(
        Math.sqrt(clr[0]/9),Math.sqrt(clr[1]/9),Math.sqrt(clr[2]/9),clr[3]/9))
      end
    end
  end
end
zum Lesen den Text mit der Maus markieren

Mittlerweile brauche ich für die Testgrafik nur noch gute 2,1 Sekunden im Vergleich zu 3,4 mit dem ursprünglichen Code, und das bei einer recht geringen Auflösung.
Wo man, nach meinem überschaubaren Verständnis eventuell noch ansetzen könnte sind die Operationen im eigentlichen Überschreibungsschritt; wäre es möglich, dass es Zeit spart, wenn man das vorher über das gesamte Array laufen lässt?
Gruß und ein großes Dankeschön, Shabz
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

8

Freitag, 16. Juli 2021, 19:51

Zitat

Das hier ist nun mein vermutlich finaler Code, einzig clr als Array zu realisieren schaut für mich immer noch ein bisschen dilettantisch aus, aber da Du da bisher nichts zu gesagt hast, kann ich mich auch täuschen.

Dein Code ist wunderschön, lass dich nicht klein machen. :mkiss:

Was Du noch probieren könntest, wäre den clr-Array nur einmal vor der Schleife zu erzeugen und dann immer die vier Einträge auf Null zu setzen bei Schleifenbeginn. Da sparst Du aber auch - wenn überhaupt - wieder nur klitzekleine Millisekunden, dadurch dass Du den Array im Speicher wiederverwendest.

Ruby Quellcode

13
14
15
16
17
18
19
20
21
22
23
24
    # actual box blur
    clr = [0,0,0,0] # created only once
    for x in 0..width
      for y in 0..height
        # sum of colors around position [x,y]
        clr[0] = 0 # reset
        clr[1] = 0 # reset
        clr[2] = 0 # reset
        clr[3] = 0 # reset
        for i in -1..1
          for j in -1..1
            pxl = colormap[[x-i,y-j]]
Ansonsten ist die Idee eines Arrays hier aber nicht schlecht.
Du könntest probieren, ob es einen Effekt hat (da Du die Array-Eigenschaften gar nicht brauchst) vier einzelne Variablen color_red, color_blue, color_green, color_alpha zu verwenden, ob Ruby da noch mehr zur Laufzeit raus-optimieren kann, aber das wird dann schon langsam magisch und schwer zu erklären, warum was schneller wäre. Ich persönlich find den clr-Array voll okay.

Zitat

Wo man, nach meinem überschaubaren Verständnis eventuell noch ansetzen könnte sind die Operationen im eigentlichen Überschreibungsschritt; wäre es möglich, dass es Zeit spart, wenn man das vorher über das gesamte Array laufen lässt?

Weiß nicht, was Du genau meinst. Um welche Code-Stelle geht es?

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

9

Samstag, 17. Juli 2021, 19:57

So viel Code-Positivity!!! :heart_full: XOXO

Ich habe mal beide Ideen umgesetzt und mehrfach über die Testgrafik laufen lassen, aber jedes mal ist der Algorithmus ein paar Bruchteile langsamer als im ursprünglichen Code. Scheint fast, als käme es, kontraintuitiv – der Game.exe oder, respektive, mkxp ganz schlicht auf die Menge an Befehlen an, nicht unbedingt auf deren Komplexität. An der Stelle wollte ich auch ansetzen (das meinte ich mit dem letzten Absatz) und wenigstens die Division durch 9 schon vor fill_rect über das Array laufen lassen, aber das spart keine Zeit.
Was Zeit spart, ist eine spontane Eingebung, die ich hatte. Farbwerte werden ja, auch, wenn sie ganzzahlig sind, intern immer als Float übergeben, und Float zu potenzieren, dividieren oder gar die Wurzel zu ziehen, ist zeitaufwendig – also habe ich alle Farbwerte als ersten Schritt in Integer umgewandelt.

Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
            pxl = colormap[[x-i,y-j]]
            # skip fully transparent pixels
            pxl.alpha.zero? ? next : clr[3] += pxl.alpha.to_i
            # squaring each RGB value to approximate linear color space
            clr[0] += pxl.red.to_i**2
            clr[1] += pxl.green.to_i**2
            clr[2] += pxl.blue.to_i**2
zum Lesen den Text mit der Maus markieren

Diese simple Änderung hat bei der Testgrafik nochmal 100 Millisekunden abgesäbelt; das Ergebnis ist natürlich durch initiale Rundungsfehler bei der Division durch 9 nicht exakt dasselbe, aber die Unterschiede (im Anhang eine Überlagerung mit difference blending in GIMP, allerdings mit massivem Kontrast) sind absolut marginal.

Seit meiner ersten Version haben wir die Laufzeit (bei einer, nochmal, geringen Auflösung) also schon ein gutes Drittel runtergemeddelt. #geilon
Hast Du noch eine ultima ratio, den Code irgendwie zu optimieren? to_int erst nach der Addition aller Werte über das Array laufen zu lassen, kam mir schon in den Sinn, aber das ist genauso fruchtlos wie mit Division oben.
Gruß, Shabz
»Shabraxxx« hat folgendes Bild angehängt:
  • difference.png
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

10

Sonntag, 18. Juli 2021, 00:56

o scheiße, wieder die Nächte durch machen, um RGSS-Probleme zu lösen. Es geht wieder los, Freunde. :hiphop:

Zitat

Was Zeit spart, ist eine spontane Eingebung, die ich hatte. Farbwerte werden ja, auch, wenn sie ganzzahlig sind, intern immer als Float übergeben, und Float zu potenzieren, dividieren oder gar die Wurzel zu ziehen, ist zeitaufwendig – also habe ich alle Farbwerte als ersten Schritt in Integer umgewandelt.
Shabz ist sehr kluk.

Zitat

Seit meiner ersten Version haben wir die Laufzeit (bei einer, nochmal, geringen Auflösung) also schon ein gutes Drittel runtergemeddelt. #geilon
So schnell und schön hat vorher noch keiner gerendert. :thumbsup:

Zitat

Hast Du noch eine ultima ratio, den Code irgendwie zu optimieren?
Ne, ich glaube wir haben langsam alles optimiert, was dein Algorithmus hergibt. Die Laufzeit hängt jetzt hauptsächlich davon ab, wie groß das Bild ist und wie oft jeder Farbwert angefasst wird.

Wenn einem Transparenzen nicht so wichtig ist (kommt auf die betreffende Grafik an), könnte man noch andere Algorithmen probieren, die aber nicht für Bilder mit Alpha-Kanal ausgelegt sind. Es gibt hier eine Variante in C geschrieben: https://github.com/francium/fast-blur/blob/master/fast_blur.c

Das sähe im RGSS dann so aus vermutlich:
Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
class Bitmap
  #----------------------------------------------------------------------
  # * swipe blur
  #----------------------------------------------------------------------
  #   each pixel is touched exactly tree times, insted of eigth times
  #   get_pixel and set_pixel is called only once per pixel
  #   algorithm allows custom blur-radius
  #   algorithm can't handle part-transparent pixels only full opaque or
  #   full transparent
  #----------------------------------------------------------------------
  def swipe_blur( radius=1 )
    size = width*height
    sums_r = Array.new(size)
    sums_g = Array.new(size)
    sums_b = Array.new(size)
    lkup_a = Array.new(size)
    for row in 0...height
      pixel = get_pixel(0,row)
      pixel = Color.new(0,0,0,0) if pixel.alpha < 1
      sums_r[calc_index(row,0,width)] = pixel.red.to_i
      sums_g[calc_index(row,0,width)] = pixel.green.to_i
      sums_b[calc_index(row,0,width)] = pixel.blue.to_i
      lkup_a[calc_index(row,0,width)] = pixel.alpha
      for col in 1...width
        pixel = get_pixel(col,row)
        pixel = Color.new(0,0,0,0) if pixel.alpha < 1
        sums_r[calc_index(row,col,width)] = pixel.red.to_i   + sums_r[calc_index(row,col-1,width)]
        sums_g[calc_index(row,col,width)] = pixel.green.to_i + sums_g[calc_index(row,col-1,width)]
        sums_b[calc_index(row,col,width)] = pixel.blue.to_i  + sums_b[calc_index(row,col-1,width)]
        lkup_a[calc_index(row,col,width)] = pixel.alpha
      end
    end
    for col in 0...width
      for row in 1...height
        sums_r[calc_index(row,col,width)] += sums_r[calc_index(row-1,col,width)]
        sums_g[calc_index(row,col,width)] += sums_g[calc_index(row-1,col,width)]
        sums_b[calc_index(row,col,width)] += sums_b[calc_index(row-1,col,width)]      
      end
    end
    # perform blur
    for row in 0...height
      for col in 0...width
        pixel_a = lkup_a[calc_index(row,col,width)]
        next if pixel_a < 1
        x_min = [col - radius, 0].max
        x_max = [col + radius, width-1].min
        y_min = [row - radius, 0].max
        y_max = [row + radius, height-1].min
        number_of_pixels = (x_max - (x_min-1)) * (y_max - (y_min-1))
        # red
        a = y_min < 1 || x_min < 1 ? 0 : sums_r[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_r[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_r[calc_index(y_max,   x_min-1, width)]
        d = sums_r[calc_index(y_max,x_max,width)]
        pixel_r = (d - (b + c - a)) / number_of_pixels
        # green
        a = y_min < 1 || x_min < 1 ? 0 : sums_g[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_g[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_g[calc_index(y_max,   x_min-1, width)]
        d = sums_g[calc_index(y_max,x_max,width)]
        pixel_g = (d - (b + c - a)) / number_of_pixels
        # blue
        a = y_min < 1 || x_min < 1 ? 0 : sums_b[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_b[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_b[calc_index(y_max,   x_min-1, width)]
        d = sums_b[calc_index(y_max,x_max,width)]
        pixel_b = (d - (b + c - a)) / number_of_pixels
        # set pixel
        set_pixel( Color.new(pixel_r, pixel_g, pixel_b, pixel_a) )
      end
    end
  end
  #----------------------------------------------------------------------
  # * swipe blur with squared sum
  #----------------------------------------------------------------------
  #   each pixel is touched exactly tree times, insted of eigth times
  #   get_pixel and set_pixel is called only once per pixel
  #   algorithm allows custom blur-radius
  #   algorithm can't handle part-transparent pixels only full opaque or
  #   full transparent
  #----------------------------------------------------------------------
  def swipe_blur_quad( radius=1 )
    size = width*height
    sums_r = Array.new(size)
    sums_g = Array.new(size)
    sums_b = Array.new(size)
    lkup_a = Array.new(size)
    for row in 0...height
      pixel = get_pixel(0,row)
      pixel = Color.new(0,0,0,0) if pixel.alpha < 1
      sums_r[calc_index(row,0,width)] = pixel.red.to_i ** 2
      sums_g[calc_index(row,0,width)] = pixel.green.to_i ** 2
      sums_b[calc_index(row,0,width)] = pixel.blue.to_i ** 2
      lkup_a[calc_index(row,0,width)] = pixel.alpha
      for col in 1...width
        pixel = get_pixel(col,row)
        pixel = Color.new(0,0,0,0) if pixel.alpha < 1
        sums_r[calc_index(row,col,width)] = pixel.red.to_i **2   + sums_r[calc_index(row,col-1,width)]
        sums_g[calc_index(row,col,width)] = pixel.green.to_i **2 + sums_g[calc_index(row,col-1,width)]
        sums_b[calc_index(row,col,width)] = pixel.blue.to_i **2  + sums_b[calc_index(row,col-1,width)]
        lkup_a[calc_index(row,col,width)] = pixel.alpha
      end
    end
    for col in 0...width
      for row in 1...height
        sums_r[calc_index(row,col,width)] += sums_r[calc_index(row-1,col,width)]
        sums_g[calc_index(row,col,width)] += sums_g[calc_index(row-1,col,width)]
        sums_b[calc_index(row,col,width)] += sums_b[calc_index(row-1,col,width)]      
      end
    end
    # perform blur
    for row in 0...height
      for col in 0...width
        pixel_a = lkup_a[calc_index(row,col,width)]
        next if pixel_a < 1
        x_min = [col - radius, 0].max
        x_max = [col + radius, width-1].min
        y_min = [row - radius, 0].max
        y_max = [row + radius, height-1].min
        number_of_pixels = (x_max - (x_min-1)) * (y_max - (y_min-1))
        # red
        a = y_min < 1 || x_min < 1 ? 0 : sums_r[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_r[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_r[calc_index(y_max,   x_min-1, width)]
        d = sums_r[calc_index(y_max,x_max,width)]
        pixel_r = (d - (b + c - a)) / number_of_pixels
        # green
        a = y_min < 1 || x_min < 1 ? 0 : sums_g[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_g[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_g[calc_index(y_max,   x_min-1, width)]
        d = sums_g[calc_index(y_max,x_max,width)]
        pixel_g = (d - (b + c - a)) / number_of_pixels
        # blue
        a = y_min < 1 || x_min < 1 ? 0 : sums_b[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_b[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_b[calc_index(y_max,   x_min-1, width)]
        d = sums_b[calc_index(y_max,x_max,width)]
        pixel_b = (d - (b + c - a)) / number_of_pixels
        # set pixel
        set_pixel( Color.new(Math.sqrt(pixel_r).to_i, Math.sqrt(pixel_g).to_i, Math.sqrt(pixel_b).to_i, pixel_a) )
      end
    end
  end
  #----------------------------------------------------------------------
  #  helper function to make above code more readable
  #----------------------------------------------------------------------
  def calc_index( row, column, width )
    return row * width + column
  end
end

Kleiner Disclaimer zu meinem Code: ich schreib den gerade auf einem Linux-Rechner ohne Maker, also programmiere ich mit der chunky_png-Bibliothek und übersetze das dann in die RGSS-Bibliothek. Kann also sein, dass ich mich manchmal bei RGSS-Methoden-Aufrufen oder Klassen vertue. :hm:
zum Lesen den Text mit der Maus markieren


Aber sobald in deiner Grafik Pixel mit Alpha-Werten ungleich 0 oder 255 auftauchen, kann das Ergebnis beliebig schlecht werden. Da kann obiger Algorithmus durch seine Vereinfachungen und Annahmen nicht mit deinem Wollmilchsau-Code mithalten. Dafür sollte er noch mal etwas schneller sein. Halt zu lasten von Transparenz-Unterstützung.

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

11

Montag, 19. Juli 2021, 14:44

Herrje.

Dass Du quasi blind codest, finde ich ausgesprochen abgefahren, besonders bei dem Ergebnis. Dein portierter Code funktioniert nämlich nach zwei kleinen Korrekturen ganz wunderbar im Maker, und ist, wie vermutet, auch ein gutes Stück flotter. Für Bilder ohne Transparenz perfekt geeignet und auch absolut deckungsgleich im Ergebnis; Wermutstropfen ist aber der Alphakanal. Meine ursprüngliche Intention war ja eine Teifenschärfe zu simulieren, und dazu gehört eben, leider, auch die leichte Transparenz an den Rändern des Objekts, das da nicht im Fokus ist – jeder, der schon einmal ein Bild geschossen hat, wird wissen, was ich meine.
Ich begreife den swipe_blue-Algorithmus nur sehr mäßig, glaubst Du denn, dass man da noch Alpha reinzaubern könnte unter Beibehalt der Schnelligkeit?

Jedenfalls: Hashtag #Liebe
Gruß, Shabz
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Shabraxxx« (20. Juli 2021, 00:07)


12

Dienstag, 20. Juli 2021, 18:51

Zitat

Dass Du quasi blind codest, finde ich ausgesprochen abgefahren

Dann wirst Du erst richtig staunen, wenn Du mich ohne meine Brille auf der Autobahn siehst :victory: Auto goes brrrrrr

Zitat

Dein portierter Code funktioniert nämlich nach zwei kleinen Korrekturen ganz wunderbar im Maker
Dann sag doch mal, was ich im Code korrigieren soll, damit der nächste Copy&Paste-Kaschper der hier durch browsed den Code direkt benutzen kann. ;)

Zitat

glaubst Du denn, dass man da noch Alpha reinzaubern könnte unter Beibehalt der Schnelligkeit?

Ja weißt Du, i hab nur 1 joke gemaked. Ich habe noch mal drüber nachgedacht und Samstagnacht dachte ich noch, dass der Alpha-Kanal anders zu behandeln sei, als die Farbkanäle. Aber eigentlich gibt es keinen Grund den anders zu behandeln. Also habe ich den einfach mal genauso wie die anderen drei Kanäle implementiert und um es mit den Worten von Todd Howard zu sagen "it just works".
Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class Bitmap
  #----------------------------------------------------------------------
  # * swipe blur with squared sum
  #----------------------------------------------------------------------
  #   each pixel is touched exactly tree times, insted of eigth times
  #   get_pixel and set_pixel is called only once per pixel
  #   algorithm allows custom blur-radius
  #----------------------------------------------------------------------
  def swipe_blur_quad( radius=1 )
    size = width*height
    sums_r = Array.new(size)
    sums_g = Array.new(size)
    sums_b = Array.new(size)
    sums_a = Array.new(size)
    for row in 0...height
      pixel = get_pixel(0,row)
      #pixel = Color.new(0,0,0,0) if pixel.alpha < 1
      sums_r[calc_index(row,0,width)] = pixel.red.to_i ** 2
      sums_g[calc_index(row,0,width)] = pixel.green.to_i ** 2
      sums_b[calc_index(row,0,width)] = pixel.blue.to_i ** 2
      sums_a[calc_index(row,0,width)] = pixel.alpha.to_i ** 2
      for col in 1...width
        pixel = get_pixel(col,row)
        #pixel = Color.new(0,0,0,0) if pixel.alpha < 1
        sums_r[calc_index(row,col,width)] = pixel.red.to_i **2   + sums_r[calc_index(row,col-1,width)]
        sums_g[calc_index(row,col,width)] = pixel.green.to_i **2 + sums_g[calc_index(row,col-1,width)]
        sums_b[calc_index(row,col,width)] = pixel.blue.to_i **2  + sums_b[calc_index(row,col-1,width)]
        sums_a[calc_index(row,col,width)] = pixel.alpha.to_i **2  + sums_a[calc_index(row,col-1,width)]
      end
    end
    for col in 0...width
      for row in 1...height
        sums_r[calc_index(row,col,width)] += sums_r[calc_index(row-1,col,width)]
        sums_g[calc_index(row,col,width)] += sums_g[calc_index(row-1,col,width)]
        sums_b[calc_index(row,col,width)] += sums_b[calc_index(row-1,col,width)]
        sums_a[calc_index(row,col,width)] += sums_a[calc_index(row-1,col,width)]
      end
    end
    # perform blur
    for row in 0...height
      for col in 0...width
        x_min = [col - radius, 0].max
        x_max = [col + radius, width-1].min
        y_min = [row - radius, 0].max
        y_max = [row + radius, height-1].min
        number_of_pixels = (x_max - (x_min-1)) * (y_max - (y_min-1))
        # alpha
        a = y_min < 1 || x_min < 1 ? 0 : sums_a[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_a[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_a[calc_index(y_max,   x_min-1, width)]
        d = sums_a[calc_index(y_max,x_max,width)]
        pixel_a = (d - (b + c - a)) / number_of_pixels
        next if pixel_a < 1
        # red
        a = y_min < 1 || x_min < 1 ? 0 : sums_r[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_r[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_r[calc_index(y_max,   x_min-1, width)]
        d = sums_r[calc_index(y_max,x_max,width)]
        pixel_r = (d - (b + c - a)) / number_of_pixels
        # green
        a = y_min < 1 || x_min < 1 ? 0 : sums_g[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_g[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_g[calc_index(y_max,   x_min-1, width)]
        d = sums_g[calc_index(y_max,x_max,width)]
        pixel_g = (d - (b + c - a)) / number_of_pixels
        # blue
        a = y_min < 1 || x_min < 1 ? 0 : sums_b[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_b[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_b[calc_index(y_max,   x_min-1, width)]
        d = sums_b[calc_index(y_max,x_max,width)]
        pixel_b = (d - (b + c - a)) / number_of_pixels
        # set pixel
        set_pixel( Color.new(Math.sqrt(pixel_r).to_i, Math.sqrt(pixel_g).to_i, Math.sqrt(pixel_b).to_i, Math.sqrt(pixel_a).to_i) )
      end
    end
  end
  #----------------------------------------------------------------------
  #  helper function to make above code more readable
  #----------------------------------------------------------------------
  def calc_index( row, column, width )
    return row * width + column
  end
end
zum Lesen den Text mit der Maus markieren




Zitat

Ich begreife den swipe_blue-Algorithmus nur sehr mäßig

Die Idee ist auf der verlinkten Github-Seite als Kommentare im Quellcode aufgeführt. Ich bin mir selber nicht ganz sicher, ob ich jedes Detail verstanden habe aber ich versuch mal zu erklären, was ich verstanden habe.

Für die Erklärung nehmen wir jetzt an, das Bild ist 8 Pixel breit und 1 Pixel hoch. Außerdem speichert es nicht RGBA sondern hat nur einen Grauwert-Kanal. Schwarz-Weißbilder sind sowieso viel schicker.
Als Beispiel stelle dir mal ein einzeiliges Schachbrett vor, also es gibt die Spalten A-H, aber nur eine Zeile. Das ist deine Bitmap, die Du bluren möchtest. Vereinfachtes Beispiel. Wir schreiben jetzt in jedes Feld des Schachbrettes einen Wert, den Farbwert des Graukanals. 0 ist Schwarz, 255 ist weiß und 128 ist so ein Mittelgrau. Im folgenden benutze ich die Großbuchstaben A-H um die einzelnen Schachbrettfelder/Pixel zu bezeichnen. A ist das Feld ganz links, der erste Pixel und H ist das Feld ganz rechts.

Quellcode

1
2
3
4
5
|----|----|----|----|----|----|----|----|
| A  | B  | C  | D  | E  | F  | G  | H  |   Spaltennamen
|----|----|----|----|----|----|----|----|
| 12 | 17 | 20 | 9  | 30 | 12 | 28 | 17 |   Farbwerte
|----|----|----|----|----|----|----|----|


Möchten wir den Blur-Farbwert von Feld F wissen brauchen wir ja folgende Formel: FBlur = (E+F+G)/3 = ( 30+12+28 )/3 = 23.33
Die Idee vom Algorithmus ist die folgende: wir nehmen uns ein zweites Schachbrett (sum-Array) und speichern in jedes Feld die Summe der Felder links davon:

Quellcode

1
2
3
4
|----|-----|-------|---------|-----------|-------------|---------------|-----------------|
| A∑ | B∑  | C∑    | D∑      | E∑        | F∑          | G∑            | H∑              |
|----|-----|-------|---------|-----------|-------------|---------------|-----------------|
| A  | A+B | A+B+C | A+B+C+D | A+B+C+D+E | A+B+C+D+E+F | A+B+C+D+E+F+G | A+B+C+D+E+F+G+H |


Jetzt können wir FBlur mit hilfe der Felder dieses Schachbretts berechnen, FBlur = (G∑ - D∑)/3 = ((A+B+C+D+E+F+G) - (A+B+C+D)) / 3 = (E+F+G) / 3 = 23.33

Warum das "schneller" als der andere Algorithmus ist, ist dass sich die einzelnen XBlur-Berechnungen die Teil-Summen-Berechnung teilen. Der vorherige Blur-Algorithmus hat viele Teil-Summen jedes Mal neu berechnet, obwohl er sie bei der Berechnung angrenzender Felder/Pixel schon einmal berechnet hat. Wir sparen also pro Pixel etwa fünf Berechnungen und wenn Du ganz viele Pixel hast lohnt sich das, weil dann sparst Du ganz viel mal fünf und das ist besonders viel.

Ich glaube das ist die Idee vom Algorithmus.

Edit ach ja und "swipe blur" deswegen, weil die Herangehensweise des verwendeten Algorithmus das Konzept einer "sweep line" hat und ich mich vertippt habe, als ich "sweep" schreiben wollte.

13

Donnerstag, 22. Juli 2021, 20:47

[off topic]Hast Du zufällig die vorkompilierte mkxp.exe für Linux und Windows? (gerne beide) Die Download-Links auf Github scheinen tot.[/off topic]

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

14

Donnerstag, 22. Juli 2021, 23:13

Es ist immer wieder faszinierend, wie viel Zeit und Herzblut Du dir beim Coden nimmst, um einem die zugrundeliegenden Techniken und Ideen zu erklären. Tatsächlich kann ich dem ganz gut folgen, und schon besonders viel durch fünf hätte mir gereicht.
Vielen Dank, dass Du dich dran gesetzt hast, das ist in Sachen Laufzeit besser, als alles, was ich mir erträumt habe.

Das hier ist meine minimal reparierte Version für RGSS-Kaschper:

Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class Bitmap
  #----------------------------------------------------------------------------
  # ** Fast Box Blur by francium (https://francium.cc), custom port by Playm
  #----------------------------------------------------------------------------
  def sweep_blur(radius = 1)
    size = width * height
    sums_r = Array.new(size)
    sums_g = Array.new(size)
    sums_b = Array.new(size)
    sums_a = Array.new(size)
    for row in 0...height
      pixel = get_pixel(0,row)
      sums_r[calc_index(row,0,width)] = pixel.red.to_i**2
      sums_g[calc_index(row,0,width)] = pixel.green.to_i**2
      sums_b[calc_index(row,0,width)] = pixel.blue.to_i**2
      sums_a[calc_index(row,0,width)] = pixel.alpha.to_i
      for col in 1...width
        pixel = get_pixel(col,row)
        sums_r[calc_index(row,col,width)] = pixel.red.to_i**2   + sums_r[calc_index(row,col-1,width)]
        sums_g[calc_index(row,col,width)] = pixel.green.to_i**2 + sums_g[calc_index(row,col-1,width)]
        sums_b[calc_index(row,col,width)] = pixel.blue.to_i**2  + sums_b[calc_index(row,col-1,width)]
        sums_a[calc_index(row,col,width)] = pixel.alpha.to_i  + sums_a[calc_index(row,col-1,width)]
      end
    end
    for col in 0...width
      for row in 0...height
        sums_r[calc_index(row,col,width)] += sums_r[calc_index(row-1,col,width)]
        sums_g[calc_index(row,col,width)] += sums_g[calc_index(row-1,col,width)]
        sums_b[calc_index(row,col,width)] += sums_b[calc_index(row-1,col,width)]
        sums_a[calc_index(row,col,width)] += sums_a[calc_index(row-1,col,width)]
      end
    end
    # perform blur
    for row in 0...height
      for col in 0...width
        x_min = [col - radius, 0].max
        x_max = [col + radius, width-1].min
        y_min = [row - radius, 0].max
        y_max = [row + radius, height-1].min
        number_of_pixels = (x_max - (x_min-1)) * (y_max - (y_min-1))
        # alpha
        a = y_min < 1 || x_min < 1 ? 0 : sums_a[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_a[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_a[calc_index(y_max,   x_min-1, width)]
        d = sums_a[calc_index(y_max,x_max,width)]
        pixel_a = (d - (b + c - a)) / number_of_pixels
        #next if pixel_a < 1
        # red
        a = y_min < 1 || x_min < 1 ? 0 : sums_r[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_r[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_r[calc_index(y_max,   x_min-1, width)]
        d = sums_r[calc_index(y_max,x_max,width)]
        pixel_r = (d - (b + c - a)) / number_of_pixels
        # green
        a = y_min < 1 || x_min < 1 ? 0 : sums_g[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_g[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_g[calc_index(y_max,   x_min-1, width)]
        d = sums_g[calc_index(y_max,x_max,width)]
        pixel_g = (d - (b + c - a)) / number_of_pixels
        # blue
        a = y_min < 1 || x_min < 1 ? 0 : sums_b[calc_index(y_min-1, x_min-1, width)]
        b = y_min < 1              ? 0 : sums_b[calc_index(y_min-1, x_max,   width)]
        c = x_min < 1              ? 0 : sums_b[calc_index(y_max,   x_min-1, width)]
        d = sums_b[calc_index(y_max,x_max,width)]
        pixel_b = (d - (b + c - a)) / number_of_pixels
        # set pixel
        set_pixel(col, row, Color.new(Math.sqrt(pixel_r), Math.sqrt(pixel_g), Math.sqrt(pixel_b),pixel_a))
      end
    end
  end
  #----------------------------------------------------------------------
  #  helper function to make above code more readable
  #----------------------------------------------------------------------
  def calc_index( row, column, width )
    return row * width + column
  end
end
zum Lesen den Text mit der Maus markieren

Das einzige, was mich noch ein bisschen stört, ist der Umstand, dass bei deiner Lösung keine schwarze, transparente Fläche "außerhalb" der Bitmap angenommen wird und so die Ränder der verwaschenen Version durch einen zu hohen Alphawert nicht ganz richtig ausschauen, siehe Anhang. Ich habe da schon selbst anzusetzen versucht, aber schaue trotz deiner Erläuterung schlicht noch immer nicht so richtig durch im eigentlichen Code. Sind aber auch lange Zeilen.
Gruß, Shabz
»Shabraxxx« hat folgendes Bild angehängt:
  • img.png
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

15

Donnerstag, 22. Juli 2021, 23:28

Ruby Quellcode

26
      for row in 1...height
wieso steht da bei dir 0? Müsste es nicht eins sein? Wir fangen in der "zweiten" "Zeile" an, und schauen dann zurück in die erste Zeile (row-1). Wenn wir bei Null anfangen schauen wir durch Indexmagie ganz komisch nach. Vielleicht hat das schon einen Effekt, das mal zu korrigieren - oder muss das gar so sein? War das dein Fix?

Zitat

Das einzige, was mich noch ein bisschen stört, ist der Umstand, dass bei deiner Lösung keine schwarze, transparente Fläche "außerhalb" der Bitmap angenommen wird
das könnte man wohl im Code anpassen. Oder Du machst Paint.NET auf und fügst im Bitmap einen tatsächlichen Rand ein und der Code kann so bleiben. Darüber schon nachgedacht? :3

Zitat

Vielen Dank, dass Du dich dran gesetzt hast
ach für den kleinen Shabz mach ich es doch gerne :freunde:

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

16

Freitag, 23. Juli 2021, 21:30

Das war tatsächlich nur ein Tippfehler mit der 0, als ich einige Codestellen verändert habe, um sie vielleicht ein bisschen besser zu verstehen. Hat leider nur so mäßig funktioniert, aber tatsächlich sehe ich auf den ersten Blick keinen Unterschied im Ergebnis, auch mit dem Typo. Aber dann verstehe ich den Code ja noch immer nicht so richtig, also…

Zitat

Oder Du machst Paint.NET auf und fügst im Bitmap einen tatsächlichen Rand ein

Aber Playm, das würde mir als altem Datensparfuchs nicht recht gefallen wollen, einfach so Füllmaterial an eine PNG zu kleben, die eigentlich schon auf ihren Inhalt heruntergedampft wurde. Ich hab da mal was nei gerendert:

Spoiler

Ruby Quellcode

1
2
3
4
5
6
7
  def sweep_blur_edgesafe
    buffer = Bitmap.new(width + 2, height + 2).blt(1, 1, self, rect)
    buffer.sweep_blur
    clear
    blt(0, 0, buffer, Rect.new(1, 1, width, height))
    buffer.dispose
  end
zum Lesen den Text mit der Maus markieren

Eine sehr shabzige Herangehensweise, zugegeben, aber es dauert nur Hundertstelsekunden länger in meinen Benchmark (skaliert selbstverständlich mit den Dimensionen der vergrößerten Grafik) und das Ergebnis ist damit deckungsgleich mit meinem ursprünglichen Code. Und das mit ohne mehr Dateigröße. Ich hatte überlegt, das gleich in den Algorithmus einzubauen, um ein bisschen Zeit zu sparen, aber dann wirds langsam wirklich unübersichtlich, weil bei jedem width und jedem height ein buffer. davorstehen müsste, da wir ja eigentlich auf der Grafik arbeiten.

Spannend, wie sich das Problem entwickelt hat. Ich bin sehr glücklich mit dem Ergebnis.
Gruß, Shabz
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

17

Freitag, 23. Juli 2021, 21:49

"Datensparfuchs"
MEME
"schlägt eine Lösung vor, die mehr CPU-Speicher verbraucht als eine Diskette fasst"

~*~ just Shabz things ~*~

Zitat

Spannend, wie sich das Problem entwickelt hat. Ich bin sehr glücklich mit dem Ergebnis.

Ja mei, wenn Du zufrieden bist, kann man auch an der Stelle Feierabend machen.
Edit Hab doch noch etwas weiter geschrieben...

Man könnte die sum-Arrays jeweils um 2 in Breite und Länge erweitern

Ruby Quellcode

6
    size = (width+2) * (height+2)

und entsprechend alle x-y-Koordinaten anpassen... aber es ist tatsächlich etwas unübersichtlich. ;)
vielleicht kann man tricksen, und nur die

Ruby Quellcode

1
2
3
  def calc_index( row, column, width )
    return (row+1) * width + (column+1)
  end
und

Ruby Quellcode

36
37
38
39
        x_min = [col - radius, -1].max
        x_max = [col + radius, width-1+1].min
        y_min = [row - radius, -1].max
        y_max = [row + radius, height-1+1].min
anpassen, aber das habe ich nicht getestet. Ist ins Blaue geraten. Aber vielleicht - falls es nicht klappt - gibt es dir einen Denkanstoß in die richtige Richtung. Und falls nicht, immerhin den Post-Counter gepusht lul

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

18

Freitag, 23. Juli 2021, 22:56

Das scheint mir ein klassischer Apfel-Birne-Vergleich zu sein, und wenn nicht, dann wenigstens maßlos übertrieben. Es gibt ja auch high density.

Mit deinem Ansatz hustet er mir irgendwann in der Berechnung, dass einer der Array-Inhalte nil sei; ich habe dann ein bisschen hin und her probiert und mir einmal sogar eingeredet, ich würde begreifen, was ich da mache, ganz kurz gingen dann auch der linke und obere Rand (frag mich nicht mehr mit welchem Code), aber rechts und unten sahen noch schlechter aus. Dann habe ich wieder herumprobiert, und der Anhang kam heraus. Ich denke, man kann also getrost sagen, dass der Denkanstoß eher mäßig funktioniert hat. Aber ich schätze deinen pädagogischen Ansatz, obgleich er in meinem Kopf eben leider auf eine Sonderschulklasse trifft, die dazu noch gerade durch die Bank weg im Delirium ist.
Gruß, Shabz
»Shabraxxx« hat folgendes Bild angehängt:
  • Fluktuation 8.png
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

19

Freitag, 23. Juli 2021, 23:13

o scheiße ist ja Freitagabend, da bumst sich der Shabz ja immer die Birne zu :kumpel:

Zitat

Mit deinem Ansatz hustet er mir irgendwann in der Berechnung, dass einer der Array-Inhalte nil sei

ah ja, mit meinem Ansatz ist der äußere Rand ja gar nicht schwarz. Der Quick-Fix wäre bei der Arrayerstellung vermutlich ein {0} anzuhängen:

Ruby Quellcode

1
sums_r = Array.new(size){0}

das initialisiert den Array mit Nullen. Das löst das initiale nil-Problem.

Shabraxxx

Projekt: Ressourcenbereich & Seitenredaktion

  • »Shabraxxx« ist der Autor dieses Themas

Motto: Mein Luftkissenfahrzeug ist voller Aale.

  • Nachricht senden

20

Freitag, 23. Juli 2021, 23:54

Impliziert, dass das abhängig vom Wochentag sei. Ulu.

Mit dem Fix sind linker und oberer Rand save, aber rechts und unten unverändert kantig. Die Lösung ist so nah, aber ich bin so weit weg davon, im Kopp.
Gruß, Shabz
„Albrecht Dürer, geboren 1471, gestorben 1530.
Der Nürnberger Maler, der ganz Europa faszinierte; mit seinem scharfen Auge, seiner Meisterschaft in Linienführung und Plastizität, sowie seiner Leihwagenfirma.“

Ähnliche Themen

Social Bookmarks