Zwei Sprites vergleichen

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
swissgregi
Beiträge: 16
Registriert: 31.01.2011 12:23

Zwei Sprites vergleichen

Beitrag von swissgregi »

Hallo

Ich habe zwei Grafiken.
Von diesen beiden Grafiken möchte ich von einem Bereich (z.B. 50x50 Pixel) den prozentualen Unterschied berechnen, und dies viele male.

Das funktioniert auch:

Code: Alles auswählen

InitSprite()

Global *I1Buffer, I1pitch, *I2Buffer, I2pitch

UseJPEGImageDecoder()

LoadImage(1,"CIMG2059.jpg")
StartDrawing(ImageOutput(1))
*I1Buffer = DrawingBuffer()
I1Pitch = DrawingBufferPitch()
  If Not *I1Buffer
    End
  EndIf
  StopDrawing()
  
  LoadImage(2,"CIMG2061.jpg")
StartDrawing(ImageOutput(2))
*I2Buffer = DrawingBuffer()
I2Pitch = DrawingBufferPitch()
  If Not *I2Buffer
    End
  EndIf
  StopDrawing()
  
Procedure.f CompareImages(x1.u,y1.u,x2.u,y2.u,sx.u,sy.u)
  For x.u = 0 To sx-1
    x3.u = (x + x1) * 3
    x4.u = (x + x2) * 3
      For y.u = 0 To sy-1
        Pixel1 = PeekL(*I1Buffer+x3+(y+y1)*I1Pitch)
        Pixel2 = PeekL(*I2Buffer+x4+(y+y2)*I2Pitch)
        Indexer = Indexer+Abs(Red(Pixel1)-Red(Pixel2))+Abs(Green(Pixel1)-Green(Pixel2))+Abs(Blue(Pixel1)-Blue(Pixel2))
      Next y
    Next x
    Indexer2.f = Round(Indexer/(sx*sy*155*3)*100000,#PB_Round_Nearest)/1000
ProcedureReturn Indexer2
EndProcedure

Procedure CompareThis(x1.u,y1.u,bx.u,by.u,sx1.u,sy1.u,sx2.u,sy2.u)
  For x.u = sx1 To sx2
    For y.u = sy1 To sy2 
      CompareImages(x1,y1,x,y,bx,by)
    Next y
  Next x
EndProcedure

  t_start = ElapsedMilliseconds()
  CompareThis(0,0,50,50,0,0,50,50)
  laufzeit_jetzt = ElapsedMilliseconds() - t_start
Debug "das programm läuft seit " + Str(laufzeit_jetzt) + "ms"
  

End
Doch dies ist mir zu langasm. Da habe ich mir gedacht, dass ich Sprite3D benutze, um die beiden Sprites übereinander zu legen.
Damit ich den Unterschied der beiden Sprites erhalte, müsste ich das eine vom anderen subtrahieren.
Dazu gibt es die funktion Sprite3DBlendingMode(Ausgangsmodus, Zielmodus).

Code: Alles auswählen

  #D3DBLEND_ZERO            = 1
  #D3DBLEND_ONE             = 2
  #D3DBLEND_SRCCOLOR        = 3
  #D3DBLEND_INVSRCCOLOR     = 4
  #D3DBLEND_SRCALPHA        = 5
  #D3DBLEND_INVSRCALPHA     = 6
  #D3DBLEND_DESTALPHA       = 7
  #D3DBLEND_INVDESTALPHA    = 8
  #D3DBLEND_DESTCOLOR       = 9
  #D3DBLEND_INVDESTCOLOR    = 10
  #D3DBLEND_SRCALPHASAT     = 11
  #D3DBLEND_BOTHSRCALPHA    = 12
  #D3DBLEND_BOTHINVSRCALPHA = 13
Doch wie kann ich damit das eine Sprite vom anderen subtrahieren?
Oder ist es damit nicht möglich?

Welche Möglichkeiten habe ich sonst noch?
Mein Ziel ist es letztendlich eine Controlpoint Generator zu programmieren, ähnlich wie http://user.cs.tu-berlin.de/~nowozin/autopano-sift/

Ich bin übrigens Anfänger, wass PB anbelangt.

Besten Dank für eure Hilfe
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Zwei Sprites vergleichen

Beitrag von STARGÅTE »

Was du haben willst ist ja quasie die Differenz beider Farben, wenn du aber B von A abziehst, dann würde es dort kein Minus geben, sonden zu 0 werden. Sprite3D fällt also weg.

Meiner Meinung nach sind deine beiden Prozeduren sehr überflüssig.
So wie ich das sehe, berechnest du jedesmal neu die Differenz zweier Pixel, obwohl diese sich nicht ändert.
Denn du verschibst ja mit der Schleife in CompareThis immer nur um Pixel.
damit wird von den meisten Pixel mehrmals die differenz berechnet.

Damit meine ich: Erst ein Array aus den Differenzen erstellen (Abs(Red(Pixel1)-Red(Pixel2))+Abs(Green(Pixel1)-Green(Pixel2))+Abs(Blue(Pixel1)-Blue(Pixel2))), und dann darauf deine CompareThis loslassen.
Damit sparst du das enrom viel Zeit, weil die Hauptprozedure dann schon mit einem Fertigen Array rechnen kann, ohne Pixelsuche mit Pitch usw. und ohne Red() Green() und co.

Außerdem solltest du in den meisten Fällen mit .i arbeiten, ein .u bei der Schleife ist unnötig, und kostet nur Convertierungszeit.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
swissgregi
Beiträge: 16
Registriert: 31.01.2011 12:23

Re: Zwei Sprites vergleichen

Beitrag von swissgregi »

Mit einem Array ist es jetzt zwar schneller, aber immer noch nicht schnell genug ;-)
Was du haben willst ist ja quasie die Differenz beider Farben, wenn du aber B von A abziehst, dann würde es dort kein Minus geben, sonden zu 0 werden. Sprite3D fällt also weg.
Richtig, jedoch habe ich einen Weg gefunden. Diesen habe ich in Photoshop getestet.
1. Ebene ist das eine Bild
2. Ebene ist das Zweite bild, jedoch invers und mit 50% Transparenz über das erste gelegt.
so erhalte ich bei einer 100% übereinstimmung der beiden Bilder Grau. Alles was heller oder Dunkler ist, ist die Differenz.
So könnte ich zuerst mittels DirectX Die Grafiken übereinander legen, und danach die durchschnittliche helligkeit des Ergebnis-Sprites errechnen.

Doch leider Blicke ich mit der Funktion Sprite3DBlendingMode nicht durch.
Weshalb muss ich denn da zwei Parameter angeben (Ausgansmodus, Zielmodus)? Ich brauche doch nur zu sagen, wie das NEUE Sprite zu überblenden ist.

Code: Alles auswählen

DisplaySprite3D(0, 0, 0)
Sprite3DBlendingMode(2,4)
DisplaySprite3D(1, 0, 0,50)
Doch leider verhält sich das nicht so wie ich mir denke.
Der erste Parameter (2) in Sprite3DBlendingMode bedeutet doch, dass das erste Sprite sich "normal" verhält.
Der zweite Parameter (4) sagt doch, dass das zweite Sprite sich invers über das erste legen soll.
Wenn ich also zwei mal das gleiche Sprite übereinanderlege, sollte das Bild komplett Grau sein (wie in Photoshop).
Und wenn ich das um ein paar wenige Pixel verschiebe, sollte es einen "Amboss" Effekt geben.
Das tut es aber absolut nicht :bluescreen:

Was mache ich falsch, wie habe ich das zu verstehen?
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Zwei Sprites vergleichen

Beitrag von STARGÅTE »

zu Sprite3DBlendingMode()

Die beiden Parameter geben an, wie die Farben+Alpha aus dem neuen Sprite mit den Farben+Alpha des Hintergrundes vermischt werden sollen. deswegen gibt es zwei.
Der Normale Modus ist
Sprite3DBlendingMode(#D3DBLEND_SRCALPHA, #D3DBLEND_INVSRCALPHA)
dass heißt, er nimmt die Echten Farben des neuen Bild mit Alpha und addiert sie zu den Inversen Wertes des Hintergrundes.

Hier eine Seite die das ganz gut darstellt:
http://msdn.microsoft.com/en-us/library ... S.85).aspx

Hier nun dein Beispiel: (halb transparent ist 128, nicht wie du hast 50 ..., weil voll darstellen 255 ist.

Hier ein Beispiel zum rumspielen, wo immer gleich alle 13x13 Modi angezeigt werden:

Code: Alles auswählen

InitSprite()
InitSprite3D()

OpenWindow(0,0,0,1100,900,"")
OpenWindowedScreen(WindowID(0),0,0,1100,900,0,0,0)

CreateSprite(1, 60, 60, #PB_Sprite_Texture)
StartDrawing(SpriteOutput(1))
 For y = 0 To 59
  Line(0,y,60,1,RGB(255,255,y*255/60))
 Next
StopDrawing()
CreateSprite3D(1,1)

CreateSprite(2, 60, 60, #PB_Sprite_Texture)
StartDrawing(SpriteOutput(2))
 For x = 0 To 59
  Line(x,0,1,60,RGB(x*255/60,255,255))
 Next
StopDrawing()
CreateSprite3D(2,2)


Repeat

 ClearScreen($808080)
 
 Start3D()
  Sprite3DBlendingMode(5,6)
  DisplaySprite3D(1,0,0)
  DisplaySprite3D(2,0,200)
  For y = 1 To 13
   For x = 1 To 13
    Sprite3DBlendingMode(5,6)
    DisplaySprite3D(1,170+x*65,y*65-30, 255)
    Sprite3DBlendingMode(x,y)
    DisplaySprite3D(2,170+x*65,y*65-30, 128)
   Next
  Next
 Stop3D()
 
 StartDrawing(ScreenOutput())
  For y = 1 To 13
   For x = 1 To 13
    DrawText(170+x*65,y*65-30,Str(x)+","+Str(y))
   Next
  Next
 StopDrawing()
 FlipBuffers()
 
Until WaitWindowEvent(10) = #PB_Event_CloseWindow
Wenn ich mir das so ansehe, könnte 10,4 der "richtige" sein.
Denn da, wo beide Sprites weiß sind, wird das resultat schwarz (gleich), an den Ecken mit weiß, Rot/bau und bei den unterschiedlcihen Farben, wird Lila, also noch größere differenz.
also:

Code: Alles auswählen

    Sprite3DBlendingMode(5, 6)
    DisplaySprite3D(#Sprite1, x, y, 255)
    Sprite3DBlendingMode(10, 4)
    DisplaySprite3D(#Sprite2, x, y, 128)
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
swissgregi
Beiträge: 16
Registriert: 31.01.2011 12:23

Re: Zwei Sprites vergleichen

Beitrag von swissgregi »

Es funktioniert, wenn ich einfach das zweite bild invers über das erste lege, mit 50% Transparenz.
Nun muss ich also nur zuerst das zweite Bild invers rechnen.

Noch damit ich richtig verstehe:

Code: Alles auswählen

DisplaySprite(1,0,0)
Sprite3DBlendingMode(Source,Dest)
DisplaySprite(2,0,0)
Welches Sprite verhält sich nun als Source und welches als Dest?
Das habe ich noch nicht ganz verstanden.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Zwei Sprites vergleichen

Beitrag von STARGÅTE »

Mit DisplaySprite() geht das eh nicht !
nur mit DisplaySprite3D()

In deinem Beispiel wäre 2 die Quelle und 1 das Ziel
Die Konstanten bestimmen dann, was passieren soll ...
Für den StandardModus 5, 6 : SRCALPHA, INVSRCALPHA
nimmt er also von der Quelle (SCR) (Sprite 2) den Vollen AlphaWert (ALPHA) + Farben
und blendet sie mit dem Ziel Farben mit Inversem (INV) Alpha (ALPHA) der Quelle (SRC)
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
swissgregi
Beiträge: 16
Registriert: 31.01.2011 12:23

Re: Zwei Sprites vergleichen

Beitrag von swissgregi »

Code: Alles auswählen

Mit DisplaySprite() geht das eh nicht !
nur mit DisplaySprite3D()
richtig, mein fehler :oops:

Mit StardDrawing() ist es ja möglich direkt auf 2D Sprites zu zeichnen.
Mit Start3D() habe ich diese möglichkeit nicht.
Habe ich bei einem "DisplaySprite3D(0, 0, 0)" keine möglichkeit zu wählen, dass dies auf ein Sprite geblendet wird?
UseBuffer(#Sprite) scheint da auch nicht zu funktionieren.

Kann ich 3D Sprites also "nur" auf den Bildschirmbuffer ausgeben, und muss diese dann "abfotografieren"?
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Zwei Sprites vergleichen

Beitrag von STARGÅTE »

Richtig, die "3D" sachen sind außschlielich für den Screen.

Eine weitere möglichkeit gibt es jedoch mit Images, nämlich CustomFilterCallback()

Damit kannst du einen eigenen Filter erzeugen mit dem ein Image gezeichnet wird.

Code: Alles auswählen

Procedure FilterCallback(x, y, QuellFarbe, ZielFarbe)
  QuellFarbe = (QuellFarbe!$FFFFFF)&$FFFFFF | 128<<24
  ProcedureReturn AlphaBlend(QuellFarbe, ZielFarbe)
EndProcedure

CreateImage(1, 256, 256)
StartDrawing(ImageOutput(1))
  For y = 0 To 255
    Line(0,y,256,1,RGB(0,0,y))
  Next
StopDrawing()

CreateImage(2, 256, 256)
StartDrawing(ImageOutput(2))
  For x = 0 To 255
    Line(x,0,1,256,RGB(0,x,0))
  Next
StopDrawing()

OpenWindow(0, 0, 0, 256, 256, "2DDrawing", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

CreateImage(0, 256, 256) 
StartDrawing(ImageOutput(0))
  ; Normal
  DrawImage(ImageID(1), 0, 0)
  ; eigener Filter
  DrawingMode(#PB_2DDrawing_CustomFilter)      
  CustomFilterCallback(@FilterCallback())
  DrawImage(ImageID(2), 0, 0)
StopDrawing() 

ImageGadget(0, 0, 0, 256, 256, ImageID(0))

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Oben im Callback kannst du dann genau einstellen, was du machen willst.
Hier halt das 2. Image (Quelle) invertieren und mit 50% Alpha auf das 1. Image (Ziel) blenden.

Hier nun genau so wie du wolltest, das Grau = gleich ist, und die andere Farben eine Differenz darstellen.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: Zwei Sprites vergleichen

Beitrag von Thorium »

Also ich sehe da ne Menge Optimierungspotenzial in deinem Code.
Das wichtigste vorweg: Warum benutzt du überall .u? Vieleicht möchtest du damit Speicher sparen? Macht du aber nicht. Nutze .i, damit bist du direkt schon schneller.

Das PeekL gegen Pointer austauschen.
Du berechnest auch viel zu viel pro Pixel: Pixel1 = PeekL(*I1Buffer+x3+(y+y1)*I1Pitch)
Viel zu viele unnötige Berechnungen. Die Multiplikation brauchst du nur einmal pro Zeile durchführen und nicht für jeden Pixel. Und dann brauchst du pro Reihe nur 4 zum Pointer addieren. Das wars schon.

Lad mal die beiden JPEGs hoch, dann optimiere ich dir den Code ein wenig.

CustomFilterCallback düfte wesentlich langsamer sein. Da hast du ja einen Call pro Pixel.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
swissgregi
Beiträge: 16
Registriert: 31.01.2011 12:23

Re: Zwei Sprites vergleichen

Beitrag von swissgregi »

Besten Dank für die Unterstützung.
Ich bin leider noch Anfänger in Purebasic. Beginne aber gerade immer mehr zu verstehen.
Ich dachte, ein Pointer liest man mit PeekL, PeekB etc. aus.
Ja, die Berechnungen kann man auf jeden Fall noch optimieren.

Letztendlich will ich eine Funktion schreiben, die in der Koordinate x1,y1 vom Bild1 und der Koordinaty x2,y2 vom Bild2 in einem Block von etwa 50x50pixel den Unterschied berechnet.
Wenn ich diese Funktion dann mehrmals an verschieden Koordinaten anwende, finde ich so die höchste Wahrscheinlichkeit für einen "Kontrollpunkt" (wie bei http://user.cs.tu-berlin.de/~nowozin/autopano-sift/)

Ich habe mir eben gedacht, dass die überblendung mit Sprite3D sehr schnell ist. Dies scheint auch zu funktionieren, nur muss scheinbar jedesmal den Bildschirm abfotografieren, um den Unterschied aus dem "Emboss effekt" zu berechnen. Ob dies dann eine schnelle Variante ist weiss ich noch nicht.
Habe ich auf die Bildschirmbuffer von Sprite3D keinen Zugriff via Pointer?

Ich habe mal vier überlappende Bilder auf meine Hosting geladen: http://gv-net.ch/purebasic/gigatest.zip
Antworten