/**
  * Calculates a single black point for each block of pixels and saves it away.
  * See the following thread for a discussion of this algorithm:
  *  http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
  */
 private static function calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height)
 {
     $blackPoints = fill_array(0, $subHeight, 0);
     foreach ($blackPoints as $key => $point) {
         $blackPoints[$key] = fill_array(0, $subWidth, 0);
     }
     for ($y = 0; $y < $subHeight; $y++) {
         $yoffset = intval32bits($y << self::$BLOCK_SIZE_POWER);
         $maxYOffset = $height - self::$BLOCK_SIZE;
         if ($yoffset > $maxYOffset) {
             $yoffset = $maxYOffset;
         }
         for ($x = 0; $x < $subWidth; $x++) {
             $xoffset = intval32bits($x << self::$BLOCK_SIZE_POWER);
             $maxXOffset = $width - self::$BLOCK_SIZE;
             if ($xoffset > $maxXOffset) {
                 $xoffset = $maxXOffset;
             }
             $sum = 0;
             $min = 0xff;
             $max = 0;
             for ($yy = 0, $offset = $yoffset * $width + $xoffset; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
                 for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
                     $pixel = intval32bits(intval($luminances[intval($offset + $xx)]) & 0xff);
                     $sum += $pixel;
                     // still looking for good contrast
                     if ($pixel < $min) {
                         $min = $pixel;
                     }
                     if ($pixel > $max) {
                         $max = $pixel;
                     }
                 }
                 // short-circuit min/max tests once dynamic range is met
                 if ($max - $min > self::$MIN_DYNAMIC_RANGE) {
                     // finish the rest of the rows quickly
                     for ($yy++, $offset += $width; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
                         for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
                             $sum += intval32bits($luminances[$offset + $xx] & 0xff);
                         }
                     }
                 }
             }
             // The default estimate is the average of the values in the block.
             $average = intval32bits($sum >> self::$BLOCK_SIZE_POWER * 2);
             if ($max - $min <= self::$MIN_DYNAMIC_RANGE) {
                 // If variation within the block is low, assume this is a block with only light or only
                 // dark pixels. In that case we do not want to use the average, as it would divide this
                 // low contrast area into black and white pixels, essentially creating data out of noise.
                 //
                 // The default assumption is that the block is light/background. Since no estimate for
                 // the level of dark pixels exists locally, use half the min for the block.
                 $average = intval($min / 2);
                 if ($y > 0 && $x > 0) {
                     // Correct the "white background" assumption for blocks that have neighbors by comparing
                     // the pixels in this block to the previously calculated black points. This is based on
                     // the fact that dark barcode symbology is always surrounded by some amount of light
                     // background for which reasonable black point estimates were made. The bp estimated at
                     // the boundaries is used for the interior.
                     // The (min < bp) is arbitrary but works better than other heuristics that were tried.
                     $averageNeighborBlackPoint = intval(($blackPoints[$y - 1][$x] + 2 * $blackPoints[$y][$x - 1] + $blackPoints[$y - 1][$x - 1]) / 4);
                     if ($min < $averageNeighborBlackPoint) {
                         $average = $averageNeighborBlackPoint;
                     }
                 }
             }
             $blackPoints[$y][$x] = intval($average);
         }
     }
     return $blackPoints;
 }
 public function renderThumbnail()
 {
     $width = intval($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
     $height = intval($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
     $pixels = array();
     //new int[width * height];
     $yuv = $this->yuvData;
     $inputOffset = $this->top * $this->dataWidth + $this->left;
     for ($y = 0; $y < $height; $y++) {
         $outputOffset = $y * $width;
         for ($x = 0; $x < $width; $x++) {
             $grey = intval32bits($yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff);
             $pixels[$outputOffset + $x] = intval32bits(0xff000000 | $grey * 0x10101);
         }
         $inputOffset += $this->dataWidth * self::$THUMBNAIL_SCALE_FACTOR;
     }
     return $pixels;
 }
 /**
  * @param $i; bit to get
  * @return true iff bit i is set
  */
 public function get($i)
 {
     $key = intval($i / 32);
     return intval32bits($this->bits[$key] & 1 << ($i & 0x1f)) != 0;
 }
 private static function estimateBlackPoint($buckets)
 {
     // Find the tallest peak in the histogram.
     $numBuckets = count($buckets);
     $maxBucketCount = 0;
     $firstPeak = 0;
     $firstPeakSize = 0;
     for ($x = 0; $x < $numBuckets; $x++) {
         if ($buckets[$x] > $firstPeakSize) {
             $firstPeak = $x;
             $firstPeakSize = $buckets[$x];
         }
         if ($buckets[$x] > $maxBucketCount) {
             $maxBucketCount = $buckets[$x];
         }
     }
     // Find the second-tallest peak which is somewhat far from the tallest peak.
     $secondPeak = 0;
     $secondPeakScore = 0;
     for ($x = 0; $x < $numBuckets; $x++) {
         $distanceToBiggest = $x - $firstPeak;
         // Encourage more distant second peaks by multiplying by square of distance.
         $score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest;
         if ($score > $secondPeakScore) {
             $secondPeak = $x;
             $secondPeakScore = $score;
         }
     }
     // Make sure firstPeak corresponds to the black peak.
     if ($firstPeak > $secondPeak) {
         $temp = $firstPeak;
         $firstPeak = $secondPeak;
         $secondPeak = $temp;
     }
     // If there is too little contrast in the image to pick a meaningful black point, throw rather
     // than waste time trying to decode the image, and risk false positives.
     if ($secondPeak - $firstPeak <= $numBuckets / 16) {
         throw NotFoundException::getNotFoundInstance();
     }
     // Find a valley between them that is low and closer to the white peak.
     $bestValley = $secondPeak - 1;
     $bestValleyScore = -1;
     for ($x = $secondPeak - 1; $x > $firstPeak; $x--) {
         $fromFirst = $x - $firstPeak;
         $score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]);
         if ($score > $bestValleyScore) {
             $bestValley = $x;
             $bestValleyScore = $score;
         }
     }
     return intval32bits($bestValley << self::$LUMINANCE_SHIFT);
 }