hmm mir scheint meine Zeichenkünste sind nicht die besten
Mein Usecase ist das jemand mit ner Kamera etwas Fotographiert (als ersatz für einen Scanner), derjenige gibt sich natürlich die größte Mühe den Winkel Kamera Sichtachse zu Oberfläche möglichst nah bei 90° zu halten (so wie man das halt macht, wenn man ein Blatt abfotographiert). Mein Beispiel das ich da die ganze Zeit Nutze ist maximal Bösartig, weil ich absichtlich den Winkel der Kamera Sichtachse zur Blattoberfläche flach gewählt habe. wp_xyz Beschreibung führt das ins extreme und setzt diesen Winkel auf 0 (und muss dass man überhaupt etwas sieht dafür das Blatt in y-Achse verschieben, so dass das Blatt durch den Öffnungswinkel der Camera unten wieder Rein Rutscht).
Bisher ging ich davon aus, dass wenn ich 4 Punkte auf dem Blatt markiere und dem Rechner sage, dass diese ein Rechteck Bilden ich mehr oder minder unabhängig von beschriebenen Winkel das wieder zurück rechnen können sollte. Interessant ist halt der Grenzfall in dem der Code aus BGRA erstaunlich gut abschneidet.
Bisher habe ich gesehen dass dieser Code 2 Matrizen Bildet (Quelle -> Quadrat, Quadrat -> Ziel) und diese letzteendes zu einer zusammenmultipliziert die dann die Transformation macht.
In meinem Buch scheinen die durch lösen des GL das wohl direkt zu machen, aber so schnell bin ich noch nicht..
Perspektivisches Entzerren eines Bildes [gelöst]
- corpsman
- Lazarusforum e. V.
- Beiträge: 1498
- Registriert: Sa 28. Feb 2009, 08:54
- OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
- CPU-Target: 64Bit
- Wohnort: Stuttgart
- Kontaktdaten:
Re: Perspektifisches Entzerren eines Bildes
--
Just try it
Just try it
-
- Beiträge: 3444
- Registriert: Mo 11. Sep 2006, 10:24
- OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
- CPU-Target: X32 / X64 / ARMv5
- Wohnort: Krefeld
Re: Perspektivisches Entzerren eines Bildes
Mir wurde mal glaubhaft versichert, dass man (Profis) solche Projektionen am besten mit Quaternionen macht,
In dieser Anwendung ist der Vorteil, dass bei Quaternionen die normalen Rechenregeln gelten und es deshalb einfach sein sollte, die Umkehrfunktion (also Entzerrung statt Projektion) zu bestimmen.
-Michael
In dieser Anwendung ist der Vorteil, dass bei Quaternionen die normalen Rechenregeln gelten und es deshalb einfach sein sollte, die Umkehrfunktion (also Entzerrung statt Projektion) zu bestimmen.
-Michael
- corpsman
- Lazarusforum e. V.
- Beiträge: 1498
- Registriert: Sa 28. Feb 2009, 08:54
- OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
- CPU-Target: 64Bit
- Wohnort: Stuttgart
- Kontaktdaten:
Re: Perspektivisches Entzerren eines Bildes
Ich hab mich nun mal an dem Reverse Enginiering Versuch gemacht und mir den Source von BGRA angesehen.
Die stellen ihre 3x3 Matrix über Variablen dar und nicht als Matrix, ungewöhnlich aber ok
die Matrix sieht dann so aus
Im Code dreht sich dann alles um diese Stelle:
Wenn man dann die entsprechenden Stellen im Code mit Breakpoints versieht und meine nachprogrammierten Matrizen vergleicht zeigt mir der Debugger bei BGRA aber ab einem gewissen Punkt nur Unsinn an, muss wohl an den vielen const Array's liegen, bis zu dem Punkt sind meine Matrizen mit denen aus BGRA aber identisch.
Wenn ich dann aber die Ergebniss Matrix auf das Bild multipliziere kommt bei mir ein deutlich verzerrtes Bild und bei BGRA das richtige heraus. da muss ich also noch mal ran
[Edit]
*g* der Linux Debugger war freundlicher, hier zeigte sich, dass ich die Falsche Matrix invertiert habe. Nun muss ich nur noch den Fillpoly Teil nachprogrammieren, denn so wie es aussieht macht der auch noch mal ein Stretching des Bildes, so auf den Ersten Blick könnte es dann sogar stimmen
Die stellen ihre 3x3 Matrix über Variablen dar und nicht als Matrix, ungewöhnlich aber ok
die Matrix sieht dann so aus
Code: Alles auswählen
// ( sx shx tx )
// ( shy sy ty )
// ( w0 w1 w2 )
// t* = Transliereung , sx, shx, shy, sy = Dreh /Scheermatrix, w* = ??
Code: Alles auswählen
procedure TBGRADefaultBitmap.FillQuadPerspectiveMapping(pt1, pt2, pt3,
pt4: TPointF; texture: IBGRAScanner; tex1, tex2, tex3, tex4: TPointF;
ADrawMode: TDrawMode);
var
persp: TBGRAPerspectiveScannerTransform;
begin
persp := TBGRAPerspectiveScannerTransform.Create(texture,[tex1,tex2,tex3,tex4],[pt1,pt2,pt3,pt4]); // Berechnen der Transformations Matrix
FillPoly([pt1,pt2,pt3,pt4],persp,ADrawMode);
persp.Free;
end;
Wenn ich dann aber die Ergebniss Matrix auf das Bild multipliziere kommt bei mir ein deutlich verzerrtes Bild und bei BGRA das richtige heraus. da muss ich also noch mal ran
[Edit]
*g* der Linux Debugger war freundlicher, hier zeigte sich, dass ich die Falsche Matrix invertiert habe. Nun muss ich nur noch den Fillpoly Teil nachprogrammieren, denn so wie es aussieht macht der auch noch mal ein Stretching des Bildes, so auf den Ersten Blick könnte es dann sogar stimmen
Zuletzt geändert von corpsman am Mo 25. Jan 2021, 13:44, insgesamt 1-mal geändert.
--
Just try it
Just try it
- corpsman
- Lazarusforum e. V.
- Beiträge: 1498
- Registriert: Sa 28. Feb 2009, 08:54
- OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
- CPU-Target: 64Bit
- Wohnort: Stuttgart
- Kontaktdaten:
Re: Perspektivisches Entzerren eines Bildes
Mit Quartenionen Rechnen ist nicht schwer, ist wie mit Complexen Zahlen, nur halt mit mehr imaginären Anteilen .mschnell hat geschrieben: ↑Mo 25. Jan 2021, 11:29Mir wurde mal glaubhaft versichert, dass man (Profis) solche Projektionen am besten mit Quaternionen macht,
In dieser Anwendung ist der Vorteil, dass bei Quaternionen die normalen Rechenregeln gelten und es deshalb einfach sein sollte, die Umkehrfunktion (also Entzerrung statt Projektion) zu bestimmen.
-Michael
Das Problem ist es den Rechenweg zu kennnen *g*, siehe meinen Post vorher..
--
Just try it
Just try it
- corpsman
- Lazarusforum e. V.
- Beiträge: 1498
- Registriert: Sa 28. Feb 2009, 08:54
- OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
- CPU-Target: 64Bit
- Wohnort: Stuttgart
- Kontaktdaten:
Re: Perspektivisches Entzerren eines Bildes
Also mit dem folgenden Code und meiner MulImage Funktion kann man aus
Vorher.jpg
das hier extrahieren Jetzt muss ich es nur noch schaffen das selber zu machen und nicht den Umweg über BGRA nehmen zu müssen ..
Also ist schon mal bewiesen, dass es geht
Vorher.jpg
das hier extrahieren Jetzt muss ich es nur noch schaffen das selber zu machen und nicht den Umweg über BGRA nehmen zu müssen ..
Code: Alles auswählen
Procedure CorrectProjection(Const Bitmap: TBitmap; TopLeft, BottomLeft,
TopRight, BottomRight: TPoint);
Var
fp, tl, tr, bl, br: TVector2;
b, SourceBGRAbitmap: Tbgrabitmap;
_ap, _bp, _cp, _dp: TPointF;
Begin
If Not IntersectLines(TopLeft, BottomLeft - TopLeft, TopRight, TopRight - BottomRight, fp) Then exit;
If fp.y < 0 Then Begin
// Berechnen der Schnittpunkte mit der Oberen Bildkante
bl := v2(0, Bitmap.Height);
br := v2(Bitmap.Width, Bitmap.Height);
If Not IntersectLines(v2(0, 0), v2(1, 0), bl, fp - bl, tl) Then Raise exception.create('Invalid Error 3.');
If Not IntersectLines(v2(0, 0), v2(1, 0), br, fp - br, tr) Then Raise exception.create('Invalid Error 4.');
End
Else Begin
If fp.y > Bitmap.Height Then Begin
// Berechnen der Schnittpunkte mit der unteren Bildkante
tl := v2(0, 0);
tr := v2(Bitmap.Width, 0);
If Not IntersectLines(v2(0, Bitmap.Height), v2(1, 0), tl, fp - tl, bl) Then Raise exception.create('Invalid Error 7.');
If Not IntersectLines(v2(0, Bitmap.Height), v2(1, 0), tr, fp - tr, br) Then Raise exception.create('Invalid Error 8.');
End
Else Begin
// Der Fluchtpunkt liegt innerhalb des Bildes -> hier können wir nix machen, oder ?
exit;
End;
End;
_ap := Pointf(0, 0);
_bp := Pointf(0, Bitmap.Height);
_cp := Pointf(Bitmap.Width, Bitmap.Height);
_dp := Pointf(Bitmap.Width, 0);
SourceBGRAbitmap := TBGRABitmap.Create(Bitmap);
b := TBGRABitmap.Create(Bitmap.Width, Bitmap.Height);
b.FillQuadPerspectiveMapping(_ap, _bp, _cp, _dp, SourceBGRAbitmap, tl, bl, br, tr);
Bitmap.Assign(b);
SourceBGRAbitmap.free;
b.free;
// Ohne diese eigentlich Sinnlose Zeile ist das Ergebnis in der Paintbox müll
MulImage(Bitmap, IdentityMatrix3x3, imNone);
End;
- Dateianhänge
-
- Vorher.jpg (60.35 KiB) 3833 mal betrachtet
--
Just try it
Just try it
- corpsman
- Lazarusforum e. V.
- Beiträge: 1498
- Registriert: Sa 28. Feb 2009, 08:54
- OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
- CPU-Target: 64Bit
- Wohnort: Stuttgart
- Kontaktdaten:
Re: Perspektivisches Entzerren eines Bildes
Sodale, hab mein Buch gewältzt und nun hab ich es auch verstanden und selbst implementieren können, das Ergebnis ist das Selbe wie mit BGRA nur eben selbst gemacht
Der Algorithmus verlangt nun einfach 4 Punkte die auf zwei eigentlich Parallelen Linien liegen müssten und berechnet daraus die im vorherigen Post gezeigten Bilder.
Und für alle die das auch nachvollziehen wollen hier mein Code:
Der Algorithmus verlangt nun einfach 4 Punkte die auf zwei eigentlich Parallelen Linien liegen müssten und berechnet daraus die im vorherigen Post gezeigten Bilder.
Und für alle die das auch nachvollziehen wollen hier mein Code:
Code: Alles auswählen
(*
* Entfernt aus einem Bild die Verzerrung die durch die Projektion entstanden ist.
* Die Geraden durch (tl, bl) und (tr, br) müssen so gelegt werden, dass sie auf eigentlich Paralellen Geraden im Bild liegen.
* Anschliesend wird das Bild so umgerechnet, dass diese Geraden im Ergebnis Senkrechte sind.
*)
Procedure CorrectProjection(Const Bitmap: TBitmap; TopLeft, BottomLeft, TopRight, BottomRight: TPoint; Mode: TInterpolationMode = imBilinear);
(*
* Lösen der Allgemeinen Gleichung der Perspektifischen Verzeichnung durch Einsetzen der 4 Bekannten Punkte
*
* Formeln und Lösungen entnommen aus: ISBN 978-3-540-21888-3 Seiten: 230 und 231
*)
Function SetMatrixByPoints(Const x, x_: Array Of TVector2): TMatrixNxM;
Var
i: Integer;
Begin
result := ZeroNxM(8, 9);
For i := 0 To 3 Do Begin // Gleichung (15.9)
// * = x_
result[0, i] := 1;
result[1, i] := x[i].x;
result[2, i] := x[i].y;
result[3, i] := 0;
result[4, i] := 0;
result[5, i] := 0;
result[6, i] := -x_[i].x * x[i].x;
result[7, i] := -x_[i].x * x[i].y;
result[8, i] := x_[i].x;
// * = y_
result[0, i + 4] := 0;
result[1, i + 4] := 0;
result[2, i + 4] := 0;
result[3, i + 4] := 1;
result[4, i + 4] := x[i].x;
result[5, i + 4] := x[i].y;
result[6, i + 4] := -x_[i].y * x[i].x;
result[7, i + 4] := -x_[i].y * x[i].y;
result[8, i + 4] := x_[i].y;
End;
End;
(*
* Gleichung 15.7:
* a0 + a1*x + a2*y
* x'(x,y) = ----------------
* 1 + c1*x + c2*y
*
* b0 + b1*x + b2*y
* y'(x,y) = ----------------
* 1 + c1*x + c2*y
*)
Var
a0, a1, a2, b0, b1, b2, c1, c2: Single;
Procedure CreateFunctionParamsFromSolvedMatrix(Const M: TMatrixNxM);
Begin
a0 := M[8, 0];
a1 := M[8, 1];
a2 := M[8, 2];
b0 := M[8, 3];
b1 := M[8, 4];
b2 := M[8, 5];
c1 := M[8, 6];
c2 := M[8, 7];
End;
Function f(x, y: Single): TVector2; // Implementierung der Gleichung 15.7
Var
xt, yt, denominator: Single;
Begin
denominator := 1 + c1 * x + c2 * y;
xt := (a0 + a1 * x + a2 * y) / denominator;
yt := (b0 + b1 * x + b2 * y) / denominator;
result := v2(xt, yt);
End;
Var
Source_intf, Dest_intf: TLazIntfImage;
ImgHandle, ImgMaskHandle: HBitmap;
tl, bl, tr, br, // Die Eckpunkte des Quellbildes, das nachher auf das Zielbild Skalliert wird
p, // f(x,y)
fp: TVector2; // Der Fluchtpunkt
NI: TBitmap;
j, i: Integer;
m: TMatrixNxM;
Begin
// 1. Fluchtpunkt Berechnen
If IntersectLines(TopLeft, BottomLeft - TopLeft, TopRight, TopRight - BottomRight, fp) Then Begin
If fp.y < 0 Then Begin
// Berechnen der Schnittpunkte mit der Oberen Bildkante
bl := v2(0, Bitmap.Height);
br := v2(Bitmap.Width, Bitmap.Height);
If Not IntersectLines(v2(0, 0), v2(1, 0), bl, fp - bl, tl) Then Raise exception.create('Invalid Error 3.');
If Not IntersectLines(v2(0, 0), v2(1, 0), br, fp - br, tr) Then Raise exception.create('Invalid Error 4.');
End
Else Begin
If fp.y > Bitmap.Height Then Begin
// Berechnen der Schnittpunkte mit der unteren Bildkante
tl := v2(0, 0);
tr := v2(Bitmap.Width, 0);
If Not IntersectLines(v2(0, Bitmap.Height), v2(1, 0), tl, fp - tl, bl) Then Raise exception.create('Invalid Error 7.');
If Not IntersectLines(v2(0, Bitmap.Height), v2(1, 0), tr, fp - tr, br) Then Raise exception.create('Invalid Error 8.');
End
Else Begin
// Der Fluchtpunkt liegt innerhalb des Bildes -> hier können wir nix machen, oder ?
exit;
End;
End;
// Wir Berechnen die Matrix direkt Invertiert, der Mathe ist das Egal, aber dafür
// Können wir das Zielbild dann sauber Abtasten ;)
m := SetMatrixByPoints(
[v2(0, 0), v2(0, Bitmap.Height), v2(Bitmap.Width, Bitmap.Height), v2(bitmap.Width, 0)]
,
[tl, bl, br, tr]
);
GaussJordan(m); // GL-Lösen
CreateFunctionParamsFromSolvedMatrix(m);
// Nun muss nur noch das tl, tr, bl, br Rechteck auf das Ni Bild projiziert werden
ni := TBitmap.Create;
ni.Width := Bitmap.Width;
ni.Height := Bitmap.Height;
Source_intf := TLazIntfImage.Create(0, 0);
Source_intf.LoadFromBitmap(Bitmap.Handle, Bitmap.MaskHandle);
Dest_intf := TLazIntfImage.Create(0, 0);
Dest_intf.LoadFromBitmap(ni.Handle, ni.MaskHandle);
For j := 0 To ni.Height - 1 Do Begin
For i := 0 To ni.Width - 1 Do Begin
p := f(i, j);
SetPixel(Dest_intf, i, j, GetPixel(Source_intf, p.x, p.y, mode));
End;
End;
Dest_intf.CreateBitmaps(ImgHandle, ImgMaskHandle, false);
Bitmap.Handle := ImgHandle;
Bitmap.MaskHandle := ImgMaskHandle;
Source_intf.free;
Dest_intf.free;
ni.free;
End
Else Begin
// Die definierenden Geraden sind Paralell => Keine Modifikation notwendig
End;
End;
--
Just try it
Just try it