Файловый менеджер - Редактировать - /home/lakoyani/lakoyani.com.fj/Detector.tar
Назад
FinderPattern.php 0000777 00000005355 14711055554 0010050 0 ustar 00 <?php /* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Zxing\Qrcode\Detector; use Zxing\ResultPoint; /** * <p>Encapsulates a finder pattern, which are the three square patterns found in * the corners of QR Codes. It also encapsulates a count of similar finder patterns, * as a convenience to the finder's bookkeeping.</p> * * @author Sean Owen */ final class FinderPattern extends ResultPoint { private $estimatedModuleSize; private $count; public function __construct($posX, $posY, $estimatedModuleSize, $count = 1) { parent::__construct($posX, $posY); $this->estimatedModuleSize = $estimatedModuleSize; $this->count = $count; } public function getEstimatedModuleSize() { return $this->estimatedModuleSize; } public function getCount() { return $this->count; } /* void incrementCount() { this.count++; } */ /** * <p>Determines if this finder pattern "about equals" a finder pattern at the stated * position and size -- meaning, it is at nearly the same center with nearly the same size.</p> */ public function aboutEquals($moduleSize, $i, $j) { if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) { $moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize); return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize; } return false; } /** * Combines this object's current estimate of a finder pattern position and module size * with a new estimate. It returns a new {@code FinderPattern} containing a weighted average * based on count. */ public function combineEstimate($i, $j, $newModuleSize) { $combinedCount = $this->count + 1; $combinedX = ($this->count * $this->getX() + $j) / $combinedCount; $combinedY = ($this->count * $this->getY() + $i) / $combinedCount; $combinedModuleSize = ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount; return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $combinedCount); } } AlignmentPattern.php 0000777 00000004271 14711055554 0010553 0 ustar 00 <?php /* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Zxing\Qrcode\Detector; use Zxing\ResultPoint; /** * <p>Encapsulates an alignment pattern, which are the smaller square patterns found in * all but the simplest QR Codes.</p> * * @author Sean Owen */ final class AlignmentPattern extends ResultPoint { private $estimatedModuleSize; public function __construct($posX, $posY, $estimatedModuleSize) { parent::__construct($posX, $posY); $this->estimatedModuleSize = $estimatedModuleSize; } /** * <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated * position and size -- meaning, it is at nearly the same center with nearly the same size.</p> */ public function aboutEquals($moduleSize, $i, $j) { if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) { $moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize); return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize; } return false; } /** * Combines this object's current estimate of a finder pattern position and module size * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two. */ public function combineEstimate($i, $j, $newModuleSize) { $combinedX = ($this->getX() + $j) / 2.0; $combinedY = ($this->getY() + $i) / 2.0; $combinedModuleSize = ($this->estimatedModuleSize + $newModuleSize) / 2.0; return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize); } } FinderPatternFinder.php 0000777 00000067300 14711055554 0011176 0 ustar 00 <?php /* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Zxing\Qrcode\Detector; use Zxing\BinaryBitmap; use Zxing\Common\BitMatrix; use Zxing\NotFoundException; use Zxing\ResultPoint; /** * <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square * markers at three corners of a QR Code.</p> * * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object. * * @author Sean Owen */ class FinderPatternFinder { protected static $MIN_SKIP = 3; protected static $MAX_MODULES = 57; // 1 pixel/module times 3 modules/center private static $CENTER_QUORUM = 2; // support up to version 10 for mobile clients private $image; private $average; private $possibleCenters; //private final List<FinderPattern> possibleCenters; private $hasSkipped = false; private $crossCheckStateCount; private $resultPointCallback; /** * <p>Creates a finder that will search the image for three finder patterns.</p> * * @param BitMatrix $image image to search */ public function __construct($image, $resultPointCallback = null) { $this->image = $image; $this->possibleCenters = [];//new ArrayList<>(); $this->crossCheckStateCount = fill_array(0, 5, 0); $this->resultPointCallback = $resultPointCallback; } final public function find($hints) {/*final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException {*/ $tryHarder = true;//$hints != null && $hints['TRY_HARDER']; $pureBarcode = $hints != null && $hints['PURE_BARCODE']; $maxI = $this->image->getHeight(); $maxJ = $this->image->getWidth(); // We are looking for black/white/black/white/black modules in // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. When trying harder, look for all // QR versions regardless of how dense they are. $iSkip = (int)((3 * $maxI) / (4 * self::$MAX_MODULES)); if ($iSkip < self::$MIN_SKIP || $tryHarder) { $iSkip = self::$MIN_SKIP; } $done = false; $stateCount = []; for ($i = $iSkip - 1; $i < $maxI && !$done; $i += $iSkip) { // Get a row of black/white values $stateCount[0] = 0; $stateCount[1] = 0; $stateCount[2] = 0; $stateCount[3] = 0; $stateCount[4] = 0; $currentState = 0; for ($j = 0; $j < $maxJ; $j++) { if ($this->image->get($j, $i)) { // Black pixel if (($currentState & 1) == 1) { // Counting white pixels $currentState++; } $stateCount[$currentState]++; } else { // White pixel if (($currentState & 1) == 0) { // Counting black pixels if ($currentState == 4) { // A winner? if (self::foundPatternCross($stateCount)) { // Yes $confirmed = $this->handlePossibleCenter($stateCount, $i, $j, $pureBarcode); if ($confirmed) { // Start examining every other line. Checking each line turned out to be too // expensive and didn't improve performance. $iSkip = 3; if ($this->hasSkipped) { $done = $this->haveMultiplyConfirmedCenters(); } else { $rowSkip = $this->findRowSkip(); if ($rowSkip > $stateCount[2]) { // Skip rows between row of lower confirmed center // and top of presumed third confirmed center // but back up a bit to get a full chance of detecting // it, entire width of center of finder pattern // Skip by rowSkip, but back off by $stateCount[2] (size of last center // of pattern we saw) to be conservative, and also back off by iSkip which // is about to be re-added $i += $rowSkip - $stateCount[2] - $iSkip; $j = $maxJ - 1; } } } else { $stateCount[0] = $stateCount[2]; $stateCount[1] = $stateCount[3]; $stateCount[2] = $stateCount[4]; $stateCount[3] = 1; $stateCount[4] = 0; $currentState = 3; continue; } // Clear state to start looking again $currentState = 0; $stateCount[0] = 0; $stateCount[1] = 0; $stateCount[2] = 0; $stateCount[3] = 0; $stateCount[4] = 0; } else { // No, shift counts back by two $stateCount[0] = $stateCount[2]; $stateCount[1] = $stateCount[3]; $stateCount[2] = $stateCount[4]; $stateCount[3] = 1; $stateCount[4] = 0; $currentState = 3; } } else { $stateCount[++$currentState]++; } } else { // Counting white pixels $stateCount[$currentState]++; } } } if (self::foundPatternCross($stateCount)) { $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ, $pureBarcode); if ($confirmed) { $iSkip = $stateCount[0]; if ($this->hasSkipped) { // Found a third one $done = $this->haveMultiplyConfirmedCenters(); } } } } $patternInfo = $this->selectBestPatterns(); $patternInfo = ResultPoint::orderBestPatterns($patternInfo); return new FinderPatternInfo($patternInfo); } /** * @param $stateCount ; count of black/white/black/white/black pixels just read * * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios * used by finder patterns to be considered a match */ protected static function foundPatternCross($stateCount) { $totalModuleSize = 0; for ($i = 0; $i < 5; $i++) { $count = $stateCount[$i]; if ($count == 0) { return false; } $totalModuleSize += $count; } if ($totalModuleSize < 7) { return false; } $moduleSize = $totalModuleSize / 7.0; $maxVariance = $moduleSize / 2.0; // Allow less than 50% variance from 1-1-3-1-1 proportions return abs($moduleSize - $stateCount[0]) < $maxVariance && abs($moduleSize - $stateCount[1]) < $maxVariance && abs(3.0 * $moduleSize - $stateCount[2]) < 3 * $maxVariance && abs($moduleSize - $stateCount[3]) < $maxVariance && abs($moduleSize - $stateCount[4]) < $maxVariance; } /** * <p>This is called when a horizontal scan finds a possible alignment pattern. It will * cross check with a vertical scan, and if successful, will, ah, cross-cross-check * with another horizontal scan. This is needed primarily to locate the real horizontal * center of the pattern in cases of extreme skew. * And then we cross-cross-cross check with another diagonal scan.</p> * * <p>If that succeeds the finder pattern location is added to a list that tracks * the number of times each location has been nearly-matched as a finder pattern. * Each additional find is more evidence that the location is in fact a finder * pattern center * * @param $stateCount reading state module counts from horizontal scan * @param i row where finder pattern may be found * @param j end of possible finder pattern in row * @param pureBarcode true if in "pure barcode" mode * * @return true if a finder pattern candidate was found this time */ protected final function handlePossibleCenter($stateCount, $i, $j, $pureBarcode) { $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]; $centerJ = $this->centerFromEnd($stateCount, $j); $centerI = $this->crossCheckVertical($i, (int)($centerJ), $stateCount[2], $stateCountTotal); if (!is_nan($centerI)) { // Re-cross check $centerJ = $this->crossCheckHorizontal((int)($centerJ), (int)($centerI), $stateCount[2], $stateCountTotal); if (!is_nan($centerJ) && (!$pureBarcode || $this->crossCheckDiagonal((int)($centerI), (int)($centerJ), $stateCount[2], $stateCountTotal)) ) { $estimatedModuleSize = (float)$stateCountTotal / 7.0; $found = false; for ($index = 0; $index < count($this->possibleCenters); $index++) { $center = $this->possibleCenters[$index]; // Look for about the same center and module size: if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) { $this->possibleCenters[$index] = $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize); $found = true; break; } } if (!$found) { $point = new FinderPattern($centerJ, $centerI, $estimatedModuleSize); $this->possibleCenters[] = $point; if ($this->resultPointCallback != null) { $this->resultPointCallback->foundPossibleResultPoint($point); } } return true; } } return false; } /** * Given a count of black/white/black/white/black pixels just seen and an end position, * figures the location of the center of this run. */ private static function centerFromEnd($stateCount, $end) { return (float)($end - $stateCount[4] - $stateCount[3]) - $stateCount[2] / 2.0; } /** * <p>After a horizontal scan finds a potential finder pattern, this method * "cross-checks" by scanning down vertically through the center of the possible * finder pattern to see if the same proportion is detected.</p> * * @param $startI ; row where a finder pattern was detected * @param centerJ ; center of the section that appears to cross a finder pattern * @param $maxCount ; maximum reasonable number of modules that should be * observed in any reading state, based on the results of the horizontal scan * * @return vertical center of finder pattern, or {@link Float#NaN} if not found */ private function crossCheckVertical($startI, $centerJ, $maxCount, $originalStateCountTotal) { $image = $this->image; $maxI = $image->getHeight(); $stateCount = $this->getCrossCheckStateCount(); // Start counting up from center $i = $startI; while ($i >= 0 && $image->get($centerJ, $i)) { $stateCount[2]++; $i--; } if ($i < 0) { return NAN; } while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { $stateCount[1]++; $i--; } // If already too many modules in this state or ran off the edge: 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; } // Now also count down from center $i = $startI + 1; while ($i < $maxI && $image->get($centerJ, $i)) { $stateCount[2]++; $i++; } if ($i == $maxI) { return NAN; } while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[3] < $maxCount) { $stateCount[3]++; $i++; } if ($i == $maxI || $stateCount[3] >= $maxCount) { return NAN; } while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[4] < $maxCount) { $stateCount[4]++; $i++; } if ($stateCount[4] >= $maxCount) { return NAN; } // If we found a finder-pattern-like section, but its size is more than 40% different than // the original, assume it's a false positive $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]; if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) { return NAN; } return self::foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN; } private function getCrossCheckStateCount() { $this->crossCheckStateCount[0] = 0; $this->crossCheckStateCount[1] = 0; $this->crossCheckStateCount[2] = 0; $this->crossCheckStateCount[3] = 0; $this->crossCheckStateCount[4] = 0; return $this->crossCheckStateCount; } /** * <p>Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, * except it reads horizontally instead of vertically. This is used to cross-cross * check a vertical cross check and locate the real center of the alignment pattern.</p> */ private function crossCheckHorizontal($startJ, $centerI, $maxCount, $originalStateCountTotal) { $image = $this->image; $maxJ = $this->image->getWidth(); $stateCount = $this->getCrossCheckStateCount(); $j = $startJ; while ($j >= 0 && $image->get($j, $centerI)) { $stateCount[2]++; $j--; } if ($j < 0) { return NAN; } while ($j >= 0 && !$image->get($j, $centerI) && $stateCount[1] <= $maxCount) { $stateCount[1]++; $j--; } if ($j < 0 || $stateCount[1] > $maxCount) { return NAN; } while ($j >= 0 && $image->get($j, $centerI) && $stateCount[0] <= $maxCount) { $stateCount[0]++; $j--; } if ($stateCount[0] > $maxCount) { return NAN; } $j = $startJ + 1; while ($j < $maxJ && $image->get($j, $centerI)) { $stateCount[2]++; $j++; } if ($j == $maxJ) { return NAN; } while ($j < $maxJ && !$image->get($j, $centerI) && $stateCount[3] < $maxCount) { $stateCount[3]++; $j++; } if ($j == $maxJ || $stateCount[3] >= $maxCount) { return NAN; } while ($j < $maxJ && $this->image->get($j, $centerI) && $stateCount[4] < $maxCount) { $stateCount[4]++; $j++; } if ($stateCount[4] >= $maxCount) { return NAN; } // If we found a finder-pattern-like section, but its size is significantly different than // the original, assume it's a false positive $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]; if (5 * abs($stateCountTotal - $originalStateCountTotal) >= $originalStateCountTotal) { return NAN; } return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $j) : NAN; } /** * After a vertical and horizontal scan finds a potential finder pattern, this method * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible * finder pattern to see if the same proportion is detected. * * @param $startI ; row where a finder pattern was detected * @param centerJ ; center of the section that appears to cross a finder pattern * @param $maxCount ; maximum reasonable number of modules that should be * observed in any reading state, based on the results of the horizontal scan * @param originalStateCountTotal ; The original state count total. * * @return true if proportions are withing expected limits */ private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalStateCountTotal) { $stateCount = $this->getCrossCheckStateCount(); // Start counting up, left from center finding black center mass $i = 0; $startI = (int)($startI); $centerJ = (int)($centerJ); while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i)) { $stateCount[2]++; $i++; } if ($startI < $i || $centerJ < $i) { return false; } // Continue up, left finding white space while ($startI >= $i && $centerJ >= $i && !$this->image->get($centerJ - $i, $startI - $i) && $stateCount[1] <= $maxCount) { $stateCount[1]++; $i++; } // If already too many modules in this state or ran off the edge: if ($startI < $i || $centerJ < $i || $stateCount[1] > $maxCount) { return false; } // Continue up, left finding black border while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i) && $stateCount[0] <= $maxCount) { $stateCount[0]++; $i++; } if ($stateCount[0] > $maxCount) { return false; } $maxI = $this->image->getHeight(); $maxJ = $this->image->getWidth(); // Now also count down, right from center $i = 1; while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i)) { $stateCount[2]++; $i++; } // Ran off the edge? if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ) { return false; } while ($startI + $i < $maxI && $centerJ + $i < $maxJ && !$this->image->get($centerJ + $i, $startI + $i) && $stateCount[3] < $maxCount) { $stateCount[3]++; $i++; } if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ || $stateCount[3] >= $maxCount) { return false; } while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i) && $stateCount[4] < $maxCount) { $stateCount[4]++; $i++; } if ($stateCount[4] >= $maxCount) { return false; } // If we found a finder-pattern-like section, but its size is more than 100% different than // the original, assume it's a false positive $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4]; return abs($stateCountTotal - $originalStateCountTotal) < 2 * $originalStateCountTotal && self::foundPatternCross($stateCount); } /** * @return true iff we have found at least 3 finder patterns that have been detected * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the * candidates is "pretty similar" */ private function haveMultiplyConfirmedCenters() { $confirmedCount = 0; $totalModuleSize = 0.0; $max = count($this->possibleCenters); foreach ($this->possibleCenters as $pattern) { if ($pattern->getCount() >= self::$CENTER_QUORUM) { $confirmedCount++; $totalModuleSize += $pattern->getEstimatedModuleSize(); } } if ($confirmedCount < 3) { return false; } // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" // and that we need to keep looking. We detect this by asking if the estimated module sizes // vary too much. We arbitrarily say that when the total deviation from average exceeds // 5% of the total module size estimates, it's too much. $average = $totalModuleSize / (float)$max; $totalDeviation = 0.0; foreach ($this->possibleCenters as $pattern) { $totalDeviation += abs($pattern->getEstimatedModuleSize() - $average); } return $totalDeviation <= 0.05 * $totalModuleSize; } /** * @return number of rows we could safely skip during scanning, based on the first * two finder patterns that have been located. In some cases their position will * allow us to infer that the third pattern must lie below a certain point farther * down in the image. */ private function findRowSkip() { $max = count($this->possibleCenters); if ($max <= 1) { return 0; } $firstConfirmedCenter = null; foreach ($this->possibleCenters as $center) { if ($center->getCount() >= self::$CENTER_QUORUM) { if ($firstConfirmedCenter == null) { $firstConfirmedCenter = $center; } else { // We have two confirmed centers // How far down can we skip before resuming looking for the next // pattern? In the worst case, only the difference between the // difference in the x / y coordinates of the two centers. // This is the case where you find top left last. $this->hasSkipped = true; return (int)((abs($firstConfirmedCenter->getX() - $center->getX()) - abs($firstConfirmedCenter->getY() - $center->getY())) / 2); } } } return 0; } /** * @return array the 3 best {@link FinderPattern}s from our list of candidates. The "best" are * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module * size differs from the average among those patterns the least * @throws NotFoundException if 3 such finder patterns do not exist */ private function selectBestPatterns() { $startSize = count($this->possibleCenters); if ($startSize < 3) { // Couldn't find enough finder patterns throw new NotFoundException; } // Filter outlier possibilities whose module size is too different if ($startSize > 3) { // But we can only afford to do so if we have at least 4 possibilities to choose from $totalModuleSize = 0.0; $square = 0.0; foreach ($this->possibleCenters as $center) { $size = $center->getEstimatedModuleSize(); $totalModuleSize += $size; $square += $size * $size; } $this->average = $totalModuleSize / (float)$startSize; $stdDev = (float)sqrt($square / $startSize - $this->average * $this->average); usort($this->possibleCenters, [$this, 'FurthestFromAverageComparator']); $limit = max(0.2 * $this->average, $stdDev); for ($i = 0; $i < count($this->possibleCenters) && count($this->possibleCenters) > 3; $i++) { $pattern = $this->possibleCenters[$i]; if (abs($pattern->getEstimatedModuleSize() - $this->average) > $limit) { unset($this->possibleCenters[$i]);//возможно что ключи меняются в java при вызове .remove(i) ??? $this->possibleCenters = array_values($this->possibleCenters); $i--; } } } if (count($this->possibleCenters) > 3) { // Throw away all but those first size candidate points we found. $totalModuleSize = 0.0; foreach ($this->possibleCenters as $possibleCenter) { $totalModuleSize += $possibleCenter->getEstimatedModuleSize(); } $this->average = $totalModuleSize / (float)count($this->possibleCenters); usort($this->possibleCenters, [$this, 'CenterComparator']); array_slice($this->possibleCenters, 3, count($this->possibleCenters) - 3); } return [$this->possibleCenters[0], $this->possibleCenters[1], $this->possibleCenters[2]]; } /** * <p>Orders by furthest from average</p> */ public function FurthestFromAverageComparator($center1, $center2) { $dA = abs($center2->getEstimatedModuleSize() - $this->average); $dB = abs($center1->getEstimatedModuleSize() - $this->average); if ($dA < $dB) { return -1; } elseif ($dA == $dB) { return 0; } else { return 1; } } public function CenterComparator($center1, $center2) { if ($center2->getCount() == $center1->getCount()) { $dA = abs($center2->getEstimatedModuleSize() - $this->average); $dB = abs($center1->getEstimatedModuleSize() - $this->average); if ($dA < $dB) { return 1; } elseif ($dA == $dB) { return 0; } else { return -1; } } else { return $center2->getCount() - $center1->getCount(); } } protected final function getImage() { return $this->image; } /** * <p>Orders by {@link FinderPattern#getCount()}, descending.</p> */ //@Override protected final function getPossibleCenters() { //List<FinderPattern> getPossibleCenters() return $this->possibleCenters; } } FinderPatternInfo.php 0000777 00000002543 14711055554 0010660 0 ustar 00 <?php /* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Zxing\Qrcode\Detector; /** * <p>Encapsulates information about finder patterns in an image, including the location of * the three finder patterns, and their estimated module size.</p> * * @author Sean Owen */ final class FinderPatternInfo { private $bottomLeft; private $topLeft; private $topRight; public function __construct($patternCenters) { $this->bottomLeft = $patternCenters[0]; $this->topLeft = $patternCenters[1]; $this->topRight = $patternCenters[2]; } public function getBottomLeft() { return $this->bottomLeft; } public function getTopLeft() { return $this->topLeft; } public function getTopRight() { return $this->topRight; } } AlignmentPatternFinder.php 0000777 00000026043 14711055554 0011704 0 ustar 00 <?php /* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Zxing\Qrcode\Detector; use Zxing\NotFoundException; use Zxing\ResultPointCallback; use Zxing\Common\BitMatrix; /** * <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder * patterns but are smaller and appear at regular intervals throughout the image.</p> * * <p>At the moment this only looks for the bottom-right alignment pattern.</p> * * <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied, * pasted and stripped down here for maximum performance but does unfortunately duplicate * some code.</p> * * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p> * * @author Sean Owen */ final class AlignmentPatternFinder { private $image; private $possibleCenters; private $startX; private $startY; private $width; private $height; private $moduleSize; private $crossCheckStateCount; private $resultPointCallback; /** * <p>Creates a finder that will look in a portion of the whole image.</p> * * @param image image to search * @param startX left column from which to start searching * @param startY top row from which to start searching * @param width width of region to search * @param height height of region to search * @param moduleSize estimated module size so far */ 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; } /** * <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since * it's pretty performance-critical and so is written to be fast foremost.</p> * * @return {@link AlignmentPattern} if found * @throws NotFoundException if not found */ public function find() { $startX = $this->startX; $height = $this->height; $maxJ = $startX + $this->width; $middleI = $this->startY + ($height / 2); // We are looking for black/white/black modules in 1:1:1 ratio; // this tracks the number of black/white/black modules seen so far $stateCount = []; for ($iGen = 0; $iGen < $height; $iGen++) { // Search from middle outwards $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; // Burn off leading white pixels before anything else; if we start in the middle of // a white run, it doesn't make sense to count its length, since we don't know if the // white run continued to the left of the start point while ($j < $maxJ && !$this->image->get($j, $i)) { $j++; } $currentState = 0; while ($j < $maxJ) { if ($this->image->get($j, $i)) { // Black pixel if ($currentState == 1) { // Counting black pixels $stateCount[$currentState]++; } else { // Counting white pixels if ($currentState == 2) { // A winner? if ($this->foundPatternCross($stateCount)) { // Yes $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 { // White pixel if ($currentState == 1) { // Counting black pixels $currentState++; } $stateCount[$currentState]++; } $j++; } if ($this->foundPatternCross($stateCount)) { $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ); if ($confirmed != null) { return $confirmed; } } } // Hmm, nothing we saw was observed and confirmed twice. If we had // any guess at all, return it. if (count($this->possibleCenters)) { return $this->possibleCenters[0]; } throw NotFoundException::getNotFoundInstance(); } /** * @param stateCount count of black/white/black pixels just read * * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios * used by alignment patterns to be considered a match */ 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; } /** * <p>This is called when a horizontal scan finds a possible alignment pattern. It will * cross check with a vertical scan, and if successful, will see if this pattern had been * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have * found the alignment pattern.</p> * * @param stateCount reading state module counts from horizontal scan * @param i row where alignment pattern may be found * @param j end of possible alignment pattern in row * * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not */ 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) { // Look for about the same center and module size: if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) { return $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize); } } // Hadn't found this before; save it $point = new AlignmentPattern($centerJ, $centerI, $estimatedModuleSize); $this->possibleCenters[] = $point; if ($this->resultPointCallback != null) { $this->resultPointCallback->foundPossibleResultPoint($point); } } return null; } /** * Given a count of black/white/black pixels just seen and an end position, * figures the location of the center of this black/white/black run. */ private static function centerFromEnd($stateCount, $end) { return (float)($end - $stateCount[2]) - $stateCount[1] / 2.0; } /** * <p>After a horizontal scan finds a potential alignment pattern, this method * "cross-checks" by scanning down vertically through the center of the possible * alignment pattern to see if the same proportion is detected.</p> * * @param startI row where an alignment pattern was detected * @param centerJ center of the section that appears to cross an alignment pattern * @param maxCount maximum reasonable number of modules that should be * observed in any reading state, based on the results of the horizontal scan * * @return vertical center of alignment pattern, or {@link Float#NaN} if not found */ 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; // Start counting up from center $i = $startI; while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) { $stateCount[1]++; $i--; } // If already too many modules in this state or ran off the edge: 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; } // Now also count down from center $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; } } Detector.php 0000777 00000040053 14711055554 0007046 0 ustar 00 <?php /* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Zxing\Qrcode\Detector; use Zxing\DecodeHintType; use Zxing\FormatException; use Zxing\NotFoundException; use Zxing\ResultPoint; use Zxing\ResultPointCallback; use Zxing\Common\BitMatrix; use Zxing\Common\DetectorResult; use Zxing\Common\GridSampler; use Zxing\Common\PerspectiveTransform; use Zxing\Common\Detector\MathUtils; use Zxing\Qrcode\Decoder\Version; /** * <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code * is rotated or skewed, or partially obscured.</p> * * @author Sean Owen */ class Detector { private $image; private $resultPointCallback; public function __construct($image) { $this->image = $image; } /** * <p>Detects a QR Code in an image.</p> * * @param hints optional hints to detector * * @return {@link DetectorResult} encapsulating results of detecting a QR Code * @throws NotFoundException if QR Code cannot be found * @throws FormatException if a QR Code cannot be decoded */ public final function detect($hints = null) {/*Map<DecodeHintType,?>*/ $resultPointCallback = $hints == null ? null : $hints->get('NEED_RESULT_POINT_CALLBACK'); /* resultPointCallback = hints == null ? null : (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/ $finder = new FinderPatternFinder($this->image, $resultPointCallback); $info = $finder->find($hints); return $this->processFinderPatternInfo($info); } protected final function processFinderPatternInfo($info) { $topLeft = $info->getTopLeft(); $topRight = $info->getTopRight(); $bottomLeft = $info->getBottomLeft(); $moduleSize = (float)$this->calculateModuleSize($topLeft, $topRight, $bottomLeft); if ($moduleSize < 1.0) { throw NotFoundException::getNotFoundInstance(); } $dimension = (int)self::computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize); $provisionalVersion = \Zxing\Qrcode\Decoder\Version::getProvisionalVersionForDimension($dimension); $modulesBetweenFPCenters = $provisionalVersion->getDimensionForVersion() - 7; $alignmentPattern = null; // Anything above version 1 has an alignment pattern if (count($provisionalVersion->getAlignmentPatternCenters()) > 0) { // Guess where a "bottom right" finder pattern would have been $bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX(); $bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY(); // Estimate that alignment pattern is closer by 3 modules // from "bottom right" to known top left location $correctionToTopLeft = 1.0 - 3.0 / (float)$modulesBetweenFPCenters; $estAlignmentX = (int)($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX())); $estAlignmentY = (int)($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY())); // Kind of arbitrary -- expand search radius before giving up for ($i = 4; $i <= 16; $i <<= 1) {//?????????? try { $alignmentPattern = $this->findAlignmentInRegion( $moduleSize, $estAlignmentX, $estAlignmentY, (float)$i ); break; } catch (NotFoundException $re) { // try next round } } // If we didn't find alignment pattern... well try anyway without it } $transform = self::createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension); $bits = self::sampleGrid($this->image, $transform, $dimension); $points = []; if ($alignmentPattern == null) { $points = [$bottomLeft, $topLeft, $topRight]; } else { // die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};'); $points = [$bottomLeft, $topLeft, $topRight, $alignmentPattern]; } return new DetectorResult($bits, $points); } /** * <p>Detects a QR Code in an image.</p> * * @return {@link DetectorResult} encapsulating results of detecting a QR Code * @throws NotFoundException if QR Code cannot be found * @throws FormatException if a QR Code cannot be decoded */ /** * <p>Computes an average estimated module size based on estimated derived from the positions * of the three finder patterns.</p> * * @param topLeft detected top-left finder pattern center * @param topRight detected top-right finder pattern center * @param bottomLeft detected bottom-left finder pattern center * * @return estimated module size */ protected final function calculateModuleSize($topLeft, $topRight, $bottomLeft) { // Take the average return ($this->calculateModuleSizeOneWay($topLeft, $topRight) + $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0; } /** * <p>Estimates module size based on two finder patterns -- it uses * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the * width of each, measuring along the axis between their centers.</p> */ private function calculateModuleSizeOneWay($pattern, $otherPattern) { $moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($pattern->getX(), (int)$pattern->getY(), (int)$otherPattern->getX(), (int)$otherPattern->getY()); $moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays((int)$otherPattern->getX(), (int)$otherPattern->getY(), (int)$pattern->getX(), (int)$pattern->getY()); if (is_nan($moduleSizeEst1)) { return $moduleSizeEst2 / 7.0; } if (is_nan($moduleSizeEst2)) { return $moduleSizeEst1 / 7.0; } // Average them, and divide by 7 since we've counted the width of 3 black modules, // and 1 white and 1 black module on either side. Ergo, divide sum by 14. return ($moduleSizeEst1 + $moduleSizeEst2) / 14.0; } /** * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of * a finder pattern by looking for a black-white-black run from the center in the direction * of another po$(another finder pattern center), and in the opposite direction too.</p> */ private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY) { $result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY); // Now count other way -- don't run off image though of course $scale = 1.0; $otherToX = $fromX - ($toX - $fromX); if ($otherToX < 0) { $scale = (float)$fromX / (float)($fromX - $otherToX); $otherToX = 0; } else if ($otherToX >= $this->image->getWidth()) { $scale = (float)($this->image->getWidth() - 1 - $fromX) / (float)($otherToX - $fromX); $otherToX = $this->image->getWidth() - 1; } $otherToY = (int)($fromY - ($toY - $fromY) * $scale); $scale = 1.0; if ($otherToY < 0) { $scale = (float)$fromY / (float)($fromY - $otherToY); $otherToY = 0; } else if ($otherToY >= $this->image->getHeight()) { $scale = (float)($this->image->getHeight() - 1 - $fromY) / (float)($otherToY - $fromY); $otherToY = $this->image->getHeight() - 1; } $otherToX = (int)($fromX + ($otherToX - $fromX) * $scale); $result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY); // Middle pixel is double-counted this way; subtract 1 return $result - 1.0; } /** * <p>This method traces a line from a po$in the image, in the direction towards another point. * It begins in a black region, and keeps going until it finds white, then black, then white again. * It reports the distance from the start to this point.</p> * * <p>This is used when figuring out how wide a finder pattern is, when the finder pattern * may be skewed or rotated.</p> */ private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY) { // Mild variant of Bresenham's algorithm; // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm $steep = abs($toY - $fromY) > abs($toX - $fromX); if ($steep) { $temp = $fromX; $fromX = $fromY; $fromY = $temp; $temp = $toX; $toX = $toY; $toY = $temp; } $dx = abs($toX - $fromX); $dy = abs($toY - $fromY); $error = -$dx / 2; $xstep = $fromX < $toX ? 1 : -1; $ystep = $fromY < $toY ? 1 : -1; // In black pixels, looking for white, first or second time. $state = 0; // Loop up until x == toX, but not beyond $xLimit = $toX + $xstep; for ($x = $fromX, $y = $fromY; $x != $xLimit; $x += $xstep) { $realX = $steep ? $y : $x; $realY = $steep ? $x : $y; // Does current pixel mean we have moved white to black or vice versa? // Scanning black in state 0,2 and white in state 1, so if we find the wrong // color, advance to next state or end if we are in state 2 already if (($state == 1) == $this->image->get($realX, $realY)) { if ($state == 2) { return MathUtils::distance($x, $y, $fromX, $fromY); } $state++; } $error += $dy; if ($error > 0) { if ($y == $toY) { break; } $y += $ystep; $error -= $dx; } } // Found black-white-black; give the benefit of the doubt that the next pixel outside the image // is "white" so this last po$at (toX+xStep,toY) is the right ending. This is really a // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this. if ($state == 2) { return MathUtils::distance($toX + $xstep, $toY, $fromX, $fromY); } // else we didn't find even black-white-black; no estimate is really possible return NAN; } /** * <p>Computes the dimension (number of modules on a size) of the QR Code based on the position * of the finder patterns and estimated module size.</p> */ private static function computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize) { $tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize); $tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize); $dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7; switch ($dimension & 0x03) { // mod 4 case 0: $dimension++; break; // 1? do nothing case 2: $dimension--; break; case 3: throw NotFoundException::getNotFoundInstance(); } return $dimension; } /** * <p>Attempts to locate an alignment pattern in a limited region of the image, which is * guessed to contain it. This method uses {@link AlignmentPattern}.</p> * * @param overallEstModuleSize estimated module size so far * @param estAlignmentX x coordinate of center of area probably containing alignment pattern * @param estAlignmentY y coordinate of above * @param allowanceFactor number of pixels in all directions to search from the center * * @return {@link AlignmentPattern} if found, or null otherwise * @throws NotFoundException if an unexpected error occurs during detection */ protected final function findAlignmentInRegion($overallEstModuleSize, $estAlignmentX, $estAlignmentY, $allowanceFactor) { // Look for an alignment pattern (3 modules in size) around where it // should be $allowance = (int)($allowanceFactor * $overallEstModuleSize); $alignmentAreaLeftX = max(0, $estAlignmentX - $allowance); $alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance); if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) { throw NotFoundException::getNotFoundInstance(); } $alignmentAreaTopY = max(0, $estAlignmentY - $allowance); $alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance); if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) { throw NotFoundException::getNotFoundInstance(); } $alignmentFinder = new AlignmentPatternFinder( $this->image, $alignmentAreaLeftX, $alignmentAreaTopY, $alignmentAreaRightX - $alignmentAreaLeftX, $alignmentAreaBottomY - $alignmentAreaTopY, $overallEstModuleSize, $this->resultPointCallback); return $alignmentFinder->find(); } private static function createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension) { $dimMinusThree = (float)$dimension - 3.5; $bottomRightX = 0.0; $bottomRightY = 0.0; $sourceBottomRightX = 0.0; $sourceBottomRightY = 0.0; if ($alignmentPattern != null) { $bottomRightX = $alignmentPattern->getX(); $bottomRightY = $alignmentPattern->getY(); $sourceBottomRightX = $dimMinusThree - 3.0; $sourceBottomRightY = $sourceBottomRightX; } else { // Don't have an alignment pattern, just make up the bottom-right point $bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX(); $bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY(); $sourceBottomRightX = $dimMinusThree; $sourceBottomRightY = $dimMinusThree; } return PerspectiveTransform::quadrilateralToQuadrilateral( 3.5, 3.5, $dimMinusThree, 3.5, $sourceBottomRightX, $sourceBottomRightY, 3.5, $dimMinusThree, $topLeft->getX(), $topLeft->getY(), $topRight->getX(), $topRight->getY(), $bottomRightX, $bottomRightY, $bottomLeft->getX(), $bottomLeft->getY()); } private static function sampleGrid($image, $transform, $dimension) { $sampler = GridSampler::getInstance(); return $sampler->sampleGrid_($image, $dimension, $dimension, $transform); } protected final function getImage() { return $this->image; } protected final function getResultPointCallback() { return $this->resultPointCallback; } } MathUtils.php 0000777 00000002663 14711056164 0007212 0 ustar 00 <?php /* * Copyright 2012 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Zxing\Common\Detector; final class MathUtils { private function __construct() { } /** * Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its * argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut * differ slightly from {@link Math#round(float)} in that half rounds down for negative * values. -2.5 rounds to -3, not -2. For purposes here it makes no difference. * * @param float $d real value to round * * @return int $nearest {@code int} */ public static function round($d) { return (int)($d + ($d < 0.0 ? -0.5 : 0.5)); } public static function distance($aX, $aY, $bX, $bY) { $xDiff = $aX - $bX; $yDiff = $aY - $bY; return (float)sqrt($xDiff * $xDiff + $yDiff * $yDiff); } } MonochromeRectangleDetector.php 0000777 00000022125 14711056164 0012720 0 ustar 00 <?php /** * Created by PhpStorm. * User: Ashot * Date: 3/24/15 * Time: 21:23 */ namespace Zxing\Common\Detector; use Zxing\BinaryBitmap; use \Zxing\NotFoundException; use \Zxing\ResultPoint; /* * * import com.google.zxing.NotFoundException; import com.google.zxing.ResultPoint; import com.google.zxing.common.BitMatrix; */ //require_once('./lib/NotFoundException.php'); //require_once('./lib/ResultPoint.php'); //require_once('./lib/common/BitMatrix.php'); /** * <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image. * It looks within a mostly white region of an image for a region of black and white, but mostly * black. It returns the four corners of the region, as best it can determine.</p> * * @author Sean Owen * @port Ashot Khanamiryan */ class MonochromeRectangleDetector { private static $MAX_MODULES = 32; private $image; public function __construct(BinaryBitmap $image) { $this->image = $image; } /** * <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly * white, in an image.</p> * * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and * last points are opposed on the diagonal, as are the second and third. The first point will be * the topmost point and the last, the bottommost. The second point will be leftmost and the * third, the rightmost * @throws NotFoundException if no Data Matrix Code can be found */ public function detect() { $height = $this->image->getHeight(); $width = $this->image->getWidth(); $halfHeight = $height / 2; $halfWidth = $width / 2; $deltaY = max(1, $height / (self::$MAX_MODULES * 8)); $deltaX = max(1, $width / (self::$MAX_MODULES * 8)); $top = 0; $bottom = $height; $left = 0; $right = $width; $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2); $top = (int)$pointA->getY() - 1; $pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left, $right, $halfHeight, 0, $top, $bottom, $halfHeight / 2); $left = (int)$pointB->getX() - 1; $pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right, $halfHeight, 0, $top, $bottom, $halfHeight / 2); $right = (int)$pointC->getX() + 1; $pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $halfHeight, $deltaY, $top, $bottom, $halfWidth / 2); $bottom = (int)$pointD->getY() + 1; // Go try to find po$A again with better information -- might have been off at first. $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right, $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4); return new ResultPoint($pointA, $pointB, $pointC, $pointD); } /** * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center * point which should be within the barcode. * * @param float $centerX center's x component (horizontal) * @param float $deltaX same as deltaY but change in x per step instead * @param float $left minimum value of x * @param float $right maximum value of x * @param float $centerY center's y component (vertical) * @param float $deltaY change in y per step. If scanning up this is negative; down, positive; * left or right, 0 * @param float $top minimum value of y to search through (meaningless when di == 0) * @param float $bottom maximum value of y * @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within * the barcode * * @return ResultPoint $a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found * @throws NotFoundException if such a point cannot be found */ private function findCornerFromCenter($centerX, $deltaX, $left, $right, $centerY, $deltaY, $top, $bottom, $maxWhiteRun) { $lastRange = null; for ($y = $centerY, $x = $centerX; $y < $bottom && $y >= $top && $x < $right && $x >= $left; $y += $deltaY, $x += $deltaX) { $range = 0; if ($deltaX == 0) { // horizontal slices, up and down $range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true); } else { // vertical slices, left and right $range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false); } if ($range == null) { if ($lastRange == null) { throw NotFoundException::getNotFoundInstance(); } // lastRange was found if ($deltaX == 0) { $lastY = $y - $deltaY; if ($lastRange[0] < $centerX) { if ($lastRange[1] > $centerX) { // straddle, choose one or the other based on direction return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY); } return new ResultPoint($lastRange[0], $lastY); } else { return new ResultPoint($lastRange[1], $lastY); } } else { $lastX = $x - $deltaX; if ($lastRange[0] < $centerY) { if ($lastRange[1] > $centerY) { return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]); } return new ResultPoint($lastX, $lastRange[0]); } else { return new ResultPoint($lastX, $lastRange[1]); } } } $lastRange = $range; } throw NotFoundException::getNotFoundInstance(); } /** * Computes the start and end of a region of pixels, either horizontally or vertically, that could * be part of a Data Matrix barcode. * * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location) * where we are scanning. If scanning vertically it's the column, the fixed horizontal location * @param maxWhiteRun largest run of white pixels that can still be considered part of the * barcode region * @param minDim minimum pixel location, horizontally or vertically, to consider * @param maxDim maximum pixel location, horizontally or vertically, to consider * @param horizontal if true, we're scanning left-right, instead of up-down * * @return int[] with start and end of found range, or null if no such range is found * (e.g. only white was found) */ private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal) { $center = ($minDim + $maxDim) / 2; // Scan left/up first $start = $center; while ($start >= $minDim) { if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) { $start--; } else { $whiteRunStart = $start; do { $start--; } while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start))); $whiteRunSize = $whiteRunStart - $start; if ($start < $minDim || $whiteRunSize > $maxWhiteRun) { $start = $whiteRunStart; break; } } } $start++; // Then try right/down $end = $center; while ($end < $maxDim) { if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) { $end++; } else { $whiteRunStart = $end; do { $end++; } while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end))); $whiteRunSize = $end - $whiteRunStart; if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) { $end = $whiteRunStart; break; } } } $end--; return $end > $start ? [$start, $end] : null; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка