// Applies simple sharpening to the row data to improve performance of the 1D Readers. @Override public BitArray getBlackRow(int y, BitArray row) throws NotFoundException { LuminanceSource source = getLuminanceSource(); int width = source.getWidth(); if (row == null || row.getSize() < width) { row = new BitArray(width); } else { row.clear(); } initArrays(width); byte[] localLuminances = source.getRow(y, luminances); int[] localBuckets = buckets; for (int x = 0; x < width; x++) { int pixel = localLuminances[x] & 0xff; localBuckets[pixel >> LUMINANCE_SHIFT]++; } int blackPoint = estimateBlackPoint(localBuckets); int left = localLuminances[0] & 0xff; int center = localLuminances[1] & 0xff; for (int x = 1; x < width - 1; x++) { int right = localLuminances[x + 1] & 0xff; // A simple -1 4 -1 box filter with a weight of 2. int luminance = ((center * 4) - left - right) / 2; if (luminance < blackPoint) { row.set(x); } left = center; center = right; } return row; }
/** * Identify where the end of the middle / payload section ends. * * @param row row of black/white values to search * @return Array, containing index of start of 'end block' and end of 'end block' * @throws NotFoundException */ int[] decodeEnd(BitArray row) throws NotFoundException { // For convenience, reverse the row and then // search from 'the start' for the end block row.reverse(); try { int endStart = skipWhiteSpace(row); int[] endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED); // The start & end patterns must be pre/post fixed by a quiet zone. This // zone must be at least 10 times the width of a narrow line. // ref: http://www.barcode-1.net/i25code.html validateQuietZone(row, endPattern[0]); // Now recalculate the indices of where the 'endblock' starts & stops to // accommodate // the reversed nature of the search int temp = endPattern[0]; endPattern[0] = row.getSize() - endPattern[1]; endPattern[1] = row.getSize() - temp; return endPattern; } finally { // Put the row back the right way. row.reverse(); } }
/** * Skip all whitespace until we get to the first black line. * * @param row row of black/white values to search * @return index of the first black line. * @throws NotFoundException Throws exception if no black lines are found in the row */ private static int skipWhiteSpace(BitArray row) throws NotFoundException { int width = row.getSize(); int endStart = 0; while (endStart < width) { if (row.get(endStart)) { break; } endStart++; } if (endStart == width) { throw NotFoundException.getNotFoundInstance(); } return endStart; }
/** * @param row row of black/white values to search * @param rowOffset position to start search * @param pattern pattern of counts of number of black and white pixels that are being searched * for as a pattern * @return start/end horizontal offset of guard pattern, as an array of two ints * @throws NotFoundException if pattern is not found */ private static int[] findGuardPattern(BitArray row, int rowOffset, int[] pattern) throws NotFoundException { // TODO: This is very similar to implementation in UPCEANReader. Consider if they can be // merged to a single method. int patternLength = pattern.length; int[] counters = new int[patternLength]; int width = row.getSize(); boolean isWhite = false; int counterPosition = 0; int patternStart = rowOffset; for (int x = rowOffset; x < width; x++) { boolean pixel = row.get(x); if (pixel ^ isWhite) { counters[counterPosition]++; } else { if (counterPosition == patternLength - 1) { if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) { return new int[] {patternStart, x}; } patternStart += counters[0] + counters[1]; for (int y = 2; y < patternLength; y++) { counters[y - 2] = counters[y]; } counters[patternLength - 2] = 0; counters[patternLength - 1] = 0; counterPosition--; } else { counterPosition++; } counters[counterPosition] = 1; isWhite = !isWhite; } } throw NotFoundException.getNotFoundInstance(); }