/// <summary> /// Initializes a new instance of the <see cref="Binarizer"/> class. /// </summary> /// <param name="source">The source.</param> protected internal Binarizer(LuminanceSource source) { if (source == null) { throw new ArgumentException("Source must be non-null."); } this.source = source; }
/// <summary> /// Initializes a new instance of the <see cref="Binarizer"/> class. /// </summary> /// <param name="source">The source.</param> protected internal HybridBinarizer(LuminanceSource source) { if (source == null) { throw new ArgumentException("Source must be non-null."); } this.source = source; }
/// <summary> /// Returns a new object with rotated image data by 45 degrees counterclockwise. /// Only callable if <see cref="RotateSupported"/> is true. /// </summary> /// <returns>A rotated version of this object.</returns> public BinaryBitmap rotateCounterClockwise45() { LuminanceSource newSource = binarizer.LuminanceSource.rotateCounterClockwise45(); return(new BinaryBitmap(binarizer.createBinarizer(newSource))); }
/// <summary> /// Tries to decode barcodes within an image which is given by a luminance source. /// That method gives a chance to prepare a luminance source completely before calling /// the time consuming decoding method. On the other hand there is a chance to create /// a luminance source which is independent from external resources (like Bitmap objects) /// and the decoding call can be made in a background thread. /// </summary> /// <param name="luminanceSource">The luminance source.</param> /// <returns></returns> public virtual Result[] DecodeMultiple(LuminanceSource luminanceSource) { var results = default(Result[]); var binarizer = CreateBinarizer(luminanceSource); var binaryBitmap = new BinaryBitmap(binarizer); var rotationCount = 0; var rotationMaxCount = 1; MultipleBarcodeReader multiReader = null; if (AutoRotate) { Options.Hints[DecodeHintType.TRY_HARDER_WITHOUT_ROTATION] = true; rotationMaxCount = 4; } var formats = Options.PossibleFormats; if (formats != null && formats.Count == 1 && formats.Contains(BarcodeFormat.QR_CODE)) { multiReader = new QRCodeMultiReader(); } else { multiReader = new GenericMultipleBarcodeReader(Reader); } for (; rotationCount < rotationMaxCount; rotationCount++) { results = multiReader.decodeMultiple(binaryBitmap, Options.Hints); if (results == null) { if (TryInverted && luminanceSource.InversionSupported) { binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.invert())); results = multiReader.decodeMultiple(binaryBitmap, Options.Hints); } } if (results != null || !luminanceSource.RotateSupported || !AutoRotate) break; binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.rotateCounterClockwise())); } if (results != null) { foreach (var result in results) { if (result.ResultMetadata == null) { result.putMetadata(ResultMetadataType.ORIENTATION, rotationCount * 90); } else if (!result.ResultMetadata.ContainsKey(ResultMetadataType.ORIENTATION)) { result.ResultMetadata[ResultMetadataType.ORIENTATION] = rotationCount * 90; } else { // perhaps the core decoder rotates the image already (can happen if TryHarder is specified) result.ResultMetadata[ResultMetadataType.ORIENTATION] = ((int)(result.ResultMetadata[ResultMetadataType.ORIENTATION]) + rotationCount * 90) % 360; } } OnResultsFound(results); } return results; }
/// <summary> /// Tries to decode a barcode within an image which is given by a luminance source. /// That method gives a chance to prepare a luminance source completely before calling /// the time consuming decoding method. On the other hand there is a chance to create /// a luminance source which is independent from external resources (like Bitmap objects) /// and the decoding call can be made in a background thread. /// </summary> /// <param name="luminanceSource">The luminance source.</param> /// <returns></returns> public virtual Result Decode(LuminanceSource luminanceSource) { var result = default(Result); var binarizer = CreateBinarizer(luminanceSource); var binaryBitmap = new BinaryBitmap(binarizer); var multiformatReader = Reader as MultiFormatReader; var rotationCount = 0; var rotationMaxCount = 1; if (AutoRotate) { Options.Hints[DecodeHintType.TRY_HARDER_WITHOUT_ROTATION] = true; rotationMaxCount = 4; } else { if (Options.Hints.ContainsKey(DecodeHintType.TRY_HARDER_WITHOUT_ROTATION)) Options.Hints.Remove(DecodeHintType.TRY_HARDER_WITHOUT_ROTATION); } for (; rotationCount < rotationMaxCount; rotationCount++) { if (usePreviousState && multiformatReader != null) { result = multiformatReader.decodeWithState(binaryBitmap); } else { result = Reader.decode(binaryBitmap, Options.Hints); usePreviousState = true; } if (result == null) { if (TryInverted && luminanceSource.InversionSupported) { binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.invert())); if (usePreviousState && multiformatReader != null) { result = multiformatReader.decodeWithState(binaryBitmap); } else { result = Reader.decode(binaryBitmap, Options.Hints); usePreviousState = true; } } } if (result != null || !luminanceSource.RotateSupported || !AutoRotate) break; luminanceSource = luminanceSource.rotateCounterClockwise(); binarizer = CreateBinarizer(luminanceSource); binaryBitmap = new BinaryBitmap(binarizer); } if (result != null) { if (result.ResultMetadata == null) { result.putMetadata(ResultMetadataType.ORIENTATION, rotationCount * 90); } else if (!result.ResultMetadata.ContainsKey(ResultMetadataType.ORIENTATION)) { result.ResultMetadata[ResultMetadataType.ORIENTATION] = rotationCount * 90; } else { // perhaps the core decoder rotates the image already (can happen if TryHarder is specified) result.ResultMetadata[ResultMetadataType.ORIENTATION] = ((int)(result.ResultMetadata[ResultMetadataType.ORIENTATION]) + rotationCount * 90) % 360; } OnResultFound(result); } return result; }
/// <summary> Creates a new object with the same type as this Binarizer implementation, but with pristine /// state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache /// of 1 bit data. See Effective Java for why we can't use Java's clone() method. /// </summary> /// <param name="source">The LuminanceSource this Binarizer will operate on.</param> /// <returns> A new concrete Binarizer implementation object.</returns> public abstract Binarizer createBinarizer(LuminanceSource source);
/// <summary> /// Tries to decode a barcode within an image which is given by a luminance source. /// That method gives a chance to prepare a luminance source completely before calling /// the time consuming decoding method. On the other hand there is a chance to create /// a luminance source which is independent from external resources (like Bitmap objects) /// and the decoding call can be made in a background thread. /// </summary> /// <param name="luminanceSource">The luminance source.</param> /// <returns></returns> private Result Decode(LuminanceSource luminanceSource) { var result = default(Result); var binarizer = CreateBinarizer(luminanceSource); var binaryBitmap = new BinaryBitmap(binarizer); var multiformatReader = Reader as MultiFormatReader; var rotationCount = 0; var rotationMaxCount = 1; if (AutoRotate) { Options.Hints[DecodeHintType.TRY_HARDER_WITHOUT_ROTATION] = true; rotationMaxCount = 4; } else { if (Options.Hints.ContainsKey(DecodeHintType.TRY_HARDER_WITHOUT_ROTATION)) Options.Hints.Remove(DecodeHintType.TRY_HARDER_WITHOUT_ROTATION); } for (; rotationCount < rotationMaxCount; rotationCount++) { if (usePreviousState && multiformatReader != null) { result = multiformatReader.decodeWithState(binaryBitmap); } else { result = Reader.decode(binaryBitmap, Options.Hints); usePreviousState = true; } if (result == null) { if (TryInverted && luminanceSource.InversionSupported) { binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.invert())); if (usePreviousState && multiformatReader != null) { result = multiformatReader.decodeWithState(binaryBitmap); } else { result = Reader.decode(binaryBitmap, Options.Hints); usePreviousState = true; } } } if (result != null || !luminanceSource.RotateSupported || !AutoRotate) break; binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.rotateCounterClockwise())); } if (result != null) { if (result.ResultMetadata == null) { result.putMetadata(ResultMetadataType.ORIENTATION, rotationCount * 90); } else if (!result.ResultMetadata.ContainsKey(ResultMetadataType.ORIENTATION)) { result.ResultMetadata[ResultMetadataType.ORIENTATION] = rotationCount * 90; } else { // perhaps the core decoder rotates the image already (can happen if TryHarder is specified) result.ResultMetadata[ResultMetadataType.ORIENTATION] = ((int)(result.ResultMetadata[ResultMetadataType.ORIENTATION]) + rotationCount * 90) % 360; } OnResultFound(result); } return result; }
/** * Writes out a single PNG which is three times the width of the input image, containing from left * to right: the original image, the row sampling monochrome version, and the 2D sampling * monochrome version. */ private static void dumpBlackPoint(Uri uri, Bitmap image, BinaryBitmap bitmap, LuminanceSource luminanceSource) { // TODO: Update to compare different Binarizer implementations. String inputName = uri.LocalPath; if (inputName.Contains(".mono.png")) { return; } // Use the current working directory for URLs String resultName = inputName; int pos; if ("http".Equals(uri.Scheme)) { pos = resultName.LastIndexOf('/'); if (pos > 0) { resultName = '.' + resultName.Substring(pos); } } pos = resultName.LastIndexOf('.'); if (pos > 0) { resultName = resultName.Substring(0, pos); } resultName += ".mono.png"; int width = bitmap.Width; int height = bitmap.Height; int stride = width * 4; var result = new Bitmap(stride, height, PixelFormat.Format32bppArgb); var offset = 0; // The original image for (int indexH = 0; indexH < height; indexH++) { for (int indexW = 0; indexW < width; indexW++) { result.SetPixel(indexW, indexH, image.GetPixel(indexW, indexH)); } } // Row sampling BitArray row = new BitArray(width); offset += width; for (int y = 0; y < height; y++) { row = bitmap.getBlackRow(y, row); if (row == null) { // If fetching the row failed, draw a red line and keep going. for (int x = 0; x < width; x++) { result.SetPixel(offset + x, y, Color.Red); } continue; } for (int x = 0; x < width; x++) { result.SetPixel(offset + x, y, row[x] ? Color.Black : Color.White); } } // 2D sampling offset += width; BitMatrix matrix = bitmap.BlackMatrix; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { result.SetPixel(offset + x, y, matrix[x, y] ? Color.Black : Color.White); } } offset += width; var luminanceMatrix = luminanceSource.Matrix; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { result.SetPixel(offset + x, y, Color.FromArgb(luminanceMatrix[y * width + x], luminanceMatrix[y * width + x], luminanceMatrix[y * width + x])); } } result.Save(resultName, ImageFormat.Png); }
/// <summary> /// Tries to decode barcodes within an image which is given by a luminance source. /// That method gives a chance to prepare a luminance source completely before calling /// the time consuming decoding method. On the other hand there is a chance to create /// a luminance source which is independent from external resources (like Bitmap objects) /// and the decoding call can be made in a background thread. /// </summary> /// <param name="luminanceSource">The luminance source.</param> /// <returns></returns> private Result[] DecodeMultiple(LuminanceSource luminanceSource) { var results = default(Result[]); var binarizer = CreateBinarizer(luminanceSource); var binaryBitmap = new BinaryBitmap(binarizer); var rotationCount = 0; var rotationMaxCount = 1; MultipleBarcodeReader multiReader = null; if (AutoRotate) { Options.Hints[DecodeHintType.TRY_HARDER_WITHOUT_ROTATION] = true; rotationMaxCount = 4; } var formats = Options.PossibleFormats; if (formats != null && formats.Length == 1 && formats[0] == BarcodeFormat.QR_CODE) { multiReader = new QRCodeMultiReader(); } else { multiReader = new GenericMultipleBarcodeReader(Reader); } for (; rotationCount < rotationMaxCount; rotationCount++) { results = multiReader.decodeMultiple(binaryBitmap, Options.Hints); if (results == null) { if (TryInverted && luminanceSource.InversionSupported) { binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.invert())); results = multiReader.decodeMultiple(binaryBitmap, Options.Hints); } } if (results != null || !luminanceSource.RotateSupported || !AutoRotate) break; binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.rotateCounterClockwise())); } if (results != null) { foreach (var result in results) { if (result.ResultMetadata == null) { result.putMetadata(ResultMetadataType.ORIENTATION, rotationCount * 90); } else if (!result.ResultMetadata.ContainsKey(ResultMetadataType.ORIENTATION)) { result.ResultMetadata[ResultMetadataType.ORIENTATION] = rotationCount * 90; } else { // perhaps the core decoder rotates the image already (can happen if TryHarder is specified) result.ResultMetadata[ResultMetadataType.ORIENTATION] = ((int)(result.ResultMetadata[ResultMetadataType.ORIENTATION]) + rotationCount * 90) % 360; } } OnResultsFound(results); } return results; }