[gelöst] Bildrotation grafischer Fehler



  • Ich habe jetzt das Programm so umgeschrieben, dass es nur drei Eckpunkte dreht, und dann daraus die positionen der restlichen Pixel im gedrehten Bild möglichst ohne Rundungen an irgendeiner Stelle berechnet. Das Resultat ist trotzdem noch exakt das Alte.

    Neuer Quelltext (nur der Part mit dem Drehen, die Pixelfarben vom Quellbild sind schon ausgelesen):

    //Rotationsvariablen erstellen
        double PosX, PosY, RotX, RotY = 0;
        double Punkt1X, Punkt1Y, Punkt2X, Punkt2Y, Punkt3X, Punkt3Y = 0;
    
        //Drei Eckpunkte drehen
        if(center)
        {
            int x, y = 0;
            //Erster Eckpunkt, 0|0
            x = (bmpsmall->w-SizeX)/2;
            y = (bmpsmall->h-SizeY)/2;
            //Koordinaten auf Bildmitte als Nullpunkt beziehen
            PosX = x - bmpsmall->w/2;
            PosY = y - bmpsmall->h/2;
            //Rotieren, dabei ist Bildmitte Nullpunkt
            RotX = PosX * cos(Angle) + PosY * sin(Angle);
            RotY = -PosX * sin(Angle) + PosY * cos(Angle);
            //Wieder auf ursprüngliche Koordinaten beziehen
            Punkt1X = RotX + bmpsmall->w/2;
            Punkt1Y = RotY + bmpsmall->h/2;
    
            //Zweiter Eckpunkt, SizeX|0
            x = SizeX + (bmpsmall->w-SizeX)/2;
            //Koordinaten auf Bildmitte als Nullpunkt beziehen
            PosX = x - bmpsmall->w/2;
            PosY = y - bmpsmall->h/2;
            //Rotieren, dabei ist Bildmitte Nullpunkt
            RotX = PosX * cos(Angle) + PosY * sin(Angle);
            RotY = -PosX * sin(Angle) + PosY * cos(Angle);
            //Wieder auf ursprüngliche Koordinaten beziehen
            Punkt2X = RotX + bmpsmall->w/2;
            Punkt2Y = RotY + bmpsmall->h/2;
    
            //Dritter Eckpunkt, 0|SizeY
            x = (bmpsmall->w-SizeX)/2;
            y = SizeY + (bmpsmall->h-SizeY)/2;
            //Koordinaten auf Bildmitte als Nullpunkt beziehen
            PosX = x - bmpsmall->w/2;
            PosY = y - bmpsmall->h/2;
            //Rotieren, dabei ist Bildmitte Nullpunkt
            RotX = PosX * cos(Angle) + PosY * sin(Angle);
            RotY = -PosX * sin(Angle) + PosY * cos(Angle);
            //Wieder auf ursprüngliche Koordinaten beziehen
            Punkt3X = RotX + bmpsmall->w/2;
            Punkt3Y = RotY + bmpsmall->h/2;
    
            //Entfernungen bestimmen
            double EntfX1, EntfY1, EntfX2, EntfY2 = 0;
            EntfX1 = Punkt2X - Punkt1X;
            EntfY1 = Punkt2Y - Punkt1Y;
            EntfX2 = Punkt3X - Punkt1X;
            EntfY2 = Punkt3Y - Punkt1Y;
    
            //Punkt übertragen
            for(int x = (bmpsmall->w-SizeX)/2; x < SizeX+(bmpsmall->w-SizeX)/2; x++)
            {
                for(int y = (bmpsmall->h-SizeY)/2; y < SizeY+(bmpsmall->h-SizeY)/2; y++)
                {
                    //Punkt verschieben
                    double Temp = (bmpsmall->w-SizeX);
                    Temp /= 2;
                    double TempX = x;
                    Temp = TempX - Temp;
                    //double Temp  = x-(bmpsmall->w-SizeX)/2; <- rundet
    
                    double Temp2 = (bmpsmall->h-SizeY);
                    Temp2 /= 2;
                    double TempY = y;
                    Temp2 = TempY - Temp2;
                    //double Temp2 = y-(bmpsmall->h-SizeY)/2; <- rundet
    
                    PosX = Punkt1X + Temp/SizeX*EntfX1;
                    PosX += Temp2/SizeY*EntfX2;
                    PosY = Punkt1Y + Temp/SizeX*EntfY1;
                    PosY += Temp2/SizeY*EntfY2;
    
                    //Punkt mit AA speichern (zwischen je zwei X und Y Koordinaten)
                    //X/Y-Werte und Faktor bestimmen
                    int X1, X2, Y1, Y2 = 0;
                    //Faktoren für jedes der vier Pixelfelder
                    double Fak1, Fak2, Fak3, Fak4 = 0;
    
                    /*
                    Struktur:
                    1 | 2
                    -----
                    4 | 3
                    */
    
                    //Schneidet beim konvertieren Kommastellen einfach ab
                    X1 = PosX;
                    X2 = X1+1;
                    Y1 = PosY;
                    Y2 = Y1+1;
    
                    //Pixelfeldfaktoren bestimmen
                    Fak1 = 1.0-(PosX - X1); //X
                    Fak1 *= 1.0-(PosY - Y1); //kombiniert mit Y
    
                    Fak2 = PosX - X1; //X
                    Fak2 *= 1.0-(PosY - Y1); //kombiniert mit Y
    
                    Fak3 = PosX - X1; //X
                    Fak3 *= PosY - Y1; //kombiniert mit Y
    
                    Fak4 = 1.0-(PosX - X1); //X
                    Fak4 *= PosY - Y1; //kombiniert mit Y
    
                    Uint8 URot, UGruen, UBlau, UAlpha = 0;
                    double dR, dG, dB, dA = 0;
    
                    //Farbe von X1/Y1 auslesen
                    getcolor(bmpsmall, X1, Y1, r, g, b, a);
                    dR = r;
                    dG = g;
                    dB = b;
                    dA = a;
                    URot = dR*(1-Fak1) + R[x][y]*Fak1;
                    UGruen = dG*(1-Fak1) + G[x][y]*Fak1;
                    UBlau = dB*(1-Fak1) + B[x][y]*Fak1;
                    UAlpha = dA*(1-Fak1) + A[x][y]*Fak1;
                    setcolor(bmpsmall, X1, Y1, URot, UGruen, UBlau, UAlpha);
    
                    //Farbe von X2/Y1 auslesen
                    getcolor(bmpsmall, X2, Y1, r, g, b, a);
                    dR = r;
                    dG = g;
                    dB = b;
                    dA = a;
                    URot = dR*(1-Fak2) + R[x][y]*Fak2;
                    UGruen = dG*(1-Fak2) + G[x][y]*Fak2;
                    UBlau = dB*(1-Fak2) + B[x][y]*Fak2;
                    UAlpha = dA*(1-Fak2) + A[x][y]*Fak2;
                    setcolor(bmpsmall, X2, Y1, URot, UGruen, UBlau, UAlpha);
    
                    //Farbe von X2/Y2 auslesen
                    getcolor(bmpsmall, X2, Y2, r, g, b, a);
                    dR = r;
                    dG = g;
                    dB = b;
                    dA = a;
                    URot = dR*(1-Fak3) + R[x][y]*Fak3;
                    UGruen = dG*(1-Fak3) + G[x][y]*Fak3;
                    UBlau = dB*(1-Fak3) + B[x][y]*Fak3;
                    UAlpha = dA*(1-Fak3) + A[x][y]*Fak3;
                    setcolor(bmpsmall, X2, Y2, URot, UGruen, UBlau, UAlpha);
    
                    //Farbe von X1/Y2 auslesen
                    getcolor(bmpsmall, X1, Y2, r, g, b, a);
                    dR = r;
                    dG = g;
                    dB = b;
                    dA = a;
                    URot = dR*(1-Fak4) + R[x][y]*Fak4;
                    UGruen = dG*(1-Fak4) + G[x][y]*Fak4;
                    UBlau = dB*(1-Fak4) + B[x][y]*Fak4;
                    UAlpha = dA*(1-Fak4) + A[x][y]*Fak4;
                    setcolor(bmpsmall, X1, Y2, URot, UGruen, UBlau, UAlpha);
                }
            }
    
        } else {
            //fehlt
        }
    

    Hier ich habe das Resultat mal hochgeladen, vielleicht hilfts jemandem weiter. Wie gesagt, es ist ein völlig gleichmäßiges Muster:

    http://img341.yfrog.com/img341/8889/grafikfehler.png

    EDIT: Jetzt probiere ich mal diese Rückwärtsmethode. Vielleicht ist die ja erfolgreicher.



  • Kann es sein, dass das rotierte Bild mehr Pixel als das Quellbild besitzt?

    Ich wollte gerade mit der invertierten Form beginnen, bei der ich Zielkoordinaten koordinaten im Quellbild zuweise. Dazu wollte ich die Zielkoordinaten wie im letzten geposteten Quelltext berechnen, also so:

    //Rotationsvariablen erstellen
        double PosX, PosY, RotX, RotY = 0;
        double Punkt1X, Punkt1Y, Punkt2X, Punkt2Y, Punkt3X, Punkt3Y = 0;
    
        //Drei Eckpunkte drehen
        if(center)
        {
            int x, y = 0;
            //Erster Eckpunkt, 0|0
            x = (bmpsmall->w-SizeX)/2;
            y = (bmpsmall->h-SizeY)/2;
            //Koordinaten auf Bildmitte als Nullpunkt beziehen
            PosX = x - bmpsmall->w/2;
            PosY = y - bmpsmall->h/2;
            //Rotieren, dabei ist Bildmitte Nullpunkt
            RotX = PosX * cos(Angle) + PosY * sin(Angle);
            RotY = -PosX * sin(Angle) + PosY * cos(Angle);
            //Wieder auf ursprüngliche Koordinaten beziehen
            Punkt1X = RotX + bmpsmall->w/2;
            Punkt1Y = RotY + bmpsmall->h/2;
    
            //Zweiter Eckpunkt, SizeX|0
            x = SizeX + (bmpsmall->w-SizeX)/2;
            //Koordinaten auf Bildmitte als Nullpunkt beziehen
            PosX = x - bmpsmall->w/2;
            PosY = y - bmpsmall->h/2;
            //Rotieren, dabei ist Bildmitte Nullpunkt
            RotX = PosX * cos(Angle) + PosY * sin(Angle);
            RotY = -PosX * sin(Angle) + PosY * cos(Angle);
            //Wieder auf ursprüngliche Koordinaten beziehen
            Punkt2X = RotX + bmpsmall->w/2;
            Punkt2Y = RotY + bmpsmall->h/2;
    
            //Dritter Eckpunkt, 0|SizeY
            x = (bmpsmall->w-SizeX)/2;
            y = SizeY + (bmpsmall->h-SizeY)/2;
            //Koordinaten auf Bildmitte als Nullpunkt beziehen
            PosX = x - bmpsmall->w/2;
            PosY = y - bmpsmall->h/2;
            //Rotieren, dabei ist Bildmitte Nullpunkt
            RotX = PosX * cos(Angle) + PosY * sin(Angle);
            RotY = -PosX * sin(Angle) + PosY * cos(Angle);
            //Wieder auf ursprüngliche Koordinaten beziehen
            Punkt3X = RotX + bmpsmall->w/2;
            Punkt3Y = RotY + bmpsmall->h/2;
    
            //Entfernungen bestimmen
            double EntfX1, EntfY1, EntfX2, EntfY2 = 0;
            EntfX1 = Punkt2X - Punkt1X;
            EntfY1 = Punkt2Y - Punkt1Y;
            EntfX2 = Punkt3X - Punkt1X;
            EntfY2 = Punkt3Y - Punkt1Y;
    
            //Punkt übertragen
            for(int x = (bmpsmall->w-SizeX)/2; x < SizeX+(bmpsmall->w-SizeX)/2; x++)
            {
                for(int y = (bmpsmall->h-SizeY)/2; y < SizeY+(bmpsmall->h-SizeY)/2; y++)
                {
                    //Punkt verschieben
                    double Temp = (bmpsmall->w-SizeX);
                    Temp /= 2;
                    double TempX = x;
                    Temp = TempX - Temp;
    
                    double Temp2 = (bmpsmall->h-SizeY);
                    Temp2 /= 2;
                    double TempY = y;
                    Temp2 = TempY - Temp2;
    
                    PosX = Punkt1X + Temp/SizeX*EntfX1;
                    PosX += Temp2/SizeY*EntfX2;
                    PosY = Punkt1Y + Temp/SizeX*EntfY1;
                    PosY += Temp2/SizeY*EntfY2;
    
                    Uint8 R = 255;
                    Uint8 G = 255;
                    Uint8 B = 255;
                    Uint8 A = 255;
    
                    setcolor(bmpsmall, PosX, PosY, R, G, B, A);
                }
            }
        }
    

    Und hier das Resultat:

    http://img231.imageshack.us/img231/9638/grafikfehler2.png

    Obwohl es jetzt kein AA hat und nur die Positionen der Pixelzentren zeigt, sieht man gleich wieder das Muster im Bild. Wie soll ich denn selbst invertiert die Zielpixel dem Quellbild zuweisen, wenn jede Methode die Zielpixel zu ermitteln immer diese Löcher hat? Da hab ich ja dann das gleiche Problem wie umgekehrt.



  • Blaze schrieb:

    Irgendwie erinnert mich das fast schon an Sampeln von Texturen was du vorhast.

    Das ist das Sampeln von Texturen. Die Textur (Bild) wird mit einer Matrix (Rotationsmatrix) auf ein Polygon (rotiertes Bild) projeziert. Es gibt da nicht so viel Mathematik dahinter. f'`8k

    Gruß, TGGC (Was Gamestar sagt...)



  • ja, mathematisch müsste ja auch sowohl meine erste, wie auch meine zweite Möglichkeit funktionieren. Meine Frage ist ja, warum tun es beide auf die selbe Weise nicht?



  • Little Programmer schrieb:

    ja, mathematisch müsste ja auch sowohl meine erste, wie auch meine zweite Möglichkeit funktionieren. Meine Frage ist ja, warum tun es beide auf die selbe Weise nicht?

    Weil sie mathematisch gesehen nicht funktioniert. Wenn z.b. die Matrix eine Streckung enthielte, dann wuerde eine Textur mit 4x4 Pixel auf dem Bildschirm 20x20 Pixel einnehmen - deine tolle Methode wuerde aber statt 400 Pixeln nur 16 zeichnen. Und da fragst du noch, wo die Loecher herkommen? f'`8k

    Gruß, TGGC (Was Gamestar sagt...)


  • Mod

    obwohl die sache mathematisch natuerlich funktioniert und richtig sein wuerde, rechnest du hier leider nicht mit unendlicher genauigkeit, sondern mit pixel-einheiten, beim rotieren wirst du also immer von float auf int runden und dabei dabei werden an manchen stellen pixel zusammenfallen und die fehlen dann an anderen stellen.
    die transformation inverse zu machen, also fuer das ziel die passenden quell-pixel suchen verhindert dass du luecken hast, aber es wird bei der rotation dennoch vorkommen, dass dafuer manche pixel aus dem quell-bild garnicht vorkommen, jedoch faellt das weniger auf.



  • rapso schrieb:

    obwohl die sache mathematisch natuerlich funktioniert und richtig sein wuerde, rechnest du hier leider nicht mit unendlicher genauigkeit, sondern mit pixel-einheiten

    Es ist ja nicht so, das die Mathematik im Computer nicht definiert waere und dann irgendwelche zufaelligen Fehler entstehen. Diese Fehler existieren nicht nur praktisch, sie ergeben sich schon im theoretischen Modell, mit dem der Computer arbeitet. Der Computer verpasst nicht irgendwelche Pixel, weil er falsch gerundet hat. Er hat natuerlich immer korrekt gerundet, ansonsten waere er defekt und nicht zu gebrauchen. Der Fehler liegt an der Stelle wo jemand dilletantisch versucht affine Transformationen in einem Computerprogramm umzusetzen. f'`8k

    Gruß, TGGC (Was Gamestar sagt...)


  • Mod

    TGGC schrieb:

    rapso schrieb:

    obwohl die sache mathematisch natuerlich funktioniert und richtig sein wuerde, rechnest du hier leider nicht mit unendlicher genauigkeit, sondern mit pixel-einheiten

    Es ist ja nicht so, das die Mathematik im Computer nicht definiert waere und dann irgendwelche zufaelligen Fehler entstehen.

    Ja, dann solltest du lieber darauf bezug nehmen statt zu behaupten etwas funktioniert schon rein mathematisch nicht.

    Diese Fehler existieren nicht nur praktisch, sie ergeben sich schon im theoretischen Modell, mit dem der Computer arbeitet. Der Computer verpasst nicht irgendwelche Pixel, weil er falsch gerundet hat. Er hat natuerlich immer korrekt gerundet, ansonsten waere er defekt und nicht zu gebrauchen.

    Ich sehe du hast meinen text sehr schnell aufgefasst.

    Der Fehler liegt an der Stelle wo jemand dilletantisch versucht affine Transformationen in einem Computerprogramm umzusetzen.

    Ja, jeder ist irgendwo dillenantisch. Traurig wird es wenn jemand auf seinem niveau stehen bleibt, da warst du gerade echt vorbildlich.



  • Nach deiner Logik funktioniert ein Computer an sich schon rein mathematisch nicht. Das ist aber Quatsch. Er kann halt keine natuerlichen Zahlen oder reelen Zahlen, aber er hat ein anderes mathematisches Modell. Und dieses Modell ist in sich schluessig und funktioniert auch korrekt. Mit dieser Mathematik funktionieren einige Sache und andere nicht.

    Aber da du ehh schon wieder auf deinen ich bin Mod und habe eh recht Trip bist - Guten Tag. f'`8k

    Gruß, TGGC (Was Gamestar sagt...)



  • @ rapso und @ TGGC, sorry das ich das jetzt sagen muss, aber ihr habt mal total am Thema vorbeigeschrieben.
    @ TGGC, wie kommst du bitte schön auf die Idee, dass ich das Bild strecken würde? Das Ziel- und Quellbild sind gleich groß. es war nirgends von einer Streckung die Rede.
    @ rapso, wie ich glaube ich schon mehrmals hier im Topic geschrieben habe und wie es auch auffällig in der Hälfte des Quelltextes steht, runde ich meine Koordinaten nicht sondern nutze oversampling, um Pixel über mehrere pixel verteilt an der Korrekten Stelle anzuzeigen. Generell ist die gesamte Berechnung möglichst rundungsfrei auf doubles beschränkt, damit keine Rundungsfehler irgendeiner Form auftreten.

    Invers tranformieren funktioniert aus den selben Fehlern wie die andere nicht, da im Zielbild bei den aktuellen beiden Berechnungsarten Pixel fehlen, daran würde sich dann gar nichts ändern. Ich versuche nun eine dritte Berechnungsart und bedanke mich hier erstmal für die Hilfe und erkläre das Thema für gelöst. Neue Erkenntnisse kommen hier wohl nicht mehr zustande.


  • Mod

    Little Programmer schrieb:

    Invers tranformieren funktioniert aus den selben Fehlern wie die andere nicht, da im Zielbild bei den aktuellen beiden Berechnungsarten Pixel fehlen, daran würde sich dann gar nichts ändern.

    das ist nicht moeglich, man setzt pixel fuer pixel, wie soll da ein pixel fehlen?



  • Little Programmer schrieb:

    Invers tranformieren funktioniert aus den selben Fehlern wie die andere nicht, da im Zielbild bei den aktuellen beiden Berechnungsarten Pixel fehlen, daran würde sich dann gar nichts ändern. Ich versuche nun eine dritte Berechnungsart und bedanke mich hier erstmal für die Hilfe und erkläre das Thema für gelöst. Neue Erkenntnisse kommen hier wohl nicht mehr zustande.

    Natürlich funktioniert invers transformieren.
    Und ganz egal ob du mit Nearest-Pixel Filter oder Bilinear/Qubisch/... arbeitest, wenn du vom rotierten Bild auf das Original zurückrechnest hast du keinen Schottenrock mehr.
    Probier's einfach mal aus.



  • Habe eine andere Lösung gefunden, die das Bild fehlerfrei rotiert. Einmal habe ich eine Rechenleistung sparende Drehberechnung, welche nur einen einzigen Punkt drehen muss und aus dem Drehwinkel und der Position dieses Pixels die Positionen der anderen Pixel berechnet. Und zweitens fiel mir ein simpler aber funktionierender Trick ein.

    //PosX und PosY sind doubles und enthalten nun schon die Koordinaten des neuen Pixels
    
                //Punkt
                PrintAA(bmpsmall, PosX, PosY, x, y, R, G, B, A);
                //Ein Viertel weiter
                PosX += 0.25;
                PosY += 0.25;
                PrintAA(bmpsmall, PosX, PosY, x, y, R, G, B, A);
                //Eine Hälfte weiter
                PosX += 0.25;
                PosY += 0.25;
                PrintAA(bmpsmall, PosX, PosY, x, y, R, G, B, A);
                //Drei Viertel weiter
                PosX += 0.25;
                PosY += 0.25;
                PrintAA(bmpsmall, PosX, PosY, x, y, R, G, B, A);
    

    Durch dieses Weiterrücken fülle ich den Leerraum zwischen zwei Pixeln vollständig auf. Alle 0.25-Pixel wird nun ein Punkt gesetzt. Auch wenn jeder Punkt so viermal gesetzt wird, wieht man dies im Endbild nicht. Dies ist nun perfekt farbecht mit Oversampling gedreht.



  • Brillant! 😉



  • @Little Programmer:
    Das meinst du jetzt hoffentlich nicht ernst...?


Anmelden zum Antworten