/// <summary> /// Gets the row indicator column. /// </summary> /// <returns>The row indicator column.</returns> /// <param name="image">Image.</param> /// <param name="boundingBox">Bounding box.</param> /// <param name="startPoint">Start point.</param> /// <param name="leftToRight">If set to <c>true</c> left to right.</param> /// <param name="minCodewordWidth">Minimum codeword width.</param> /// <param name="maxCodewordWidth">Max codeword width.</param> private static DetectionResultRowIndicatorColumn getRowIndicatorColumn(BitMatrix image, BoundingBox boundingBox, ResultPoint startPoint, bool leftToRight, int minCodewordWidth, int maxCodewordWidth) { DetectionResultRowIndicatorColumn rowIndicatorColumn = new DetectionResultRowIndicatorColumn(boundingBox, leftToRight); for (int i = 0; i < 2; i++) { int increment = i == 0 ? 1 : -1; int startColumn = (int)startPoint.X; for (int imageRow = (int)startPoint.Y; imageRow <= boundingBox.MaxY && imageRow >= boundingBox.MinY; imageRow += increment) { Codeword codeword = detectCodeword(image, 0, image.Width, leftToRight, startColumn, imageRow, minCodewordWidth, maxCodewordWidth); if (codeword != null) { rowIndicatorColumn.setCodeword(imageRow, codeword); if (leftToRight) { startColumn = codeword.StartX; } else { startColumn = codeword.EndX; } } } } return(rowIndicatorColumn); }
/// <summary> /// Gets the barcode metadata. /// </summary> /// <returns>The barcode metadata.</returns> /// <param name="leftRowIndicatorColumn">Left row indicator column.</param> /// <param name="rightRowIndicatorColumn">Right row indicator column.</param> private static BarcodeMetadata getBarcodeMetadata(DetectionResultRowIndicatorColumn leftRowIndicatorColumn, DetectionResultRowIndicatorColumn rightRowIndicatorColumn) { BarcodeMetadata leftBarcodeMetadata; if (leftRowIndicatorColumn == null || (leftBarcodeMetadata = leftRowIndicatorColumn.getBarcodeMetadata()) == null) { return(rightRowIndicatorColumn == null ? null : rightRowIndicatorColumn.getBarcodeMetadata()); } BarcodeMetadata rightBarcodeMetadata; if (rightRowIndicatorColumn == null || (rightBarcodeMetadata = rightRowIndicatorColumn.getBarcodeMetadata()) == null) { return(leftBarcodeMetadata); } if (leftBarcodeMetadata.ColumnCount != rightBarcodeMetadata.ColumnCount && leftBarcodeMetadata.ErrorCorrectionLevel != rightBarcodeMetadata.ErrorCorrectionLevel && leftBarcodeMetadata.RowCount != rightBarcodeMetadata.RowCount) { return(null); } return(leftBarcodeMetadata); }
/// <summary> /// Gets the bounding box. /// </summary> /// <returns>The bounding box.</returns> /// <param name="leftRowIndicatorColumn">Left row indicator column.</param> /// <param name="rightRowIndicatorColumn">Right row indicator column.</param> private static BoundingBox GetBoundingBox(DetectionResultRowIndicatorColumn leftRowIndicatorColumn, DetectionResultRowIndicatorColumn rightRowIndicatorColumn) { BoundingBox box1 = AdjustBoundingBox(leftRowIndicatorColumn); BoundingBox box2 = AdjustBoundingBox(rightRowIndicatorColumn); return(BoundingBox.Merge(box1, box2)); }
/// <summary> /// Merge the specified leftRowIndicatorColumn and rightRowIndicatorColumn. /// </summary> /// <param name="leftRowIndicatorColumn">Left row indicator column.</param> /// <param name="rightRowIndicatorColumn">Right row indicator column.</param> private static DetectionResult Merge(DetectionResultRowIndicatorColumn leftRowIndicatorColumn, DetectionResultRowIndicatorColumn rightRowIndicatorColumn) { if (leftRowIndicatorColumn == null && rightRowIndicatorColumn == null) { return(null); } BarcodeMetadata barcodeMetadata = GetBarcodeMetadata(leftRowIndicatorColumn, rightRowIndicatorColumn); if (barcodeMetadata == null) { return(null); } BoundingBox boundingBox = GetBoundingBox(leftRowIndicatorColumn, rightRowIndicatorColumn); return(new DetectionResult(barcodeMetadata, boundingBox)); }
/// <summary> /// Adjusts the bounding box. /// </summary> /// <returns>The bounding box.</returns> /// <param name="rowIndicatorColumn">Row indicator column.</param> private static BoundingBox adjustBoundingBox(DetectionResultRowIndicatorColumn rowIndicatorColumn) { if (rowIndicatorColumn == null) { return(null); } int[] rowHeights = rowIndicatorColumn.getRowHeights(); if (rowHeights == null) { return(null); } int maxRowHeight = getMax(rowHeights); int missingStartRows = 0; foreach (int rowHeight in rowHeights) { missingStartRows += maxRowHeight - rowHeight; if (rowHeight > 0) { break; } } Codeword[] codewords = rowIndicatorColumn.Codewords; for (int row = 0; missingStartRows > 0 && codewords[row] == null; row++) { missingStartRows--; } int missingEndRows = 0; for (int row = rowHeights.Length - 1; row >= 0; row--) { missingEndRows += maxRowHeight - rowHeights[row]; if (rowHeights[row] > 0) { break; } } for (int row = codewords.Length - 1; missingEndRows > 0 && codewords[row] == null; row--) { missingEndRows--; } return(rowIndicatorColumn.Box.addMissingRows(missingStartRows, missingEndRows, rowIndicatorColumn.IsLeft)); }
/// <summary> /// Decode the specified image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight, minCodewordWidth /// and maxCodewordWidth. /// TODO: don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern /// columns. That way width can be deducted from the pattern column. /// This approach also allows to detect more details about the barcode, e.g. if a bar type (white or black) is wider /// than it should be. This can happen if the scanner used a bad blackpoint. /// </summary> /// <param name="image">Image.</param> /// <param name="imageTopLeft">Image top left.</param> /// <param name="imageBottomLeft">Image bottom left.</param> /// <param name="imageTopRight">Image top right.</param> /// <param name="imageBottomRight">Image bottom right.</param> /// <param name="minCodewordWidth">Minimum codeword width.</param> /// <param name="maxCodewordWidth">Max codeword width.</param> public static DecoderResult decode(BitMatrix image, ResultPoint imageTopLeft, ResultPoint imageBottomLeft, ResultPoint imageTopRight, ResultPoint imageBottomRight, int minCodewordWidth, int maxCodewordWidth) { BoundingBox boundingBox = BoundingBox.Create(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight); if (boundingBox == null) return null; DetectionResultRowIndicatorColumn leftRowIndicatorColumn = null; DetectionResultRowIndicatorColumn rightRowIndicatorColumn = null; DetectionResult detectionResult = null; for (int i = 0; i < 2; i++) { if (imageTopLeft != null) { leftRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopLeft, true, minCodewordWidth, maxCodewordWidth); } if (imageTopRight != null) { rightRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopRight, false, minCodewordWidth, maxCodewordWidth); } detectionResult = merge(leftRowIndicatorColumn, rightRowIndicatorColumn); if (detectionResult == null) { // TODO Based on Owen's Comments in <see cref="ZXing.ReaderException"/>, this method has been modified to continue silently // if a barcode was not decoded where it was detected instead of throwing a new exception object. return null; } if (i == 0 && detectionResult.Box != null && (detectionResult.Box.MinY < boundingBox.MinY || detectionResult.Box.MaxY > boundingBox.MaxY)) { boundingBox = detectionResult.Box; } else { detectionResult.Box = boundingBox; break; } } int maxBarcodeColumn = detectionResult.ColumnCount + 1; detectionResult.DetectionResultColumns[0] = leftRowIndicatorColumn; detectionResult.DetectionResultColumns[maxBarcodeColumn] = rightRowIndicatorColumn; bool leftToRight = leftRowIndicatorColumn != null; for (int barcodeColumnCount = 1; barcodeColumnCount <= maxBarcodeColumn; barcodeColumnCount++) { int barcodeColumn = leftToRight ? barcodeColumnCount : maxBarcodeColumn - barcodeColumnCount; if (detectionResult.DetectionResultColumns[barcodeColumn] != null) { // This will be the case for the opposite row indicator column, which doesn't need to be decoded again. continue; } DetectionResultColumn detectionResultColumn; if (barcodeColumn == 0 || barcodeColumn == maxBarcodeColumn) { detectionResultColumn = new DetectionResultRowIndicatorColumn(boundingBox, barcodeColumn == 0); } else { detectionResultColumn = new DetectionResultColumn(boundingBox); } detectionResult.DetectionResultColumns[barcodeColumn] = detectionResultColumn; int startColumn = -1; int previousStartColumn = startColumn; // TODO start at a row for which we know the start position, then detect upwards and downwards from there. for (int imageRow = boundingBox.MinY; imageRow <= boundingBox.MaxY; imageRow++) { startColumn = getStartColumn(detectionResult, barcodeColumn, imageRow, leftToRight); if (startColumn < 0 || startColumn > boundingBox.MaxX) { if (previousStartColumn == -1) { continue; } startColumn = previousStartColumn; } Codeword codeword = detectCodeword(image, boundingBox.MinX, boundingBox.MaxX, leftToRight, startColumn, imageRow, minCodewordWidth, maxCodewordWidth); if (codeword != null) { detectionResultColumn.setCodeword(imageRow, codeword); previousStartColumn = startColumn; minCodewordWidth = Math.Min(minCodewordWidth, codeword.Width); maxCodewordWidth = Math.Max(maxCodewordWidth, codeword.Width); } } } return createDecoderResult(detectionResult); }
/// <summary> /// Gets the row indicator column. /// </summary> /// <returns>The row indicator column.</returns> /// <param name="image">Image.</param> /// <param name="boundingBox">Bounding box.</param> /// <param name="startPoint">Start point.</param> /// <param name="leftToRight">If set to <c>true</c> left to right.</param> /// <param name="minCodewordWidth">Minimum codeword width.</param> /// <param name="maxCodewordWidth">Max codeword width.</param> private static DetectionResultRowIndicatorColumn getRowIndicatorColumn(BitMatrix image, BoundingBox boundingBox, ResultPoint startPoint, bool leftToRight, int minCodewordWidth, int maxCodewordWidth) { DetectionResultRowIndicatorColumn rowIndicatorColumn = new DetectionResultRowIndicatorColumn(boundingBox, leftToRight); for (int i = 0; i < 2; i++) { int increment = i == 0 ? 1 : -1; int startColumn = (int) startPoint.X; for (int imageRow = (int) startPoint.Y; imageRow <= boundingBox.MaxY && imageRow >= boundingBox.MinY; imageRow += increment) { Codeword codeword = detectCodeword(image, 0, image.Width, leftToRight, startColumn, imageRow, minCodewordWidth, maxCodewordWidth); if (codeword != null) { rowIndicatorColumn.setCodeword(imageRow, codeword); if (leftToRight) { startColumn = codeword.StartX; } else { startColumn = codeword.EndX; } } } } return rowIndicatorColumn; }
/// <summary> /// Gets the barcode metadata. /// </summary> /// <returns>The barcode metadata.</returns> /// <param name="leftRowIndicatorColumn">Left row indicator column.</param> /// <param name="rightRowIndicatorColumn">Right row indicator column.</param> private static BarcodeMetadata getBarcodeMetadata(DetectionResultRowIndicatorColumn leftRowIndicatorColumn, DetectionResultRowIndicatorColumn rightRowIndicatorColumn) { BarcodeMetadata leftBarcodeMetadata; if (leftRowIndicatorColumn == null || (leftBarcodeMetadata = leftRowIndicatorColumn.getBarcodeMetadata()) == null) { return rightRowIndicatorColumn == null ? null : rightRowIndicatorColumn.getBarcodeMetadata(); } BarcodeMetadata rightBarcodeMetadata; if (rightRowIndicatorColumn == null || (rightBarcodeMetadata = rightRowIndicatorColumn.getBarcodeMetadata()) == null) { return leftBarcodeMetadata; } if (leftBarcodeMetadata.ColumnCount != rightBarcodeMetadata.ColumnCount && leftBarcodeMetadata.ErrorCorrectionLevel != rightBarcodeMetadata.ErrorCorrectionLevel && leftBarcodeMetadata.RowCount != rightBarcodeMetadata.RowCount) { return null; } return leftBarcodeMetadata; }
/// <summary> /// Adjusts the bounding box. /// </summary> /// <returns>The bounding box.</returns> /// <param name="rowIndicatorColumn">Row indicator column.</param> private static BoundingBox adjustBoundingBox(DetectionResultRowIndicatorColumn rowIndicatorColumn) { if (rowIndicatorColumn == null) { return null; } int[] rowHeights = rowIndicatorColumn.getRowHeights(); if (rowHeights == null) { return null; } int maxRowHeight = getMax(rowHeights); int missingStartRows = 0; foreach (int rowHeight in rowHeights) { missingStartRows += maxRowHeight - rowHeight; if (rowHeight > 0) { break; } } Codeword[] codewords = rowIndicatorColumn.Codewords; for (int row = 0; missingStartRows > 0 && codewords[row] == null; row++) { missingStartRows--; } int missingEndRows = 0; for (int row = rowHeights.Length - 1; row >= 0; row--) { missingEndRows += maxRowHeight - rowHeights[row]; if (rowHeights[row] > 0) { break; } } for (int row = codewords.Length - 1; missingEndRows > 0 && codewords[row] == null; row--) { missingEndRows--; } return rowIndicatorColumn.Box.addMissingRows(missingStartRows, missingEndRows, rowIndicatorColumn.IsLeft); }
/// <summary> /// Merge the specified leftRowIndicatorColumn and rightRowIndicatorColumn. /// </summary> /// <param name="leftRowIndicatorColumn">Left row indicator column.</param> /// <param name="rightRowIndicatorColumn">Right row indicator column.</param> private static DetectionResult merge(DetectionResultRowIndicatorColumn leftRowIndicatorColumn, DetectionResultRowIndicatorColumn rightRowIndicatorColumn) { if (leftRowIndicatorColumn == null && rightRowIndicatorColumn == null) { return null; } BarcodeMetadata barcodeMetadata = getBarcodeMetadata(leftRowIndicatorColumn, rightRowIndicatorColumn); if (barcodeMetadata == null) { return null; } BoundingBox boundingBox = BoundingBox.merge(adjustBoundingBox(leftRowIndicatorColumn), adjustBoundingBox(rightRowIndicatorColumn)); return new DetectionResult(barcodeMetadata, boundingBox); }
/// <summary> /// Decode the specified image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight, minCodewordWidth /// and maxCodewordWidth. /// TODO: don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern /// columns. That way width can be deducted from the pattern column. /// This approach also allows to detect more details about the barcode, e.g. if a bar type (white or black) is wider /// than it should be. This can happen if the scanner used a bad blackpoint. /// </summary> /// <param name="image">Image.</param> /// <param name="imageTopLeft">Image top left.</param> /// <param name="imageBottomLeft">Image bottom left.</param> /// <param name="imageTopRight">Image top right.</param> /// <param name="imageBottomRight">Image bottom right.</param> /// <param name="minCodewordWidth">Minimum codeword width.</param> /// <param name="maxCodewordWidth">Max codeword width.</param> public static DecoderResult decode(BitMatrix image, ResultPoint imageTopLeft, ResultPoint imageBottomLeft, ResultPoint imageTopRight, ResultPoint imageBottomRight, int minCodewordWidth, int maxCodewordWidth) { BoundingBox boundingBox = BoundingBox.Create(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight); if (boundingBox == null) { return(null); } DetectionResultRowIndicatorColumn leftRowIndicatorColumn = null; DetectionResultRowIndicatorColumn rightRowIndicatorColumn = null; DetectionResult detectionResult = null; for (int i = 0; i < 2; i++) { if (imageTopLeft != null) { leftRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopLeft, true, minCodewordWidth, maxCodewordWidth); } if (imageTopRight != null) { rightRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopRight, false, minCodewordWidth, maxCodewordWidth); } detectionResult = merge(leftRowIndicatorColumn, rightRowIndicatorColumn); if (detectionResult == null) { // TODO Based on Owen's Comments in <see cref="ZXing.ReaderException"/>, this method has been modified to continue silently // if a barcode was not decoded where it was detected instead of throwing a new exception object. return(null); } if (i == 0 && detectionResult.Box != null && (detectionResult.Box.MinY < boundingBox.MinY || detectionResult.Box.MaxY > boundingBox.MaxY)) { boundingBox = detectionResult.Box; } else { detectionResult.Box = boundingBox; break; } } int maxBarcodeColumn = detectionResult.ColumnCount + 1; detectionResult.DetectionResultColumns[0] = leftRowIndicatorColumn; detectionResult.DetectionResultColumns[maxBarcodeColumn] = rightRowIndicatorColumn; bool leftToRight = leftRowIndicatorColumn != null; for (int barcodeColumnCount = 1; barcodeColumnCount <= maxBarcodeColumn; barcodeColumnCount++) { int barcodeColumn = leftToRight ? barcodeColumnCount : maxBarcodeColumn - barcodeColumnCount; if (detectionResult.DetectionResultColumns[barcodeColumn] != null) { // This will be the case for the opposite row indicator column, which doesn't need to be decoded again. continue; } DetectionResultColumn detectionResultColumn; if (barcodeColumn == 0 || barcodeColumn == maxBarcodeColumn) { detectionResultColumn = new DetectionResultRowIndicatorColumn(boundingBox, barcodeColumn == 0); } else { detectionResultColumn = new DetectionResultColumn(boundingBox); } detectionResult.DetectionResultColumns[barcodeColumn] = detectionResultColumn; int startColumn = -1; int previousStartColumn = startColumn; // TODO start at a row for which we know the start position, then detect upwards and downwards from there. for (int imageRow = boundingBox.MinY; imageRow <= boundingBox.MaxY; imageRow++) { startColumn = getStartColumn(detectionResult, barcodeColumn, imageRow, leftToRight); if (startColumn < 0 || startColumn > boundingBox.MaxX) { if (previousStartColumn == -1) { continue; } startColumn = previousStartColumn; } Codeword codeword = detectCodeword(image, boundingBox.MinX, boundingBox.MaxX, leftToRight, startColumn, imageRow, minCodewordWidth, maxCodewordWidth); if (codeword != null) { detectionResultColumn.setCodeword(imageRow, codeword); previousStartColumn = startColumn; minCodewordWidth = Math.Min(minCodewordWidth, codeword.Width); maxCodewordWidth = Math.Max(maxCodewordWidth, codeword.Width); } } } return(createDecoderResult(detectionResult)); }
/// <summary> /// Gets the bounding box. /// </summary> /// <returns>The bounding box.</returns> /// <param name="leftRowIndicatorColumn">Left row indicator column.</param> /// <param name="rightRowIndicatorColumn">Right row indicator column.</param> private static BoundingBox GetBoundingBox(DetectionResultRowIndicatorColumn leftRowIndicatorColumn, DetectionResultRowIndicatorColumn rightRowIndicatorColumn) { BoundingBox box1 = AdjustBoundingBox(leftRowIndicatorColumn); BoundingBox box2 = AdjustBoundingBox(rightRowIndicatorColumn); return BoundingBox.Merge(box1, box2); }