MonochromeRectangleDetector.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Ashot
  5. * Date: 3/24/15
  6. * Time: 21:23
  7. */
  8. namespace Zxing\Common\Detector;
  9. use \Zxing\NotFoundException;
  10. use \Zxing\ResultPoint;
  11. use \Zxing\BitMatrix;
  12. /*
  13. *
  14. *
  15. import com.google.zxing.NotFoundException;
  16. import com.google.zxing.ResultPoint;
  17. import com.google.zxing.common.BitMatrix;
  18. */
  19. //require_once('./lib/NotFoundException.php');
  20. //require_once('./lib/ResultPoint.php');
  21. //require_once('./lib/common/BitMatrix.php');
  22. /**
  23. * <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
  24. * It looks within a mostly white region of an image for a region of black and white, but mostly
  25. * black. It returns the four corners of the region, as best it can determine.</p>
  26. *
  27. * @author Sean Owen
  28. * @port Ashot Khanamiryan
  29. */
  30. class MonochromeRectangleDetector {
  31. private static $MAX_MODULES = 32;
  32. private $image;
  33. function __construct($image){
  34. $this->image = $image;
  35. }
  36. /**
  37. * <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
  38. * white, in an image.</p>
  39. *
  40. * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
  41. * last points are opposed on the diagonal, as are the second and third. The first point will be
  42. * the topmost point and the last, the bottommost. The second point will be leftmost and the
  43. * third, the rightmost
  44. * @throws NotFoundException if no Data Matrix Code can be found
  45. */
  46. public function detect(){
  47. $height = $this->image->getHeight();
  48. $width = $this->image->getWidth();
  49. $halfHeight = $height / 2;
  50. $halfWidth = $width / 2;
  51. $deltaY = max(1, $height / (self::$MAX_MODULES * 8));
  52. $deltaX = max(1, $width / (self::$MAX_MODULES * 8));
  53. $top = 0;
  54. $bottom = $height;
  55. $left = 0;
  56. $right = $width;
  57. $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
  58. $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2);
  59. $top = (int) $pointA->getY() - 1;
  60. $pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left,$right,
  61. $halfHeight, 0, $top, $bottom, $halfHeight / 2);
  62. $left = (int) $pointB->getX() - 1;
  63. $pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right,
  64. $halfHeight, 0, $top, $bottom, $halfHeight / 2);
  65. $right = (int) $pointC->getX() + 1;
  66. $pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
  67. $halfHeight, $deltaY, $top, $bottom, $halfWidth / 2);
  68. $bottom = (int) $pointD->getY() + 1;
  69. // Go try to find po$A again with better information -- might have been off at first.
  70. $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
  71. $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4);
  72. return new ResultPoint( $pointA, $pointB, $pointC, $pointD );
  73. }
  74. /**
  75. * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
  76. * point which should be within the barcode.
  77. *
  78. * @param centerX center's x component (horizontal)
  79. * @param deltaX same as deltaY but change in x per step instead
  80. * @param left minimum value of x
  81. * @param right maximum value of x
  82. * @param centerY center's y component (vertical)
  83. * @param deltaY change in y per step. If scanning up this is negative; down, positive;
  84. * left or right, 0
  85. * @param top minimum value of y to search through (meaningless when di == 0)
  86. * @param bottom maximum value of y
  87. * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
  88. * the barcode
  89. * @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
  90. * @throws NotFoundException if such a point cannot be found
  91. */
  92. private function findCornerFromCenter($centerX,
  93. $deltaX,
  94. $left,
  95. $right,
  96. $centerY,
  97. $deltaY,
  98. $top,
  99. $bottom,
  100. $maxWhiteRun){
  101. $lastRange = null;
  102. for ($y = $centerY, $x = $centerX;
  103. $y < $bottom && $y >= $top && $x < $right && $x >= $left;
  104. $y += $deltaY, $x += $deltaX) {
  105. $range = 0;
  106. if ($deltaX == 0) {
  107. // horizontal slices, up and down
  108. $range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
  109. } else {
  110. // vertical slices, left and right
  111. $range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
  112. }
  113. if ($range == null) {
  114. if ($lastRange == null) {
  115. throw NotFoundException::getNotFoundInstance();
  116. }
  117. // lastRange was found
  118. if ($deltaX == 0) {
  119. $lastY = $y - $deltaY;
  120. if ($lastRange[0] < $centerX) {
  121. if ($lastRange[1] > $centerX) {
  122. // straddle, choose one or the other based on direction
  123. return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
  124. }
  125. return new ResultPoint($lastRange[0], $lastY);
  126. } else {
  127. return new ResultPoint($lastRange[1], $lastY);
  128. }
  129. } else {
  130. $lastX = $x - $deltaX;
  131. if ($lastRange[0] < $centerY) {
  132. if ($lastRange[1] > $centerY) {
  133. return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
  134. }
  135. return new ResultPoint($lastX, $lastRange[0]);
  136. } else {
  137. return new ResultPoint($lastX, $lastRange[1]);
  138. }
  139. }
  140. }
  141. $lastRange = $range;
  142. }
  143. throw NotFoundException::getNotFoundInstance();
  144. }
  145. /**
  146. * Computes the start and end of a region of pixels, either horizontally or vertically, that could
  147. * be part of a Data Matrix barcode.
  148. *
  149. * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
  150. * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
  151. * @param maxWhiteRun largest run of white pixels that can still be considered part of the
  152. * barcode region
  153. * @param minDim minimum pixel location, horizontally or vertically, to consider
  154. * @param maxDim maximum pixel location, horizontally or vertically, to consider
  155. * @param horizontal if true, we're scanning left-right, instead of up-down
  156. * @return int[] with start and end of found range, or null if no such range is found
  157. * (e.g. only white was found)
  158. */
  159. private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal){
  160. $center = ($minDim + $maxDim) / 2;
  161. // Scan left/up first
  162. $start = $center;
  163. while ($start >= $minDim) {
  164. if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
  165. $start--;
  166. } else {
  167. $whiteRunStart = $start;
  168. do {
  169. $start--;
  170. } while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
  171. $this->image->get($fixedDimension, $start)));
  172. $whiteRunSize = $whiteRunStart - $start;
  173. if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
  174. $start = $whiteRunStart;
  175. break;
  176. }
  177. }
  178. }
  179. $start++;
  180. // Then try right/down
  181. $end = $center;
  182. while ($end < $maxDim) {
  183. if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
  184. $end++;
  185. } else {
  186. $whiteRunStart = $end;
  187. do {
  188. $end++;
  189. } while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
  190. $this->image->get($fixedDimension, $end)));
  191. $whiteRunSize = $end - $whiteRunStart;
  192. if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
  193. $end = $whiteRunStart;
  194. break;
  195. }
  196. }
  197. }
  198. $end--;
  199. return $end > $start ? array($start, $end) : null;
  200. }
  201. }