/// <summary> /// Adjusts the codeword count. /// </summary> /// <param name="detectionResult">Detection result.</param> /// <param name="barcodeMatrix">Barcode matrix.</param> private static bool adjustCodewordCount(DetectionResult detectionResult, BarcodeValue[][] barcodeMatrix) { var barcodeMatrix01 = barcodeMatrix[0][1]; int[] numberOfCodewords = barcodeMatrix01.getValue(); int calculatedNumberOfCodewords = detectionResult.ColumnCount * detectionResult.RowCount - getNumberOfECCodeWords(detectionResult.ErrorCorrectionLevel); if (numberOfCodewords.Length == 0) { if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common.MAX_CODEWORDS_IN_BARCODE) { return(false); } barcodeMatrix01.setValue(calculatedNumberOfCodewords); } else if (numberOfCodewords[0] != calculatedNumberOfCodewords) { // The calculated one is more reliable as it is derived from the row indicator columns barcodeMatrix01.setValue(calculatedNumberOfCodewords); } return(true); }
/// <summary> /// Creates the barcode matrix. /// </summary> /// <returns>The barcode matrix.</returns> /// <param name="detectionResult">Detection result.</param> private static BarcodeValue[][] createBarcodeMatrix(DetectionResult detectionResult) { // Manually setup Jagged Array in C# BarcodeValue[][] barcodeMatrix = new BarcodeValue[detectionResult.RowCount][]; for (int row = 0; row < barcodeMatrix.Length; row++) { barcodeMatrix[row] = new BarcodeValue[detectionResult.ColumnCount + 2]; for (int col = 0; col < barcodeMatrix[row].Length; col++) { barcodeMatrix[row][col] = new BarcodeValue(); } } int column = -1; foreach (DetectionResultColumn detectionResultColumn in detectionResult.getDetectionResultColumns()) { column++; if (detectionResultColumn == null) { continue; } foreach (Codeword codeword in detectionResultColumn.Codewords) { if (codeword == null || codeword.RowNumber == -1) { continue; } barcodeMatrix[codeword.RowNumber][column].setValue(codeword.Value); } } return(barcodeMatrix); }
/// <summary> /// Gets the start column. /// </summary> /// <returns>The start column.</returns> /// <param name="detectionResult">Detection result.</param> /// <param name="barcodeColumn">Barcode column.</param> /// <param name="imageRow">Image row.</param> /// <param name="leftToRight">If set to <c>true</c> left to right.</param> private static int getStartColumn(DetectionResult detectionResult, int barcodeColumn, int imageRow, bool leftToRight) { int offset = leftToRight ? 1 : -1; Codeword codeword = null; if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { codeword = detectionResult.DetectionResultColumns[barcodeColumn - offset].getCodeword(imageRow); } if (codeword != null) { return(leftToRight ? codeword.EndX : codeword.StartX); } codeword = detectionResult.DetectionResultColumns[barcodeColumn].getCodewordNearby(imageRow); if (codeword != null) { return(leftToRight ? codeword.StartX : codeword.EndX); } if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { codeword = detectionResult.DetectionResultColumns[barcodeColumn - offset].getCodewordNearby(imageRow); } if (codeword != null) { return(leftToRight ? codeword.EndX : codeword.StartX); } int skippedColumns = 0; while (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { barcodeColumn -= offset; foreach (Codeword previousRowCodeword in detectionResult.DetectionResultColumns[barcodeColumn].Codewords) { if (previousRowCodeword != null) { return((leftToRight ? previousRowCodeword.EndX : previousRowCodeword.StartX) + offset * skippedColumns * (previousRowCodeword.EndX - previousRowCodeword.StartX)); } } skippedColumns++; } return(leftToRight ? detectionResult.Box.MinX : detectionResult.Box.MaxX); }
/// <summary> /// Creates the decoder result. /// </summary> /// <returns>The decoder result.</returns> /// <param name="detectionResult">Detection result.</param> private static DecoderResult createDecoderResult(DetectionResult detectionResult) { BarcodeValue[][] barcodeMatrix = createBarcodeMatrix(detectionResult); if (barcodeMatrix == null) { return(null); } if (!adjustCodewordCount(detectionResult, barcodeMatrix)) { return(null); } List <int> erasures = new List <int>(); int[] codewords = new int[detectionResult.RowCount * detectionResult.ColumnCount]; List <int[]> ambiguousIndexValuesList = new List <int[]>(); List <int> ambiguousIndexesList = new List <int>(); for (int row = 0; row < detectionResult.RowCount; row++) { for (int column = 0; column < detectionResult.ColumnCount; column++) { int[] values = barcodeMatrix[row][column + 1].getValue(); int codewordIndex = row * detectionResult.ColumnCount + column; if (values.Length == 0) { erasures.Add(codewordIndex); } else if (values.Length == 1) { codewords[codewordIndex] = values[0]; } else { ambiguousIndexesList.Add(codewordIndex); ambiguousIndexValuesList.Add(values); } } } int[][] ambiguousIndexValues = new int[ambiguousIndexValuesList.Count][]; for (int i = 0; i < ambiguousIndexValues.Length; i++) { ambiguousIndexValues[i] = ambiguousIndexValuesList[i]; } return(createDecoderResultFromAmbiguousValues(detectionResult.ErrorCorrectionLevel, codewords, erasures.ToArray(), ambiguousIndexesList.ToArray(), ambiguousIndexValues)); }
/// <summary> /// Creates the barcode matrix. /// </summary> /// <returns>The barcode matrix.</returns> /// <param name="detectionResult">Detection result.</param> private static BarcodeValue[][] createBarcodeMatrix(DetectionResult detectionResult) { // Manually setup Jagged Array in C# var barcodeMatrix = new BarcodeValue[detectionResult.RowCount][]; for (int row = 0; row < barcodeMatrix.Length; row++) { barcodeMatrix[row] = new BarcodeValue[detectionResult.ColumnCount + 2]; for (int col = 0; col < barcodeMatrix[row].Length; col++) { barcodeMatrix[row][col] = new BarcodeValue(); } } int column = 0; foreach (DetectionResultColumn detectionResultColumn in detectionResult.getDetectionResultColumns()) { if (detectionResultColumn != null) { foreach (Codeword codeword in detectionResultColumn.Codewords) { if (codeword != null) { int rowNumber = codeword.RowNumber; if (rowNumber >= 0) { if (rowNumber >= barcodeMatrix.Length) { // We have more rows than the barcode metadata allows for, ignore them. continue; } barcodeMatrix[rowNumber][column].setValue(codeword.Value); } } } } column++; } return(barcodeMatrix); }
/// <summary> /// Adjusts the codeword count. /// </summary> /// <param name="detectionResult">Detection result.</param> /// <param name="barcodeMatrix">Barcode matrix.</param> private static void AdjustCodewordCount(DetectionResult detectionResult, BarcodeValue[][] barcodeMatrix) { int[] numberOfCodewords = barcodeMatrix[0][1].GetValue(); int calculatedNumberOfCodewords = detectionResult.ColumnCount * detectionResult.RowCount - GetNumberOfECCodeWords(detectionResult.ErrorCorrectionLevel); if (numberOfCodewords.Length == 0) { if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common.MAX_CODEWORDS_IN_BARCODE) { throw ReaderException.Instance; } barcodeMatrix[0][1].SetValue(calculatedNumberOfCodewords); } else if (numberOfCodewords[0] != calculatedNumberOfCodewords) { // The calculated one is more reliable as it is derived from the row indicator columns barcodeMatrix[0][1].SetValue(calculatedNumberOfCodewords); } }
/// <summary> /// Gets the start column. /// </summary> /// <returns>The start column.</returns> /// <param name="detectionResult">Detection result.</param> /// <param name="barcodeColumn">Barcode column.</param> /// <param name="imageRow">Image row.</param> /// <param name="leftToRight">If set to <c>true</c> left to right.</param> private static int getStartColumn(DetectionResult detectionResult, int barcodeColumn, int imageRow, bool leftToRight) { int offset = leftToRight ? 1 : -1; Codeword codeword = null; if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { codeword = detectionResult.DetectionResultColumns[barcodeColumn - offset].getCodeword(imageRow); } if (codeword != null) { return leftToRight ? codeword.EndX : codeword.StartX; } codeword = detectionResult.DetectionResultColumns[barcodeColumn].getCodewordNearby(imageRow); if (codeword != null) { return leftToRight ? codeword.StartX : codeword.EndX; } if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { codeword = detectionResult.DetectionResultColumns[barcodeColumn - offset].getCodewordNearby(imageRow); } if (codeword != null) { return leftToRight ? codeword.EndX : codeword.StartX; } int skippedColumns = 0; while (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { barcodeColumn -= offset; foreach (Codeword previousRowCodeword in detectionResult.DetectionResultColumns[barcodeColumn].Codewords) { if (previousRowCodeword != null) { return (leftToRight ? previousRowCodeword.EndX : previousRowCodeword.StartX) + offset* skippedColumns* (previousRowCodeword.EndX - previousRowCodeword.StartX); } } skippedColumns++; } return leftToRight ? detectionResult.Box.MinX : detectionResult.Box.MaxX; }
/// <summary> /// Tests to see if the Barcode Column is Valid /// </summary> /// <returns><c>true</c>, if barcode column is valid, <c>false</c> otherwise.</returns> /// <param name="detectionResult">Detection result.</param> /// <param name="barcodeColumn">Barcode column.</param> private static bool isValidBarcodeColumn(DetectionResult detectionResult, int barcodeColumn) { return (barcodeColumn >= 0) && (barcodeColumn <= detectionResult.DetectionResultColumns.Length + 1); }
/// <summary> /// Creates the barcode matrix. /// </summary> /// <returns>The barcode matrix.</returns> /// <param name="detectionResult">Detection result.</param> private static BarcodeValue[][] createBarcodeMatrix(DetectionResult detectionResult) { // Manually setup Jagged Array in C# BarcodeValue[][] barcodeMatrix = new BarcodeValue[detectionResult.RowCount][]; for (int row = 0; row < barcodeMatrix.Length; row++) { barcodeMatrix[row] = new BarcodeValue[detectionResult.ColumnCount + 2]; for (int col = 0; col < barcodeMatrix[row].Length; col++) { barcodeMatrix[row][col] = new BarcodeValue(); } } int column = -1; foreach (DetectionResultColumn detectionResultColumn in detectionResult.getDetectionResultColumns()) { column++; if (detectionResultColumn == null) { continue; } foreach (Codeword codeword in detectionResultColumn.Codewords) { if (codeword == null || codeword.RowNumber == -1) { continue; } barcodeMatrix[codeword.RowNumber][column].setValue(codeword.Value); } } return barcodeMatrix; }
/// <summary> /// Creates the decoder result. /// </summary> /// <returns>The decoder result.</returns> /// <param name="detectionResult">Detection result.</param> private static DecoderResult createDecoderResult(DetectionResult detectionResult) { BarcodeValue[][] barcodeMatrix = createBarcodeMatrix(detectionResult); if (!adjustCodewordCount(detectionResult, barcodeMatrix)) { return null; } List<int> erasures = new List<int>(); int[] codewords = new int[detectionResult.RowCount*detectionResult.ColumnCount]; List<int[]> ambiguousIndexValuesList = new List<int[]>(); List<int> ambiguousIndexesList = new List<int>(); for (int row = 0; row < detectionResult.RowCount; row++) { for (int column = 0; column < detectionResult.ColumnCount; column++) { int[] values = barcodeMatrix[row][column + 1].getValue(); int codewordIndex = row*detectionResult.ColumnCount + column; if (values.Length == 0) { erasures.Add(codewordIndex); } else if (values.Length == 1) { codewords[codewordIndex] = values[0]; } else { ambiguousIndexesList.Add(codewordIndex); ambiguousIndexValuesList.Add(values); } } } int[][] ambiguousIndexValues = new int[ambiguousIndexValuesList.Count][]; for (int i = 0; i < ambiguousIndexValues.Length; i++) { ambiguousIndexValues[i] = ambiguousIndexValuesList[i]; } return createDecoderResultFromAmbiguousValues(detectionResult.ErrorCorrectionLevel, codewords, erasures.ToArray(), ambiguousIndexesList.ToArray(), ambiguousIndexValues); }
/// <summary> /// Adjusts the codeword count. /// </summary> /// <param name="detectionResult">Detection result.</param> /// <param name="barcodeMatrix">Barcode matrix.</param> private static bool adjustCodewordCount(DetectionResult detectionResult, BarcodeValue[][] barcodeMatrix) { int[] numberOfCodewords = barcodeMatrix[0][1].getValue(); int calculatedNumberOfCodewords = detectionResult.ColumnCount* detectionResult.RowCount - getNumberOfECCodeWords(detectionResult.ErrorCorrectionLevel); if (numberOfCodewords.Length == 0) { if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common.MAX_CODEWORDS_IN_BARCODE) { return false; } barcodeMatrix[0][1].setValue(calculatedNumberOfCodewords); } else if (numberOfCodewords[0] != calculatedNumberOfCodewords) { // The calculated one is more reliable as it is derived from the row indicator columns barcodeMatrix[0][1].setValue(calculatedNumberOfCodewords); } return true; }
/// <summary> /// Creates the barcode matrix. /// </summary> /// <returns>The barcode matrix.</returns> /// <param name="detectionResult">Detection result.</param> private static BarcodeValue[][] createBarcodeMatrix(DetectionResult detectionResult) { // Manually setup Jagged Array in C# var barcodeMatrix = new BarcodeValue[detectionResult.RowCount][]; for (int row = 0; row < barcodeMatrix.Length; row++) { barcodeMatrix[row] = new BarcodeValue[detectionResult.ColumnCount + 2]; for (int col = 0; col < barcodeMatrix[row].Length; col++) { barcodeMatrix[row][col] = new BarcodeValue(); } } int column = 0; foreach (DetectionResultColumn detectionResultColumn in detectionResult.getDetectionResultColumns()) { if (detectionResultColumn != null) { foreach (Codeword codeword in detectionResultColumn.Codewords) { if (codeword != null) { int rowNumber = codeword.RowNumber; if (rowNumber >= 0) { if (rowNumber >= barcodeMatrix.Length) { // We have more rows than the barcode metadata allows for, ignore them. continue; } barcodeMatrix[rowNumber][column].setValue(codeword.Value); } } } } column++; } return barcodeMatrix; }
/// <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> /// Tests to see if the Barcode Column is Valid /// </summary> /// <returns><c>true</c>, if barcode column is valid, <c>false</c> otherwise.</returns> /// <param name="detectionResult">Detection result.</param> /// <param name="barcodeColumn">Barcode column.</param> private static bool isValidBarcodeColumn(DetectionResult detectionResult, int barcodeColumn) { return((barcodeColumn >= 0) && (barcodeColumn < detectionResult.DetectionResultColumns.Length)); }
/// <summary> /// Creates the decoder result. /// </summary> /// <returns>The decoder result.</returns> /// <param name="detectionResult">Detection result.</param> private static DecoderResult CreateDecoderResult(DetectionResult detectionResult) { BarcodeValue[][] barcodeMatrix = CreateBarcodeMatrix(detectionResult); // System.Diagnostics.Debug.WriteLine("Before Adjustment: \n" + ToString(barcodeMatrix)); AdjustCodewordCount(detectionResult, barcodeMatrix); // System.Diagnostics.Debug.WriteLine("After Adjustment: \n" + ToString(barcodeMatrix)); List<int> erasures = new List<int>(); int[] codewords = new int[detectionResult.RowCount * detectionResult.ColumnCount]; List<int[]> ambiguousIndexValuesList = new List<int[]>(); List<int> ambiguousIndexesList = new List<int>(); for (int row = 0; row < detectionResult.RowCount; row++) { for (int column = 0; column < detectionResult.ColumnCount; column++) { int[] values = barcodeMatrix[row][column + 1].GetValue(); int codewordIndex = row * detectionResult.ColumnCount + column; if (values.Length == 0) { erasures.Add(codewordIndex); } else if (values.Length == 1) { codewords[codewordIndex] = values[0]; } else { ambiguousIndexesList.Add(codewordIndex); ambiguousIndexValuesList.Add(values); } } } int[][] ambiguousIndexValues = new int[ambiguousIndexValuesList.Count][]; for (int i = 0; i < ambiguousIndexValues.Length; i++) { ambiguousIndexValues[i] = ambiguousIndexValuesList[i]; } return CreateDecoderResultFromAmbiguousValues(detectionResult.ErrorCorrectionLevel, codewords, erasures.ToArray(), ambiguousIndexesList.ToArray(), ambiguousIndexValues); }