BaiduFMX
346 строк · 8.6 Кб
1unit PerspectiveCorrect;
2
3interface
4
5uses
6System.Math,
7SimpleSVD,
8GR32,
9GR32_Transforms,
10GR32_Math,
11GR32_Blend;
12
13type
14TMyProjectiveTransformation = class(T3x3Transformation)
15private
16FDestHeight: TFloat;
17FDestWidth: TFloat;
18FQuadX: array [0..3] of TFloat;
19FQuadY: array [0..3] of TFloat;
20function GetDestHeight: TFloat;
21function GetDestWidth: TFloat;
22procedure SetX0(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
23procedure SetX1(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
24procedure SetX2(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
25procedure SetX3(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
26procedure SetY0(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
27procedure SetY1(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
28procedure SetY2(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
29procedure SetY3(Value: TFloat); {$IFDEF UseInlining} inline; {$ENDIF}
30protected
31procedure PrepareTransform; override;
32procedure ReverseTransformFixed(DstX, DstY: TFixed; out SrcX, SrcY: TFixed); override;
33procedure ReverseTransformFloat(DstX, DstY: TFloat; out SrcX, SrcY: TFloat); override;
34procedure TransformFixed(SrcX, SrcY: TFixed; out DstX, DstY: TFixed); override;
35procedure TransformFloat(SrcX, SrcY: TFloat; out DstX, DstY: TFloat); override;
36public
37function GetTransformedBounds(const ASrcRect: TFloatRect): TFloatRect; override;
38property DestWidth: TFloat read GetDestWidth;
39property DestHeight: TFloat read GetDestHeight;
40published
41property X0: TFloat read FQuadX[0] write SetX0;
42property X1: TFloat read FQuadX[1] write SetX1;
43property X2: TFloat read FQuadX[2] write SetX2;
44property X3: TFloat read FQuadX[3] write SetX3;
45property Y0: TFloat read FQuadY[0] write SetY0;
46property Y1: TFloat read FQuadY[1] write SetY1;
47property Y2: TFloat read FQuadY[2] write SetY2;
48property Y3: TFloat read FQuadY[3] write SetY3;
49end;
50
51function getPerspectiveTransform(const src,
52dst: TArray<TFloatPoint>): TFloatMatrix;
53implementation
54
55function getPerspectiveTransform(const src,
56dst: TArray<TFloatPoint>): TFloatMatrix;
57const
58DECOMP_SVD = 1;
59
60var
61A, b, c: TMatrix;
62M, N, i: Integer;
63svd_deomposition: TSVD;
64A_pinv: TMatrix;
65Mat: TFloatMatrix;
66begin
67M := 8;
68N := 8;
69A := TMatrix.Create(M, N, 'A', TMatrix.AS_MATRIX);
70b := TMatrix.Create(M, 1, 'b', TMatrix.AS_VECTOR);
71c := TMatrix.Create(N, 1, 'c', TMatrix.AS_VECTOR);
72
73for i := 0 to 3 do
74begin
75A[i,0] := src[i].X;
76A[i+4,3] := src[i].X;
77A[i,1] := src[i].Y;
78a[i+4,4] := src[i].Y;
79A[i,2] := 1;
80A[i+4,5] := 1;
81A[i,3] := 0;
82A[i,4] := 0;
83A[i,5] := 0;
84A[i+4,0] := 0;
85A[i+4,1] := 0;
86A[i+4,2] := 0;
87A[i,6] := -src[i].x*dst[i].x;
88A[i,7] := -src[i].y*dst[i].x;
89A[i+4,6] := -src[i].x*dst[i].y;
90A[i+4,7] := -src[i].y*dst[i].y;
91B.Mat[i] := dst[i].x;
92B.Mat[i+4] := dst[i].y;
93end;
94svd_deomposition := TSVD.Create;
95A_pinv := nil;
96try
97A_pinv := svd_deomposition.PinvCompute(A, A.Rows, A.Cols);
98svd_deomposition.multiply(A_pinv, b, c);
99Mat[0][0] := c.Mat[0];
100Mat[1][0] := c.Mat[1];
101Mat[2][0] := c.Mat[2];
102Mat[0][1] := c.Mat[3];
103Mat[1][1] := c.Mat[4];
104Mat[2][1] := c.Mat[5];
105Mat[0][2] := c.Mat[6];
106Mat[1][2] := c.Mat[7];
107Mat[2][2] := 1;
108Result := Mat;
109finally
110A_pinv.Free;
111svd_deomposition.Free;
112end;
113end;
114
115{ TMyProjectiveTransformation }
116
117function TMyProjectiveTransformation.GetDestHeight: TFloat;
118begin
119if not TransformValid then PrepareTransform;
120Result := FDestHeight;
121end;
122
123function TMyProjectiveTransformation.GetDestWidth: TFloat;
124begin
125if not TransformValid then PrepareTransform;
126Result := FDestWidth;
127end;
128
129function TMyProjectiveTransformation.GetTransformedBounds(
130const ASrcRect: TFloatRect): TFloatRect;
131var
132p0, p1, p2, p3: TFloatPoint;
133begin
134p0 := Transform(FloatPoint(X0, Y0));
135p1 := Transform(FloatPoint(X1, Y1));
136p2 := Transform(FloatPoint(X2, Y2));
137p3 := Transform(FloatPoint(X3, Y3));
138
139// p0 := (FloatPoint(X0, Y0));
140// p1 := (FloatPoint(X1, Y1));
141// p2 := (FloatPoint(X2, Y2));
142// p3 := (FloatPoint(X3, Y3));
143
144Result.Left := Min(Min(p0.X, p1.X), Min(p2.X, p3.X));
145Result.Right := Max(Max(p0.X, p1.X), Max(p2.X, p3.X));
146Result.Top := Min(Min(p0.Y, p1.Y), Min(p2.Y, p3.Y));
147Result.Bottom := Max(Max(p0.Y, p1.Y), Max(p2.Y, p3.Y));
148end;
149
150function Distance(P1, P2: TFloatPoint): Single;
151begin
152Result := Sqrt(Sqr(P1.X - P2.X) + Sqr(P1.Y - P2.Y));
153end;
154
155procedure TMyProjectiveTransformation.PrepareTransform;
156var
157widthA, widthB, heightA, heightB: TFloat;
158src, dst: TArray<TFloatPoint>;
159begin
160src := TArray<TFloatPoint>.Create(
161FloatPoint(X0, Y0),
162FloatPoint(X1, Y1),
163FloatPoint(X2, Y2),
164FloatPoint(X3, Y3)
165);
166widthA := Distance(src[2], src[3]);
167widthB := Distance(src[1], src[0]);
168FDestWidth := Max(widthA, widthB);
169
170heightA := Distance(src[1], src[2]);
171heightB := Distance(src[0], src[3]);
172FDestHeight := Max(heightA, heightB);
173
174dst := TArray<TFloatPoint>.Create(
175FloatPoint(0, 0),
176FloatPoint(FDestWidth, 0),
177FloatPoint(FDestWidth, FDestHeight),
178FloatPoint(0, FDestHeight)
179);
180
181FMatrix := getPerspectiveTransform(src, dst);
182// Invert(FMatrix);
183inherited;
184end;
185
186procedure TMyProjectiveTransformation.ReverseTransformFixed(DstX, DstY: TFixed;
187out SrcX, SrcY: TFixed);
188var
189Z: TFixed;
190Zf: TFloat;
191begin
192Z := FixedMul(FInverseFixedMatrix[0, 2], DstX) +
193FixedMul(FInverseFixedMatrix[1, 2], DstY) + FInverseFixedMatrix[2, 2];
194
195if Z = 0 then Exit;
196
197{$IFDEF UseInlining}
198SrcX := FixedMul(DstX, FInverseFixedMatrix[0, 0]) +
199FixedMul(DstY, FInverseFixedMatrix[1, 0]) + FInverseFixedMatrix[2, 0];
200SrcY := FixedMul(DstX, FInverseFixedMatrix[0,1]) +
201FixedMul(DstY, FInverseFixedMatrix[1, 1]) + FInverseFixedMatrix[2, 1];
202{$ELSE}
203inherited;
204{$ENDIF}
205
206if Z <> FixedOne then
207begin
208EMMS;
209Zf := FixedOne / Z;
210SrcX := Round(SrcX * Zf);
211SrcY := Round(SrcY * Zf);
212end;
213end;
214
215procedure TMyProjectiveTransformation.ReverseTransformFloat(DstX, DstY: TFloat;
216out SrcX, SrcY: TFloat);
217var
218Z: TFloat;
219begin
220EMMS;
221Z := FInverseMatrix[0, 2] * DstX + FInverseMatrix[1, 2] * DstY +
222FInverseMatrix[2, 2];
223
224if Z = 0 then Exit;
225
226{$IFDEF UseInlining}
227SrcX := DstX * FInverseMatrix[0, 0] + DstY * FInverseMatrix[1, 0] +
228FInverseMatrix[2, 0];
229SrcY := DstX * FInverseMatrix[0, 1] + DstY * FInverseMatrix[1, 1] +
230FInverseMatrix[2, 1];
231{$ELSE}
232inherited;
233{$ENDIF}
234
235if Z <> 1 then
236begin
237Z := 1 / Z;
238SrcX := SrcX * Z;
239SrcY := SrcY * Z;
240end;
241end;
242
243procedure TMyProjectiveTransformation.SetX0(Value: TFloat);
244begin
245FQuadX[0] := Value;
246Changed;
247end;
248
249procedure TMyProjectiveTransformation.SetX1(Value: TFloat);
250begin
251FQuadX[1] := Value;
252Changed;
253end;
254
255procedure TMyProjectiveTransformation.SetX2(Value: TFloat);
256begin
257FQuadX[2] := Value;
258Changed;
259end;
260
261procedure TMyProjectiveTransformation.SetX3(Value: TFloat);
262begin
263FQuadX[3] := Value;
264Changed;
265end;
266
267procedure TMyProjectiveTransformation.SetY0(Value: TFloat);
268begin
269FQuadY[0] := Value;
270Changed;
271end;
272
273procedure TMyProjectiveTransformation.SetY1(Value: TFloat);
274begin
275FQuadY[1] := Value;
276Changed;
277end;
278
279procedure TMyProjectiveTransformation.SetY2(Value: TFloat);
280begin
281FQuadY[2] := Value;
282Changed;
283end;
284
285procedure TMyProjectiveTransformation.SetY3(Value: TFloat);
286begin
287FQuadY[3] := Value;
288Changed;
289end;
290
291
292procedure TMyProjectiveTransformation.TransformFixed(SrcX, SrcY: TFixed;
293out DstX, DstY: TFixed);
294var
295Z: TFixed;
296Zf: TFloat;
297begin
298Z := FixedMul(FFixedMatrix[0, 2], SrcX) +
299FixedMul(FFixedMatrix[1, 2], SrcY) + FFixedMatrix[2, 2];
300
301if Z = 0 then Exit;
302
303{$IFDEF UseInlining}
304DstX := FixedMul(SrcX, FFixedMatrix[0, 0]) +
305FixedMul(SrcY, FFixedMatrix[1, 0]) + FFixedMatrix[2, 0];
306DstY := FixedMul(SrcX, FFixedMatrix[0, 1]) +
307FixedMul(SrcY, FFixedMatrix[1, 1]) + FFixedMatrix[2, 1];
308{$ELSE}
309inherited;
310{$ENDIF}
311
312if Z <> FixedOne then
313begin
314EMMS;
315Zf := FixedOne / Z;
316DstX := Round(DstX * Zf);
317DstY := Round(DstY * Zf);
318end;
319end;
320
321procedure TMyProjectiveTransformation.TransformFloat(SrcX, SrcY: TFloat;
322out DstX, DstY: TFloat);
323var
324Z: TFloat;
325begin
326EMMS;
327Z := FMatrix[0, 2] * SrcX + FMatrix[1, 2] * SrcY + FMatrix[2, 2];
328
329if Z = 0 then Exit;
330
331{$IFDEF UseInlining}
332DstX := SrcX * Matrix[0, 0] + SrcY * Matrix[1, 0] + Matrix[2, 0];
333DstY := SrcX * Matrix[0, 1] + SrcY * Matrix[1, 1] + Matrix[2, 1];
334{$ELSE}
335inherited;
336{$ENDIF}
337
338if Z <> 1 then
339begin
340Z := 1 / Z;
341DstX := DstX * Z;
342DstY := DstY * Z;
343end;
344end;
345
346end.
347