PerspectiveTransform::times()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 10
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 11
rs 9.9332
1
<?php
2
/**
3
 * Class PerspectiveTransform
4
 *
5
 * @created      17.01.2021
6
 * @author       ZXing Authors
7
 * @author       Smiley <[email protected]>
8
 * @copyright    2021 Smiley
9
 * @license      Apache-2.0
10
 */
11
12
namespace chillerlan\QRCode\Detector;
13
14
use function count;
15
16
/**
17
 * This class implements a perspective transform in two dimensions. Given four source and four
18
 * destination points, it will compute the transformation implied between them. The code is based
19
 * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
20
 *
21
 * @author Sean Owen
22
 */
23
final class PerspectiveTransform{
24
25
	private float $a11;
26
	private float $a12;
27
	private float $a13;
28
	private float $a21;
29
	private float $a22;
30
	private float $a23;
31
	private float $a31;
32
	private float $a32;
33
	private float $a33;
34
35
	/**
36
	 *
37
	 */
38
	private function set(
39
		float $a11, float $a21, float $a31,
40
		float $a12, float $a22, float $a32,
41
		float $a13, float $a23, float $a33
42
	):self{
43
		$this->a11 = $a11;
44
		$this->a12 = $a12;
45
		$this->a13 = $a13;
46
		$this->a21 = $a21;
47
		$this->a22 = $a22;
48
		$this->a23 = $a23;
49
		$this->a31 = $a31;
50
		$this->a32 = $a32;
51
		$this->a33 = $a33;
52
53
		return $this;
54
	}
55
56
	/**
57
	 * @SuppressWarnings(PHPMD.ExcessiveParameterList)
58
	 */
59
	public function quadrilateralToQuadrilateral(
60
		float $x0, float $y0, float $x1, float $y1, float $x2, float $y2, float $x3, float $y3,
61
		float $x0p, float $y0p, float $x1p, float $y1p, float $x2p, float $y2p, float $x3p, float $y3p
62
	):self{
63
		return (new self)
64
			->squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p)
65
			->times($this->quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3));
66
	}
67
68
	/**
69
	 *
70
	 */
71
	private function quadrilateralToSquare(
72
		float $x0, float $y0, float $x1, float $y1,
73
		float $x2, float $y2, float $x3, float $y3
74
	):self{
75
		// Here, the adjoint serves as the inverse:
76
		return $this
77
			->squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
78
			->buildAdjoint();
79
	}
80
81
	/**
82
	 *
83
	 */
84
	private function buildAdjoint():self{
85
		// Adjoint is the transpose of the cofactor matrix:
86
		return $this->set(
87
			($this->a22 * $this->a33 - $this->a23 * $this->a32),
88
			($this->a23 * $this->a31 - $this->a21 * $this->a33),
89
			($this->a21 * $this->a32 - $this->a22 * $this->a31),
90
			($this->a13 * $this->a32 - $this->a12 * $this->a33),
91
			($this->a11 * $this->a33 - $this->a13 * $this->a31),
92
			($this->a12 * $this->a31 - $this->a11 * $this->a32),
93
			($this->a12 * $this->a23 - $this->a13 * $this->a22),
94
			($this->a13 * $this->a21 - $this->a11 * $this->a23),
95
			($this->a11 * $this->a22 - $this->a12 * $this->a21)
96
		);
97
	}
98
99
	/**
100
	 *
101
	 */
102
	private function squareToQuadrilateral(
103
		float $x0, float $y0, float $x1, float $y1,
104
		float $x2, float $y2, float $x3, float $y3
105
	):self{
106
		$dx3 = ($x0 - $x1 + $x2 - $x3);
107
		$dy3 = ($y0 - $y1 + $y2 - $y3);
108
109
		if($dx3 === 0.0 && $dy3 === 0.0){
0 ignored issues
show
introduced by
The condition $dx3 === 0.0 is always false.
Loading history...
110
			// Affine
111
			return $this->set(($x1 - $x0), ($x2 - $x1), $x0, ($y1 - $y0), ($y2 - $y1), $y0, 0.0, 0.0, 1.0);
112
		}
113
114
		$dx1         = ($x1 - $x2);
115
		$dx2         = ($x3 - $x2);
116
		$dy1         = ($y1 - $y2);
117
		$dy2         = ($y3 - $y2);
118
		$denominator = ($dx1 * $dy2 - $dx2 * $dy1);
119
		$a13         = (($dx3 * $dy2 - $dx2 * $dy3) / $denominator);
120
		$a23         = (($dx1 * $dy3 - $dx3 * $dy1) / $denominator);
121
122
		return $this->set(
123
			($x1 - $x0 + $a13 * $x1),
124
			($x3 - $x0 + $a23 * $x3),
125
			$x0,
126
			($y1 - $y0 + $a13 * $y1),
127
			($y3 - $y0 + $a23 * $y3),
128
			$y0,
129
			$a13,
130
			$a23,
131
			1.0
132
		);
133
	}
134
135
	/**
136
	 *
137
	 */
138
	private function times(PerspectiveTransform $other):self{
139
		return $this->set(
140
			($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13),
141
			($this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23),
142
			($this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33),
143
			($this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13),
144
			($this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23),
145
			($this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33),
146
			($this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13),
147
			($this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23),
148
			($this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33)
149
		);
150
	}
151
152
	/**
153
	 *
154
	 */
155
	public function transformPoints(array &$xValues, array &$yValues = null):void{
156
		$max = count($xValues);
157
158
		if($yValues !== null){
159
160
			for($i = 0; $i < $max; $i++){
161
				$x           = $xValues[$i];
162
				$y           = $yValues[$i];
163
				$denominator = ($this->a13 * $x + $this->a23 * $y + $this->a33);
164
				$xValues[$i] = (($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator);
165
				$yValues[$i] = (($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator);
166
			}
167
168
			return;
169
		}
170
171
		for($i = 0; $i < $max; $i += 2){
172
			$x                 = $xValues[$i];
173
			$y                 = $xValues[($i + 1)];
174
			$denominator       = ($this->a13 * $x + $this->a23 * $y + $this->a33);
175
			$xValues[$i]       = (($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator);
176
			$xValues[($i + 1)] = (($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator);
177
		}
178
	}
179
180
}
181