private static DataRectangle <double> Blur(DataRectangle <double> values, int maxRadius) { return(values.Transform((value, point) => { var valuesInArea = new List <double>(); for (var x = -maxRadius; x <= maxRadius; x++) { for (var y = -maxRadius; y <= maxRadius; y++) { var newPoint = new Point(point.X + x, point.Y + y); if ((newPoint.X < 0) || (newPoint.Y < 0) || (newPoint.X >= values.Width) || (newPoint.Y >= values.Height)) { continue; } valuesInArea.Add(values[newPoint.X, newPoint.Y]); } } return valuesInArea.Average(); })); }
private static IEnumerable <int> GetBarLengthsFromBarcodeSlice(DataRectangle <bool> barcodeDetails, int sliceY) { if ((sliceY < 0) || (sliceY >= barcodeDetails.Height)) { throw new ArgumentOutOfRangeException(nameof(sliceY)); } // Take the horizontal slice of the data var values = new List <bool>(); for (var x = 0; x < barcodeDetails.Width; x++) { values.Add(barcodeDetails[x, sliceY]); } // Split the slice into bars - we only care about how long each segment is when they // alternate, not whether they're dark bars or light bars var segments = new List <Tuple <bool, int> >(); foreach (var value in values) { if ((segments.Count == 0) || (segments[^ 1].Item1 != value))
/// <summary> /// This will return values in the range 0-255 (inclusive) /// </summary> // Based on http://stackoverflow.com/a/4748383/3813189 public static DataRectangle <double> GetGreyscale(this Bitmap image) { var values = new double[image.Width, image.Height]; var data = image.LockBits( new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb ); try { var pixelData = new Byte[data.Stride]; for (var lineIndex = 0; lineIndex < data.Height; lineIndex++) { Marshal.Copy( source: data.Scan0 + (lineIndex * data.Stride), destination: pixelData, startIndex: 0, length: data.Stride ); for (var pixelOffset = 0; pixelOffset < data.Width; pixelOffset++) { // Note: PixelFormat.Format24bppRgb means the data is stored in memory as BGR const int PixelWidth = 3; var r = pixelData[pixelOffset * PixelWidth + 2]; var g = pixelData[pixelOffset * PixelWidth + 1]; var b = pixelData[pixelOffset * PixelWidth]; values[pixelOffset, lineIndex] = (0.2989 * r) + (0.5870 * g) + (0.1140 * b); } } } finally { image.UnlockBits(data); } return(DataRectangle.For(values)); }
private static string?TryToReadBarcodeValueFromSingleLine(DataRectangle <bool> barcodeDetails, int sliceY) { if ((sliceY < 0) || (sliceY >= barcodeDetails.Height)) { throw new ArgumentOutOfRangeException(nameof(sliceY)); } var lengths = GetBarLengthsFromBarcodeSlice(barcodeDetails, sliceY).ToArray(); if (lengths.Length < 57) { // As explained, we'd like 60 bars (which would include the final guard region) but we // can still make an attempt with 57 (but no fewer) // - There will often be another section of blank content after the barcode that we ignore // - If we don't want to validate the final guard region then we can work with a barcode // image where some of the end is cut off, so long as the data for the 12 digits is // there (this will be the case where there are only 57 lengths) return(null); } var offset = 0; var extractedNumericValues = new List <int>(); for (var i = 0; i < 14; i++) { if (i == 0) { // This should be the first guard region and it should be a pattern of three single- // width bars offset += 3; } else if (i == 7) { // This should be the guard region in the middle of the barcode and it should be a // pattern of five single-width bars offset += 5; } else { var value = TryToGetValueForLengths( lengths[offset], lengths[offset + 1], lengths[offset + 2], lengths[offset + 3] ); if (value is null) { return(null); } extractedNumericValues.Add(value.Value); offset += 4; } } // Calculate what the checksum should be based upon the first 11 numbers and ensure that // the 12th matches it if (extractedNumericValues.Last() != CalculateChecksum(extractedNumericValues.Take(11))) { return(null); } return(string.Join("", extractedNumericValues)); }