| <?php |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| namespace Zxing\Qrcode\Detector; |
|
|
| use Zxing\NotFoundException; |
| use Zxing\ResultPointCallback; |
| use Zxing\Common\BitMatrix; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| final class AlignmentPatternFinder |
| { |
| private $image; |
| private $possibleCenters; |
| private $startX; |
| private $startY; |
| private $width; |
| private $height; |
| private $moduleSize; |
| private $crossCheckStateCount; |
| private $resultPointCallback; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function __construct($image, |
| $startX, |
| $startY, |
| $width, |
| $height, |
| $moduleSize, |
| $resultPointCallback) |
| { |
| $this->image = $image; |
| $this->possibleCenters = []; |
| $this->startX = $startX; |
| $this->startY = $startY; |
| $this->width = $width; |
| $this->height = $height; |
| $this->moduleSize = $moduleSize; |
| $this->crossCheckStateCount = []; |
| $this->resultPointCallback = $resultPointCallback; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function find() |
| { |
| $startX = $this->startX; |
| $height = $this->height; |
| $maxJ = $startX + $this->width; |
| $middleI = $this->startY + ($height / 2); |
| |
| |
| $stateCount = []; |
| for ($iGen = 0; $iGen < $height; $iGen++) { |
| |
| $i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2)); |
| $i = (int)($i); |
| $stateCount[0] = 0; |
| $stateCount[1] = 0; |
| $stateCount[2] = 0; |
| $j = $startX; |
| |
| |
| |
| while ($j < $maxJ && !$this->image->get($j, $i)) { |
| $j++; |
| } |
| $currentState = 0; |
| while ($j < $maxJ) { |
| if ($this->image->get($j, $i)) { |
| |
| if ($currentState == 1) { |
| $stateCount[$currentState]++; |
| } else { |
| if ($currentState == 2) { |
| if ($this->foundPatternCross($stateCount)) { |
| $confirmed = $this->handlePossibleCenter($stateCount, $i, $j); |
| if ($confirmed != null) { |
| return $confirmed; |
| } |
| } |
| $stateCount[0] = $stateCount[2]; |
| $stateCount[1] = 1; |
| $stateCount[2] = 0; |
| $currentState = 1; |
| } else { |
| $stateCount[++$currentState]++; |
| } |
| } |
| } else { |
| if ($currentState == 1) { |
| $currentState++; |
| } |
| $stateCount[$currentState]++; |
| } |
| $j++; |
| } |
| if ($this->foundPatternCross($stateCount)) { |
| $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ); |
| if ($confirmed != null) { |
| return $confirmed; |
| } |
| } |
|
|
| } |
|
|
| |
| |
| if (count($this->possibleCenters)) { |
| return $this->possibleCenters[0]; |
| } |
|
|
| throw NotFoundException::getNotFoundInstance(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| private function foundPatternCross($stateCount) |
| { |
| $moduleSize = $this->moduleSize; |
| $maxVariance = $moduleSize / 2.0; |
| for ($i = 0; $i < 3; $i++) { |
| if (abs($moduleSize - $stateCount[$i]) >= $maxVariance) { |
| return false; |
| } |
| } |
|
|
| return true; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private function handlePossibleCenter($stateCount, $i, $j) |
| { |
| $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2]; |
| $centerJ = $this->centerFromEnd($stateCount, $j); |
| $centerI = $this->crossCheckVertical($i, (int)$centerJ, 2 * $stateCount[1], $stateCountTotal); |
| if (!is_nan($centerI)) { |
| $estimatedModuleSize = (float)($stateCount[0] + $stateCount[1] + $stateCount[2]) / 3.0; |
| foreach ($this->possibleCenters as $center) { |
| |
| if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) { |
| return $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize); |
| } |
| } |
| |
| $point = new AlignmentPattern($centerJ, $centerI, $estimatedModuleSize); |
| $this->possibleCenters[] = $point; |
| if ($this->resultPointCallback != null) { |
| $this->resultPointCallback->foundPossibleResultPoint($point); |
| } |
| } |
|
|
| return null; |
| } |
|
|
| |
| |
| |
| |
| private static function centerFromEnd($stateCount, $end) |
| { |
| return (float)($end - $stateCount[2]) - $stateCount[1] / 2.0; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private function crossCheckVertical($startI, $centerJ, $maxCount, |
| $originalStateCountTotal) |
| { |
| $image = $this->image; |
|
|
| $maxI = $image->getHeight(); |
| $stateCount = $this->crossCheckStateCount; |
| $stateCount[0] = 0; |
| $stateCount[1] = 0; |
| $stateCount[2] = 0; |
|
|
| |
| $i = $startI; |
| while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { |
| $stateCount[1]++; |
| $i--; |
| } |
| |
| if ($i < 0 || $stateCount[1] > $maxCount) { |
| return NAN; |
| } |
| while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) { |
| $stateCount[0]++; |
| $i--; |
| } |
| if ($stateCount[0] > $maxCount) { |
| return NAN; |
| } |
|
|
| |
| $i = $startI + 1; |
| while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { |
| $stateCount[1]++; |
| $i++; |
| } |
| if ($i == $maxI || $stateCount[1] > $maxCount) { |
| return NAN; |
| } |
| while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) { |
| $stateCount[2]++; |
| $i++; |
| } |
| if ($stateCount[2] > $maxCount) { |
| return NAN; |
| } |
|
|
| $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2]; |
| if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) { |
| return NAN; |
| } |
|
|
| return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN; |
| } |
| } |
|
|