Laden...

Bild drehen und Zeilenweise in ein neues Bild schreiben

Erstellt von Gimmick vor 8 Jahren Letzter Beitrag vor 8 Jahren 7.408 Views
G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren
Bild drehen und Zeilenweise in ein neues Bild schreiben

Hallo,

ich bin C# Anfänger und habe hier ein paar Pobleme.

Und zwar möchte ich ein Bild öffnen, anzeigen, schrittweise drehen, zeilenweise nach jedem Schritt in ein neuen Bild schreiben und das Ergebnis dann anzeigen.

Bisher habe ein Fenster mit zwei PictureBoxen und kann ein Bild laden, links anzeigen, um einen festen, aber beliebigen Winkel drehen und auf der rechten Seite anzeigen.

Mein großes Problem ist jetzt, dass ich nicht weiß wie ich am besten eine Bildzeile auslesen und in ein neues Bild schreibe. Ich habe überlegt das mit Hilfe von getPixel und setPixel zu machen bin mir aber nicht sicher ob das so eine gute Idee ist ^^.

Das zweite Problem: Bei jeder Rotation wird das Bild unschärfer. Kann man da irgendwas gegen machen?

Danke schon mal.

W
955 Beiträge seit 2010
vor 8 Jahren

Das zweite Problem: Bei jeder Rotation wird das Bild unschärfer. Kann man da irgendwas gegen machen? In dem man das vom Originalbild ausgehend immer wieder dreht und nicht vom zuletzt gedrehten Bild?

T
64 Beiträge seit 2011
vor 8 Jahren

Hallo Gimmick,

ich bin mir nicht ganz sicher was du vorhast. Wozu musst du das gedrehte Bild Zeilenweise einlesen? Warum nicht das Bild laden, wie gewünscht drehen, sich um die Randprobleme kümmern und dann speichern.
Falls du es Zeilenweise benötigst würde ich dir den Bresenham-Algorithmus empfehlen. Dabei solltest du eine Geradengleichung aufstellen und diese je nach Drehwinkel anpassen, diese anschließend durch dein Bild führen und mit Hilfe des Bresenham Algorithmus überprüfen welche Pixel auf der Geraden liegen.

Zum zweiten Problem.
Drehst du das Original Bild einmalig um einen festen Winkel oder drehst du das resultierende Bild immer wieder um einen kleinen Winkel? Bei der zweiten Variante werden immer Aliasingeffekte auftreten wenn der Drehwinkel nicht durch 90° teilbar ist. Somit verlierst du immer mehr Informationen vom Originalbild und dieses ist nicht mehr verlustfrei rekonstruierbar.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Okay, danke =). Das offensichtlichste hab ich natürlich nicht gesehen: Wenn ich immer das ursprüngliche Bild um den neuen Winkel drehe, statt weiter zu drehen wird das Bild nicht schlechter.

Was das werden soll:

Es soll nach jedem Rotationsschritt eine neue Zeile gelesen und in dem neuen Bild ergänzt werden. Rauskommen soll eine Art Rolling-Shutter-Effekt.

Den Bresenham-Algorithmus werde ich mir auf jedenfall mal ansehen, das könnte ich für den nächsten Schritt, das entfernen des Effekts gut gebrauchen.

Edit: Was genau meinst du denn mit "Randproblemen"?

Hinweis von gfoidl vor 8 Jahren

Bitte beachte [Hinweis] Wie poste ich richtig? Punkt 2.3

T
64 Beiträge seit 2011
vor 8 Jahren

Nettes Projekt, gefällt mir.

Zum Randproblem. Wenn du dein Bild drehst wird die benötigte Auflösung zur Darstellung aller Pixel größer, wenn der Drehwinkel nicht durch 90° teilbar ist. Wenn du zum Beispiel eine 100x100 Px Abbildung hast und diese um 45° drehst brauchst 142x142 Px um alle Bereiche des ursprünglichen Bildes darzustellen. Bei diesem Beispiel bleiben die Ecken aber undefiniert und müssen mit einer anderen Farbe aufgefüllt werden. Das gleiche tritt auch auf wenn du die Auflösung bei 100x100 Px belässt und die Ränder abschneidest. An den Ecken treten undefinierte Bereiche auf.

Für deinen Rolling-Shutter-Effekt könnte der Bresenham schon das richtige Werkzeug sein. Ich würde an deiner Stelle einfach eine Gerade spalten- oder zeilenweise durchs Bild schieben. Mit jedem Schiebeschritt drehst du die Gerade dann um einen gewünschten Winkel und ermittelst mit Hilfe des Bresenham-Algorithmus, welche Bildkoordinaten du für diese Zeile im resultierenden Bild brauchst.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Okay, also ich hab mir da einige Gedanken gemacht und habe einige Probleme mit der Geraden.

Da sich die Gerade ja nach oben bewegt und gleichzeitig dreht, kommt da folgende Bewegung bei raus

Meine Überlegung war jetzt die Gerade über Anfangs- und Endpunkt zu beschreiben. Wobei ich die Punkte über eine Funktion bewege.
Aber irgendwie.... kommt da nur Käse bei rum.

Meine Überlegung folgende:

Ich beschreibe den Anfangs- und Endpunkt der Geraden über eine Funktion, die aus einer Addition der Bewegung nach oben und der Kreisbewegung entsteht. Aber dann kommen ja auch Werte außerhalb des Bildes bei rum. Ich müsste doch dann eigentlich quasi den Schnittpunkt der Gerade mit dem Rand des Bildes bestimmen und daraus dann die eigentliche Gerade erzeugen.

Und kann es dann nicht sein, dass dann Pixel am unteren Rand übersprungen werden, durch Rundungen und begrenzte Genauigkeit?

Also wärs nicht eigentlich besser, die X-Achse des Bildes Punkt für Punkt abzugehen und daras dann den Endpunkt zu bestimmen?
Dabei stellt sich mir dann wieder die Frage wie denn bei was die Funktion aussehen soll 🤔

Irgendwie komm ich da nicht weiter.

Edit: Anhang

T
64 Beiträge seit 2011
vor 8 Jahren

Ich kann mir hier keine Bilder von Web-Storage Hostern ansehen. Hänge es doch bitte direkt an deinen Beitrag an. Wenn du das gemacht hast schaue ich es mir mal in Ruhe an und editier dann diesen Beitrag hier und versuche weiter zu helfen.

Edit:
Eigentlich reicht mir der Text auch schon um weiterzuhelfen.

Deine Grundgedanken sind schon mal nicht schlecht wir müssen nur noch etwas Ordnung und System in die Sache reinbringen.
Ja du brauchst Start- und Endpunkte deiner Geraden um dann über den Bresenham alle dazwischenliegenden Pixel zu ermitteln. Der Startpunkt kann aber nicht kleiner als (0,0) und der Endpunkt nicht größer als (Bildbreite,Bildhöhe) sein. Diese Grenzen sind für die nächsten Schritte wichtig. Folgende einfach Geradengleichung:
y= mx+n

  • y und x sind Koordinaten
  • m ist dein Anstieg welcher sich aus dem Drehwinkel ergibt
  • n ist deine Verschiebung nach oben oder unten

Wenn du jetzt anfängst die Gerade nach oben zu bewegen ändert sich nur das n. Um zusätzlich eine Rotation zu erzeugen muss mit jedem Verschiebungsschritt auch das m geändert werden.
Du kannst dir den Startpunkt einfach ausrechnen indem du einfach 0 bei x oder y einsetzt und die fehlende Komponente bestimmst. Du musst natürlich überprüfen welche von den beiden möglichen Koordinaten die gültige ist aber das sollte nicht schwer sein, da kein Wert kleiner als 0 sein darf.
Beim Endpunkt ist das Vorgehen gleich nur das du statt nicht kleiner als Minimum auf nicht größer als Maximum prüfen musst.
Mit den beiden bestimmten Punkten bekommst du anschließend über den Bresenham Algorithmus alle dazwischenliegenden Punkte welche den gewünschten neuen Bildpunkten entsprechen.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Erst mal danke für die Antwort ^^.

Okay, das mit den Schnittpunkten bekomm ich denk ich hin.

In dem Fall muss ich aber noch beachten, dass sich der y-Achsenabschnitt n nicht linear bewegt. Ich münze ja quasi die von einem Betrachter gesehene Bewegung auf die Gerade um und wenn sich die Gerade auf dem Bild um 90° gedreht hat bewegt sie sich ja nur noch nach links, bei 180° eigentlich nach unten, bei 270° nach rechts und dann wieder nach oben 🤔

Das heisst ich brauche sowas wie n=jcos(Pii) für den y-Achsenabschnitt und bei der Steigung müsste ich Fälle einbauen, da ja eine senkrechte Gerade keine Funktion hat.

... oder? =)

Edit: Ach was rede ich da. Die Drehrichtung auf meinem eigenen Bild hat mich verwirrt 😁

Ja, ok. Ich lass n steigen und kann da diekt meine y Koordinate für einen Punkt ablesen. Bis die maximale Höhe erreicht ist. Dann muss ich schauen "nurnoch" schauen wo die Gerade so zu sagen in mein Bild eintritt und wieder austritt. Wobei ich den Eintrittspunkt ja bestimmen kann weil ich die Geradengleichung und die Y-Koordiante des Randes habe.

Aber ich vermute da noch ein Problem:

Wenn ich n immer um 1 erhöhe und m z.B. auch immer um 1. Überspinge ich dann nicht Pixel am anderen Ende der Geraden.
Oder: Ich reduziere die Schrittweite, dann erhalte ich doch oft viele Pixel doppelt, oder?

Das würde bedeuten mein Ergebnisbild verliert an Auflösung.

T
64 Beiträge seit 2011
vor 8 Jahren

Ja du sprichst da Probleme an die ich bei meiner Erklärung nicht beachtet habe. Senkrechte Gerade hatte ich zum Beispiel komplett vergessen. Um das zu lösen fallen mir spontan zwei Möglichkeiten ein. Zum einen könnte man die Geraden mit Hilfe der Hesseschen Normalform beschreiben oder du richtist dir Stützstellen für Sonderfälle ein.
Dabei musst du vorher wissen wie viele Geradendrehungen in dein Bild passen. Dann bestimmst du bei welchen n deine Gerade Senkrecht oder Waagerecht ist und es ergeben sich folgende Sonderfälle.

Gerade Senkrecht bzw. keine oder halbe Drehung: y=n
Gerade Waagerecht bzw. viertel oder dreivierteldrehung: x=n

Das zweite Problem was du ansprichst ist in etwa das was ich weiter oben mit dem Randproblem gemeint habe. Deine Geraden können kürzer oder länger werden als die eigentlich Bildbreite bzw. -höhe. Du könntest jede Zeile normieren bzw. interpolieren um alle auf die gleiche länge zu bringen aber das ist nicht gerade trivial, da du nur schwer schätzen kannst an welcher Stelle welcher Wert einzubringen ist.
Zum Testen reicht es ja erstmal wenn du dich an der kürzesten Gerade orientierst und die anderen auf diese entsprechende Länge kürzt. Anschließend skalierst du das entstandene Bild auf die ursprüngliche Bildgröße.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Aaaalso:

Ich habs jetzt so gemacht, dass ich die Steigung aus der dem Winkel berechne (nachdem ich mir an den Kopf gefasst hab, weil immer m+1 und n+1 den selben Schnittpunkt in der X-Achse erzeugt... ).
Dabei hab ich zum Testen den Winkel auf -80° beschränkt.

Mit y=m*x+n und m= tan(a) und a= Winkel zwischen x-Achse und Geraden, erhöhe ich jetzt pro Schritt den Winkel um 1° und den y-Achsenabschnitt n auch um 1.

Dabei will ich es so bewerkstelligen, dass der x-Koordinatenzähler für den Bresenham-Algorithmus immer beim Schnittpunkt mit der X-Achse anfängt und bis zum Ende des Bildes läuft.
Dabei aber der Zähler für die x-Koordinate, in die geschrieben wird, bei Null anfängt.
Dabei kann die Gerade dann kürzer sein, als das Bild breit ist - in dem Fall möchte ich abbrechen oder länger, in dem Fall müsste die Schleife aber vor dem Ende aufhören, da maximal bis zur Breite gezählt wird.

Die Farbe wird dabei mit getPixel gelesen und mit setPixel geschrieben.

Den Bresenham-Algorithmus hab ich nach https://www.youtube.com/watch?v=vlZFSzCIwoc gebaut.

Es funktioniert - sagen wir teilweise großes Grinsen siehe Anhang: Links Input, Rechts Output

Der wesentliche Code:


while (a >=-80 && n <= 80 )
                {
                    double radians;
                    radians = a * (Math.PI / 180);
                    M = Math.Tan(radians);
                    m = M;
                    Y = m * x + n;
                    if (m == 0) { x0 = 0; }
                    else
                    {

                        x0 = Convert.ToInt32(Math.Floor(-n/m));
                    }

                   

                    y0 = 0;

                    x1 = 399;
                    y1 = Convert.ToInt32(Math.Floor(m * 399 + n));
                    
                    dx = x1 - x0;
                    dy = y1 - y0;

                    // x = schnelle Richtung -> immer +1

                    fehler = dx / 2;
                    int i=0;
                    while (i <= 399)
                    {

                        
                        Color Farbe = Bild1.GetPixel(x0, y0);
                        int Rot = Farbe.R, Gruen = Farbe.G, Blau = Farbe.B;
                        fehler = fehler - dy;

                        Ausgabe.SetPixel(i, yA, Color.FromArgb(255, Farbe.R, Farbe.G, Farbe.B));

                        if (fehler < 0)
                        {
                            y0++;
                            fehler = fehler + dx;
                        }
                        else
                        {
                            //y0 = y0;
                            //fehler = fehler;
                        }
                        

                        x0++;
                        xA++;
                        if (x0 > 399) { break; }
                        i++;

                    }
                    
                    a--;
                    n++;
                    yA++;
                }

Mich wunderts, dass nach der gebogenen Schwarzen Linie kein Gelb kommt 🤔 Bzw. scheint da irgendwas falschrum zu sein.

Ich werde mal weiter suchen ^^

Edit:

Dabei ist mir noch eingefallen: Müsste ich nicht eigentlich immer die Gerade im ihren Mittelpunkt drehen und dann erst den Y-Achsenabschnitt verschieben?

Edit2: Wobei natürlich die Frage ist um welchen Punkt gedreht wird :<

Das ist irgendwie noch nicht so wirklich richtig.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Weils etwas unübersichtlich wird editier ich nicht sondern schriebs mal neu:

Meine Idee dazu ist jetzt, dass ich doch eigentlich lieber über die Punkt-Steigungsform rechne.
Dazu müsste ich aus der Bildmitte einen Kreis mit Radius Bildhöhe/2 berechnen. Den Radius würde ich dann quasi immer um einen Pixel reduzieren, nach Radius=0 wieder erhöhen.
Dabei dann den Winkel jeweils um 360/Bildhöhe erhöhen.

Aus den Punkten und den Winkeln müsste sich dann eine korrekte Gerade abbilden, oder?

5.658 Beiträge seit 2006
vor 8 Jahren

Hi Gimmick,

es tut mir leid, aber ich kann deinen Ausführungen nicht folgen. Und ich kann auch auf den geposteten Bilder nichts erkennen, was dir weiterhelfen könnte. Deine ursprüngliche Anforderung war doch:

Und zwar möchte ich ein Bild öffnen, anzeigen, schrittweise drehen, zeilenweise nach jedem Schritt in ein neuen Bild schreiben und das Ergebnis dann anzeigen.

An welcher Stelle hast du jetzt Probleme? Ist es die Implementierung des Bresenham-Algorithmus? Dafür gibt es fertige Implementierungen im Netz. Wenn deine Implementierung nicht tut, was sie soll, dann kannst du sie debuggen und Unit-testen, um die Fehlerursache einzugrenzen.

Bei deiner Idee mit dem Kreis kann ich leider nicht nachvollziehen, wie dir das dabei helfen sollte, das Bild zeilenweise abzutasten und zu drehen. Warum soll das Bild überhaupt zeilenweise gedreht werden? Das Framework bietet dir doch bereits Möglichkeiten, ein Bild im Ganzen zu drehen. Warum reicht das für deine Anforderungen nicht aus?

Christian

Weeks of programming can save you hours of planning

T
64 Beiträge seit 2011
vor 8 Jahren

@MrSparkle

Es soll so ein Effekt nachgebildet werden: Rolling Shutter Animation
Meine Idee zu dem Problem war es virtuell eine Gerade durch das Bild zu schieben welche sich beim Verschieben dreht. Über die Geradengleichung und dem Bresenham sollte man recht schnell die benötigten Koordinaten für das neue Bild erhalten ohne, dass man immer das originale Bild drehen muss.

@Gimmick

Ich würde dir dazu raten das ganze erstmal kleiner anzufangen. Schiebe als erstes einfach mal eine Gerade von unten nach oben durch dein Bild ohne diese zu drehen und teste ob das Ergebnisbild ein umgekehrtes Original ist.

Wenn das funktioniert testest du was passiert wenn du eine Gerade in der Mitte eines Bildes drehst und während der Drehung die Pixel auf der Gerade Zeilenweise in ein neues Bild schreibst. Die Anzahl der Drehschritte ergibt sich aus der Bildhöhe.Ich denke für den Anfang reicht eine Umdrehung für das Ergebnis. Also 360°/Bildhöhe sind deine Winkelinkremente.

Beachte aber bitte den Sonderfall Senkrecht, den musst du abfangen, da ein unendlich hohes m nicht realisierbar ist. Sobald du ein m erhälst welches größer ist als die Bildhöhe (oder ein -m welche kleiner ist als -Bildhöhe) ist das ein Anzeichen dafür, dass es sich um eine Senkrechte handelt und den Fall musst du dann gesondert behandeln um anschließend das Vorzeichen bei m zu ändern.
In etwa sollte es dann so aussehen:


double m = 0;
double increment_m = 1;

            for(int i=0;i<=2*originalPicture.Height;i++)
            {
                m += increment_m;
                if (Math.Abs(m) >= originalPicture.Height)
                {
                    //Senkrecht Sonderfall behandeln                    
                    
                    //Start und Endpunkte bestimmen
                    //Bresenham
                    //neue Bildzeile bestimmen

                    //Anschließend Anstiegsrichtung umdrehen
                    m *= -1;
                }
                else
                {
                    //m in Gleichung einsetzen
                    //Start und Endpunkte bestimmen
                    //Bresenham
                    //neue Bildzeile bestimmen
                }
            }

Wenn dies auch funktioniert sollte es anschließend kein Problem mehr sein beide Funktionen zu kombinieren.

5.658 Beiträge seit 2006
vor 8 Jahren

Hallo allerseits,

ich befürchte,daß der Bresenham-Algorithmus dabei zu Antialiasing-Effekten führen wird. Es gibt zwar Varianten für glatte Linien, aber das ist gemacht, um Linien zu zeichnen, und nicht für Bild-Transformationen.

Ich würde evtl. so an die Sache herangehen, daß ich eine Koordinaten-Transformation durchführen würde. D.h. jeder Pixel im Ergebnisbild entspricht einem Pixel im Ausgangsbild: C'(x', y') = C(x, y).

Die Transformation der X- und Y-Koordinaten entspricht einer Drehung um den Winkel, der durch die Y-Koordinate vorgegeben wird. Das ließe sich sehr einfach über die Winkelfunktionen berechnen, also einfach mit Sinus und Cosinus.

Dann kann man das Ergebnisbild Pixel für Pixel aufbauen, indem man für jede Koordinate die Koordinate im Originalbild berechnet und die Farbe von dort übernimmt. Das läßt sich sowohl in C# als auch in einem Hardware-Shader umsetzen und ist erweiterungsfähig. Wenn man z.B. im Originalbild nicht nur einen Pixel sampelt, sondern auch die Pixel runderherum, dann kann man Stufeneffekte (Aliasing) verhindern.

Christian

Weeks of programming can save you hours of planning

T
64 Beiträge seit 2011
vor 8 Jahren

*KopfgegenTisch*
Stimmt du hast recht. Eine Drehmatrix ist ja sogar noch einfacher und schneller.
Habs mal ganz einfach aufgebaut weil mich das Thema interessiert:


private void button1_Click(object sender, EventArgs e)
        {
            Bitmap orig = (Bitmap)pictureBox1.Image;
            Bitmap Shutter = new Bitmap(200, 200);


            for(int i=0;i<Shutter.Width;i++)
                for(int j=0;j<Shutter.Height;j++)
                {
                    double angle=(Math.PI*(3.6*i)/180);
                    int newX = Convert.ToInt32(i*Math.Cos(angle)-j*Math.Sin(angle));
                    int newY = Convert.ToInt32(i*Math.Sin(angle)+j*Math.Cos(angle));

                    if (newX<0)
                        newX=0;

                    if (newX>Shutter.Width-1)
                        newX=Shutter.Width-1;

                    if (newY < 0)
                        newY = 0;

                    if (newY > Shutter.Height - 1)
                        newY = Shutter.Height - 1;


                    Shutter.SetPixel(i, j, orig.GetPixel(newX, newY));
                }

            pictureBox2.Image = Shutter;
        }

Das Ergebnis ist im Anhang ersichtlich.

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Schön, dass das auch per Drehmatrix funktioniert, will ich aber grad nicht machen :X
Aber dennoch danke, kann ich ja evtl. dazupacken 😉 aber erst wenn die Methode mit der Geraden funktioniert. Ich hör doch jetzt nicht mittendrin auf 😁

@ Toxo: Um auf deine Anmerkung einzugehen:
Was funktioniert:

Gerade einfach von Oben nach Unten durch das Bild schieben und in neues Bild übertragen.
Gerade unter konstantem Winkel aber variierendem Radius durchs Bild laufen lassen und abbilden (kommt das gleiche raus).
Y-Koordinate aus Kreisfunktion mit varieerendem Winkel und Radius bestimmen und durchlaufen lassen. Da sieht man schon, dass durch Runden auf ganzzahlige Koordinaten was verloren geht. Ist aber nicht schlimm.

Jetzt kommt: Gerade um selben Winkel wie den Kreiswinkel neigen und entsprechende x/y Koordinaten als Start- und Endpunkt berechnen.

Wenn das klappt:

Drehmatrix, das muss ich mir aber erst anschauen. Hab ich noch nicht, eins nach dem anderen. Aber ich bin dankbar, dass du dich damit so beschäftigst.
Witzig wirds, wenn das ganze dann rückwärts laufen soll mit einer echten Aufnahme 😁

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Also, das mit der Linie funktioniert prinzipiell jetzt, ist aber total die Arbeit das um zu setzen.
Im Prinzip muss ich für jeden Sonderfall und jedes 90° Intervall eigene Bedingungen schreiben und zwei Versionen meines Bresemham-Algorithmus einbauen, da sich ja die Leserichtung ändert...
Zudem gibts durch die Rechungen von Winkel zu Steigung zu Koordinate Lücken. 🙁

Da habter wohl recht, besser ist das wohl per Rotationsmatrix.

@ Toxo: Du drehst aber glaube ich um die (0/0) Koordinate und nicht um den Mittelpunkt. ^^

Die Transformation der X- und Y-Koordinaten entspricht einer Drehung um den Winkel, der durch die Y-Koordinate vorgegeben wird. Das ließe sich sehr einfach über die Winkelfunktionen berechnen, also einfach mit Sinus und Cosinus.

Dann kann man das Ergebnisbild Pixel für Pixel aufbauen, indem man für jede Koordinate die Koordinate im Originalbild berechnet und die Farbe von dort übernimmt.

Christian

Weil ich hier ja um die Mitte rotieren müsste hätte ich doch dann:

x=x0 + r * cos (w) und y=y0 + r * sin (w)

Wobei x0 und y0 die Koordinaten des Kreismittelpunkts sind.

Wenn ich die Urpsrungskoordinate (0/0) nehme, den Kreis in die Bildmitte setze, und um den Winkel "Winkel" drehen will, müsste ich doch rechnen:

Ursprungswinkel von (0/0):

acos[(0-Bildbreite/2) / r] = w

mit r = sqrt[(Bildbreite/2 - 0)² + (Bildhöhe/2-0)²]

bzw.

asin[(0-Bildhöhe/2 / r)] = w

Um Winkel "Winkel" gedreht, wäre die neue Koordinate dann:

x_neu= Bildbreite/2 + r * cos (w + Winkel)

y_neu= Bildhöhe/2 + r * cos (w + Winkel)

Allerdings ist das ja nicht eindeutig. Die einzelnen x/y-Koordinaten haben ja in einer Periode immer zwei Lösungen.
Das heisst ich müsste 4 Fälle unterscheiden und dabei jeweils 90° dazu rechnen?

5.658 Beiträge seit 2006
vor 8 Jahren

Hi Gimmick,

Zudem gibts durch die Rechungen von Winkel zu Steigung zu Koordinate Lücken. 😦

Der Algorithmus ist doch eigentlich genau dazu da, daß es keine Lücken in den Linien gibt, oder wo treten bei dir Lücken auf?

Das heisst ich müsste 4 Fälle unterscheiden und dabei jeweils 90° dazu rechnen?

Wenn du eine Rotations-Matrix verwendest, mußt du nur eine Matrix erstellen. Das wäre die einfachste Möglichkeit. Siehe auch System.Drawing.Drawing2D.Matrix.Rotate-Methode bzw. System.Windows.Media.Matrix.Rotate-Methode.

Christian

Weeks of programming can save you hours of planning

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Der Algorithmus ist doch eigentlich genau dazu da, daß es keine Lücken in den Linien gibt, oder wo treten bei dir Lücken auf?

Da hab ich mich undeutlich ausgedrückt. Nicht die Linien haben Lücken, es gibt Bereiche zwischen den Linien, die nicht abgedekct werden, weil z.B. ganz am Rand auf Grund des Winkel ein Pixel übersprungen wird.

Wenn du eine Rotations-Matrix verwendest, mußt du nur eine Matrix erstellen. Das wäre die einfachste Möglichkeit. Siehe auch
>
bzw.
>
.

Christian

Das Problem ist, dass dabei standardmäßig um die 0/0 Koordinate gedreht wird und ich keine Ahnung habe wie ich das umgehen kann und mich das etwas überfordert x)

Was ich jetzt getan habe:
Mich auf alte Tugenden besonnen 😁

Das Bild wird geladen -> in die Mitte einer größeren weißen Fläche kopiert -> gedreht und nach jedem Drehschitt wird eine Zeile ausgelesen.

Bin mir was die Fehlerfreiheit angeht aber nicht so sicher. Die "Lesezeile" müsste von oben nach unten wandern.
Und ich bin mir sicher, dass man das alles geschickter und besser machen kann :<

Ist halt etwas langsam weil ständig irgendwas kopiert und ausgegeben wird. Aber ich hab das gebraucht um sehen zu können, dass alles klappt.


float Winkel = 0;
            Bitmap zwAusgabe = new Bitmap(Bild1.Width + 200, Bild1.Height + 200);
            Bitmap rotiert = new Bitmap(zwAusgabe.Width, zwAusgabe.Height);
            Bitmap Ausgabe = new Bitmap(zwAusgabe.Width, zwAusgabe.Height);

            if (Bild1 != null)
            {
                
                for(int i=0;i<zwAusgabe.Width;i++)
                    for (int j = 0; j < zwAusgabe.Height; j++)
                    { zwAusgabe.SetPixel(i, j, Color.White); }

                for(int i=0;i<Bild1.Width;i++)
                    for (int j = 0; j < Bild1.Height; j++)
                    {

                        zwAusgabe.SetPixel(i + 100, j + 100, Bild1.GetPixel(i, j));
                        pictureBox2.SizeMode = PictureBoxSizeMode.Zoom;
                        
                    }
                pictureBox2.Image = zwAusgabe;


                for (int i = 0; i < Ausgabe.Height; )
                {
                    for (int j = 0; j < Ausgabe.Width; )
                    {

                        Ausgabe.SetPixel(i, j, rotiert.GetPixel(i, j));
                        j++;

                    }


                    var graphics = Graphics.FromImage(rotiert);
                    graphics.TranslateTransform((float)zwAusgabe.Width / 2, (float)zwAusgabe.Height / 2);
                    graphics.RotateTransform(Winkel);
                    graphics.TranslateTransform(-(float)zwAusgabe.Width / 2, -(float)zwAusgabe.Height / 2);
                    graphics.DrawImage(zwAusgabe, new Point(0, 0));

                    float w = 0.06f;
                    Winkel = Winkel + w;
                    i++;
                }

                Bitmap EndAusgabe = new Bitmap(Bild1.Width, Bild1.Height);

                for (int i = 0; i < Bild1.Width; i++)
                    for (int j = 0; j < Bild1.Height; j++)
                    {

                        EndAusgabe.SetPixel(i , j , Ausgabe.GetPixel(i+100, j+100));
                        pictureBox2.SizeMode = PictureBoxSizeMode.Zoom;

                    }


                pictureBox3.SizeMode = PictureBoxSizeMode.Zoom;                    
                
                pictureBox3.Image = EndAusgabe;

Nächste Überlegung:

Wie bekommt man das wieder weg? 😁

Eigentlich müsste das doch genauso gehen. Nur eben ääh andersrumdrehen?

5.658 Beiträge seit 2006
vor 8 Jahren

Hi Gimmick,

ich weiß wirklich nicht, wie man dir weiterhelfen kann, wenn du die Antworten auf deine Fragen mit "überfordert mich" oder "will ich gerade nicht" abtust. Wie du eine Transformations-Matrix erstellst, die um einen beliebigen Punkt drehst, findest du doch sehr schnell in der Doku oder bei Google. Abgesehen davon, daß du es in deinem letzten Code-Auschnitt bereits richtig anwendest: Graphics.RotateTransform und Graphics.TranslateTransform machen ja auch nichts anderes, als eine Transformations-Matrix zu erstellen.

Daß es bei der Variante mit dem Bresenham-Algorithmus zu unterschiedlich langen Linien kommt, sollte auch klar sein. Denn eine vertikale oder horizontale Linie durch das Bild ist immer kürzer als eine Diagonale. Da muß du die Linien eben auf die Breite des Ausgabebildes beschneiden.

Insgesamt stellt sich mir die Frage, warum man sich als Anfänger ausgerechnet mit so einer relativ abwegig erscheinenden Problemstellung beschäftigen sollte. Wozu benötigst du diesen Effekt letztendlich? Ist er Teil eines größeren Programmes/Spieles/Effektbibliothek? Was hast du am Ende damit vor?

Letztendlich bildest du auch nur einen extremen Spezialfall des Rolling-Shutter-Effektes ab. Der Effekt entsteht ja (bei den entsprechend gebauten Kameras) bei jeder Art von Bewegung. Siehe z.B. die Bilder mit den Autos im Wikipedia-Artikel dazu: dort geht es nicht um eine Rotation, sondern um eine Translation.

Dein Beispiel ist in sofern nur ein Spezialfall, da du lediglich den Effekt für eine Rotation in der Kameraebene um genau 360° pro Verschlußzeit berechnest.

Christian

Weeks of programming can save you hours of planning

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 8 Jahren

Hi,

ja sehe ich alles ein. Mein Problem war auch einfach, dass viele Wege nach Rom führen und ich dann mal hier und mal da rumgedoktort habe. Im Nachhinein hab ich durch den Bresenham-Kram zwar einiges gelernt, aber hätte es lieber ganz lassen sollen und mich nur mit der ersten Idee beschäfigen sollen. Das gab einfach ein Durcheinander.

Zum Bresenham: Nicht die Länge ist das Problem, sondern dass das dy zwischen den Endpunkten zweier Linien mit unterschiedlicher Steigung mit größerem x natürlich auch größer wird. Und wenn die Linie z.B. innerhalb einer Umdrehung einmal von oben nach unten wandert hab ich für den Winkel nur 360/Bildhöhe Schrittgrößen. Da gabs dann in Richtung Rand Lücken.

Warum ich das mache:
Ich hab vor 2 Jahren oder so mal wegen einer Uniprojektarbeit in C ein Programm geschrieben was ein klein wenig (idealster Fall usw) Bilder deckungsgleich rückt. Und jetzt hatte ich den Rolling Shutter Effekt aufgeschnappt und wollte das halt mal machen. Im Nachhinein ein weiters Programm ohne besonderen Zweck in meiner Sammlung 😄

Abgesehen von der Geschwindigkeit bin ich mit der Lösung jetzt zufrieden.

5.658 Beiträge seit 2006
vor 8 Jahren

Eine andere naheliegende Optimierung wäre, bei jeder Drehung mit der entsprechenden Überladung von Graphics.DrawImage immer nur den Teil des Bildes zu zeichnen, den man später braucht. Also nur die jeweilige Zeile. Dann kann man sich dann auch gleich das darauffolgende zeilenweise Umkopieren ins Ausgabebild sparen.

Und wenn die Linie z.B. innerhalb einer Umdrehung einmal von oben nach unten wandert hab ich für den Winkel nur 360/Bildhöhe Schrittgrößen. Da gabs dann in Richtung Rand Lücken.

Die Lücken sollte es aber nur im Originalbild (also beim Lesen) geben. Lücken im Ausgabebild (also beim Schreiben) sollten nicht auftreten. Denn zum Ausgleich für die nicht abgetasteten Pixel im Randbereich tastest du die Pixel in der Bildmitte mehrmals ab. Dadurch gleicht es sich wieder aus.

Christian

Weeks of programming can save you hours of planning