/**
   * 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.application.food.zxing.pdf417.PDF417Reader#extractPureBits(com.application.food.zxing.common.BitMatrix)
   * @see
   *     com.application.food.zxing.datamatrix.DataMatrixReader#extractPureBits(com.application.food.zxing.common.BitMatrix)
   */
  private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {

    int[] leftTopBlack = image.getTopLeftOnBit();
    int[] rightBottomBlack = image.getBottomRightOnBit();
    if (leftTopBlack == null || rightBottomBlack == null) {
      throw NotFoundException.getNotFoundInstance();
    }

    float moduleSize = moduleSize(leftTopBlack, image);

    int top = leftTopBlack[1];
    int bottom = rightBottomBlack[1];
    int left = leftTopBlack[0];
    int right = rightBottomBlack[0];

    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);
    }

    int matrixWidth = Math.round((right - left + 1) / moduleSize);
    int matrixHeight = Math.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.
    int nudge = Math.round(moduleSize / 2.0f);
    top += nudge;
    left += nudge;

    // Now just read off the bits
    BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);
    for (int y = 0; y < matrixHeight; y++) {
      int iOffset = top + (int) (y * moduleSize);
      for (int x = 0; x < matrixWidth; x++) {
        if (image.get(left + (int) (x * moduleSize), iOffset)) {
          bits.set(x, y);
        }
      }
    }
    return bits;
  }
 protected static int parseFinderValue(int[] counters, int[][] finderPatterns)
     throws NotFoundException {
   for (int value = 0; value < finderPatterns.length; value++) {
     if (patternMatchVariance(counters, finderPatterns[value], MAX_INDIVIDUAL_VARIANCE)
         < MAX_AVG_VARIANCE) {
       return value;
     }
   }
   throw NotFoundException.getNotFoundInstance();
 }
  /**
   * @return 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 com.application.food.zxing.NotFoundException if 3 such finder patterns do not exist
   */
  private FinderPattern[] selectBestPatterns() throws NotFoundException {

    int startSize = possibleCenters.size();
    if (startSize < 3) {
      // Couldn't find enough finder patterns
      throw NotFoundException.getNotFoundInstance();
    }

    // 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
      float totalModuleSize = 0.0f;
      float square = 0.0f;
      for (FinderPattern center : possibleCenters) {
        float size = center.getEstimatedModuleSize();
        totalModuleSize += size;
        square += size * size;
      }
      float average = totalModuleSize / (float) startSize;
      float stdDev = (float) Math.sqrt(square / startSize - average * average);

      Collections.sort(possibleCenters, new FurthestFromAverageComparator(average));

      float limit = Math.max(0.2f * average, stdDev);

      for (int i = 0; i < possibleCenters.size() && possibleCenters.size() > 3; i++) {
        FinderPattern pattern = possibleCenters.get(i);
        if (Math.abs(pattern.getEstimatedModuleSize() - average) > limit) {
          possibleCenters.remove(i);
          i--;
        }
      }
    }

    if (possibleCenters.size() > 3) {
      // Throw away all but those first size candidate points we found.

      float totalModuleSize = 0.0f;
      for (FinderPattern possibleCenter : possibleCenters) {
        totalModuleSize += possibleCenter.getEstimatedModuleSize();
      }

      float average = totalModuleSize / (float) possibleCenters.size();

      Collections.sort(possibleCenters, new CenterComparator(average));

      possibleCenters.subList(3, possibleCenters.size()).clear();
    }

    return new FinderPattern[] {
      possibleCenters.get(0), possibleCenters.get(1), possibleCenters.get(2)
    };
  }
 private static float moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException {
   int height = image.getHeight();
   int width = image.getWidth();
   int x = leftTopBlack[0];
   int y = leftTopBlack[1];
   boolean inBlack = true;
   int transitions = 0;
   while (x < width && y < height) {
     if (inBlack != image.get(x, y)) {
       if (++transitions == 5) {
         break;
       }
       inBlack = !inBlack;
     }
     x++;
     y++;
   }
   if (x == width || y == height) {
     throw NotFoundException.getNotFoundInstance();
   }
   return (x - leftTopBlack[0]) / 7.0f;
 }