PerspectiveTransform.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. /*
  3. * Copyright 2007 ZXing authors
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace Zxing\Common;
  18. /**
  19. * <p>This class implements a perspective transform in two dimensions. Given four source and four
  20. * destination points, it will compute the transformation implied between them. The code is based
  21. * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
  22. *
  23. * @author Sean Owen
  24. */
  25. final class PerspectiveTransform
  26. {
  27. private function __construct(private $a11, private $a21, private $a31, private $a12, private $a22, private $a32, private $a13, private $a23, private $a33)
  28. {
  29. }
  30. public static function quadrilateralToQuadrilateral(
  31. $x0,
  32. $y0,
  33. $x1,
  34. $y1,
  35. $x2,
  36. $y2,
  37. $x3,
  38. $y3,
  39. $x0p,
  40. $y0p,
  41. $x1p,
  42. $y1p,
  43. $x2p,
  44. $y2p,
  45. $x3p,
  46. $y3p
  47. ) {
  48. $qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
  49. $sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
  50. return $sToQ->times($qToS);
  51. }
  52. public static function quadrilateralToSquare(
  53. $x0,
  54. $y0,
  55. $x1,
  56. $y1,
  57. $x2,
  58. $y2,
  59. $x3,
  60. $y3
  61. ) {
  62. // Here, the adjoint serves as the inverse:
  63. return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
  64. }
  65. public function buildAdjoint(): \Zxing\Common\PerspectiveTransform
  66. {
  67. // Adjoint is the transpose of the cofactor matrix:
  68. return new PerspectiveTransform(
  69. $this->a22 * $this->a33 - $this->a23 * $this->a32,
  70. $this->a23 * $this->a31 - $this->a21 * $this->a33,
  71. $this->a21 * $this->a32 - $this->a22 * $this->a31,
  72. $this->a13 * $this->a32 - $this->a12 * $this->a33,
  73. $this->a11 * $this->a33 - $this->a13 * $this->a31,
  74. $this->a12 * $this->a31 - $this->a11 * $this->a32,
  75. $this->a12 * $this->a23 - $this->a13 * $this->a22,
  76. $this->a13 * $this->a21 - $this->a11 * $this->a23,
  77. $this->a11 * $this->a22 - $this->a12 * $this->a21
  78. );
  79. }
  80. public static function squareToQuadrilateral(
  81. $x0,
  82. $y0,
  83. $x1,
  84. $y1,
  85. $x2,
  86. $y2,
  87. $x3,
  88. $y3
  89. ): \Zxing\Common\PerspectiveTransform {
  90. $dx3 = $x0 - $x1 + $x2 - $x3;
  91. $dy3 = $y0 - $y1 + $y2 - $y3;
  92. if ($dx3 == 0.0 && $dy3 == 0.0) {
  93. // Affine
  94. return new PerspectiveTransform(
  95. $x1 - $x0,
  96. $x2 - $x1,
  97. $x0,
  98. $y1 - $y0,
  99. $y2 - $y1,
  100. $y0,
  101. 0.0,
  102. 0.0,
  103. 1.0
  104. );
  105. } else {
  106. $dx1 = $x1 - $x2;
  107. $dx2 = $x3 - $x2;
  108. $dy1 = $y1 - $y2;
  109. $dy2 = $y3 - $y2;
  110. $denominator = $dx1 * $dy2 - $dx2 * $dy1;
  111. $a13 = ($dx3 * $dy2 - $dx2 * $dy3) / $denominator;
  112. $a23 = ($dx1 * $dy3 - $dx3 * $dy1) / $denominator;
  113. return new PerspectiveTransform(
  114. $x1 - $x0 + $a13 * $x1,
  115. $x3 - $x0 + $a23 * $x3,
  116. $x0,
  117. $y1 - $y0 + $a13 * $y1,
  118. $y3 - $y0 + $a23 * $y3,
  119. $y0,
  120. $a13,
  121. $a23,
  122. 1.0
  123. );
  124. }
  125. }
  126. public function times($other): \Zxing\Common\PerspectiveTransform
  127. {
  128. return new PerspectiveTransform(
  129. $this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13,
  130. $this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23,
  131. $this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33,
  132. $this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13,
  133. $this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23,
  134. $this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33,
  135. $this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13,
  136. $this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23,
  137. $this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33
  138. );
  139. }
  140. public function transformPoints(&$points, &$yValues = 0): void
  141. {
  142. if ($yValues) {
  143. $this->transformPoints_($points, $yValues);
  144. return;
  145. }
  146. $max = is_countable($points) ? count($points) : 0;
  147. $a11 = $this->a11;
  148. $a12 = $this->a12;
  149. $a13 = $this->a13;
  150. $a21 = $this->a21;
  151. $a22 = $this->a22;
  152. $a23 = $this->a23;
  153. $a31 = $this->a31;
  154. $a32 = $this->a32;
  155. $a33 = $this->a33;
  156. for ($i = 0; $i < $max; $i += 2) {
  157. $x = $points[$i];
  158. $y = $points[$i + 1];
  159. $denominator = $a13 * $x + $a23 * $y + $a33;
  160. $points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator;
  161. $points[$i + 1] = ($a12 * $x + $a22 * $y + $a32) / $denominator;
  162. }
  163. }
  164. public function transformPoints_(&$xValues, &$yValues): void
  165. {
  166. $n = is_countable($xValues) ? count($xValues) : 0;
  167. for ($i = 0; $i < $n; $i++) {
  168. $x = $xValues[$i];
  169. $y = $yValues[$i];
  170. $denominator = $this->a13 * $x + $this->a23 * $y + $this->a33;
  171. $xValues[$i] = ($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator;
  172. $yValues[$i] = ($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator;
  173. }
  174. }
  175. }