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.
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;