/** * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans. * "true" is taken to mean a black module.</p> * * @param image booleans representing white/black QR Code modules * @param hints decoding hints that should be used to influence decoding * @return text and bytes encoded within the QR Code * @throws FormatException if the QR Code cannot be decoded * @throws ChecksumException if error correction fails */ public function decodeImage($image, $hints = null) { $dimension = count($image); $bits = new BitMatrix($dimension); for ($i = 0; $i < $dimension; $i++) { for ($j = 0; $j < $dimension; $j++) { if ($image[$i][$j]) { $bits->set($j, $i); } } } return $this->decode($bits, $hints); }
public function sampleGrid_($image, $dimensionX, $dimensionY, $transform) { if ($dimensionX <= 0 || $dimensionY <= 0) { throw NotFoundException::getNotFoundInstance(); } $bits = new BitMatrix($dimensionX, $dimensionY); $points = fill_array(0, 2 * $dimensionX, 0.0); for ($y = 0; $y < $dimensionY; $y++) { $max = count($points); $iValue = (double) $y + 0.5; for ($x = 0; $x < $max; $x += 2) { $points[$x] = (double) ($x / 2) + 0.5; $points[$x + 1] = $iValue; } $transform->transformPoints($points); // Quick check to see if points transformed to something inside the image; // sufficient to check the endpoints $this->checkAndNudgePoints($image, $points); try { for ($x = 0; $x < $max; $x += 2) { if ($image->get((int) $points[$x], (int) $points[$x + 1])) { // Black(-ish) pixel $bits->set($x / 2, $y); } } } catch (\Exception $aioobe) { //ArrayIndexOutOfBoundsException // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting // transform gets "twisted" such that it maps a straight line of points to a set of points // whose endpoints are in bounds, but others are not. There is probably some mathematical // way to detect this about the transformation that I don't know yet. // This results in an ugly runtime exception despite our clever checks above -- can't have // that. We could check each point's coordinates but that feels duplicative. We settle for // catching and wrapping ArrayIndexOutOfBoundsException. throw NotFoundException::getNotFoundInstance(); } } return $bits; }
/** * This method detects a code in a "pure" image -- that is, pure monochrome image * which contains only an unrotated, unskewed, image of a code, with some white border * around it. This is a specialized method that works exceptionally fast in this special * case. * * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix) */ private static function extractPureBits($image) { $leftTopBlack = $image->getTopLeftOnBit(); $rightBottomBlack = $image->getBottomRightOnBit(); if ($leftTopBlack == null || $rightBottomBlack == null) { throw NotFoundException::getNotFoundInstance(); } $moduleSize = self::moduleSize($leftTopBlack, $image); $top = $leftTopBlack[1]; $bottom = $rightBottomBlack[1]; $left = $leftTopBlack[0]; $right = $rightBottomBlack[0]; // Sanity check! if ($left >= $right || $top >= $bottom) { throw NotFoundException::getNotFoundInstance(); } if ($bottom - $top != $right - $left) { // Special case, where bottom-right module wasn't black so we found something else in the last row // Assume it's a square, so use height as the width $right = $left + ($bottom - $top); } $matrixWidth = round(($right - $left + 1) / $moduleSize); $matrixHeight = round(($bottom - $top + 1) / $moduleSize); if ($matrixWidth <= 0 || $matrixHeight <= 0) { throw NotFoundException::getNotFoundInstance(); } if ($matrixHeight != $matrixWidth) { // Only possibly decode square regions throw NotFoundException::getNotFoundInstance(); } // Push in the "border" by half the module width so that we start // sampling in the middle of the module. Just in case the image is a // little off, this will help recover. $nudge = (int) ($moduleSize / 2.0); // $nudge = (int) ($moduleSize / 2.0f); $top += $nudge; $left += $nudge; // But careful that this does not sample off the edge // "right" is the farthest-right valid pixel location -- right+1 is not necessarily // This is positive by how much the inner x loop below would be too large $nudgedTooFarRight = $left + (int) (($matrixWidth - 1) * $moduleSize) - $right; if ($nudgedTooFarRight > 0) { if ($nudgedTooFarRight > $nudge) { // Neither way fits; abort throw NotFoundException::getNotFoundInstance(); } $left -= $nudgedTooFarRight; } // See logic above $nudgedTooFarDown = $top + (int) (($matrixHeight - 1) * $moduleSize) - $bottom; if ($nudgedTooFarDown > 0) { if ($nudgedTooFarDown > $nudge) { // Neither way fits; abort throw NotFoundException::getNotFoundInstance(); } $top -= $nudgedTooFarDown; } // Now just read off the bits $bits = new BitMatrix($matrixWidth, $matrixHeight); for ($y = 0; $y < $matrixHeight; $y++) { $iOffset = $top + (int) ($y * $moduleSize); for ($x = 0; $x < $matrixWidth; $x++) { if ($image->get($left + (int) ($x * $moduleSize), $iOffset)) { $bits->set($x, $y); } } } return $bits; }
/** * See ISO 18004:2006 Annex E */ function buildFunctionPattern() { $dimension = self::getDimensionForVersion(); $bitMatrix = new BitMatrix($dimension); // Top left finder pattern + separator + format $bitMatrix->setRegion(0, 0, 9, 9); // Top right finder pattern + separator + format $bitMatrix->setRegion($dimension - 8, 0, 8, 9); // Bottom left finder pattern + separator + format $bitMatrix->setRegion(0, $dimension - 8, 9, 8); // Alignment patterns $max = count($this->alignmentPatternCenters); for ($x = 0; $x < $max; $x++) { $i = $this->alignmentPatternCenters[$x] - 2; for ($y = 0; $y < $max; $y++) { if ($x == 0 && ($y == 0 || $y == $max - 1) || $x == $max - 1 && $y == 0) { // No alignment patterns near the three finder paterns continue; } $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5); } } // Vertical timing pattern $bitMatrix->setRegion(6, 9, 1, $dimension - 17); // Horizontal timing pattern $bitMatrix->setRegion(9, 6, $dimension - 17, 1); if ($this->versionNumber > 6) { // Version info, top right $bitMatrix->setRegion($dimension - 11, 0, 3, 6); // Version info, bottom left $bitMatrix->setRegion(0, $dimension - 11, 6, 3); } return $bitMatrix; }
public function getBlackMatrix() { $source = $this->getLuminanceSource(); $width = $source->getWidth(); $height = $source->getHeight(); $matrix = new BitMatrix($width, $height); // Quickly calculates the histogram by sampling four rows from the image. This proved to be // more robust on the blackbox tests than sampling a diagonal as we used to do. $this->initArrays($width); $localBuckets = $this->buckets; for ($y = 1; $y < 5; $y++) { $row = intval($height * $y / 5); $localLuminances = $source->getRow($row, $this->luminances); $right = intval($width * 4 / 5); for ($x = intval($width / 5); $x < $right; $x++) { $pixel = intval32bits($localLuminances[intval($x)] & 0xff); $localBuckets[intval32bits($pixel >> self::$LUMINANCE_SHIFT)]++; } } $blackPoint = $this->estimateBlackPoint($localBuckets); // We delay reading the entire image luminance until the black point estimation succeeds. // Although we end up reading four rows twice, it is consistent with our motto of // "fail quickly" which is necessary for continuous scanning. $localLuminances = $source->getMatrix(); for ($y = 0; $y < $height; $y++) { $offset = $y * $width; for ($x = 0; $x < $width; $x++) { $pixel = intval($localLuminances[$offset + $x] & 0xff); if ($pixel < $blackPoint) { $matrix->set($x, $y); } } } return $matrix; }