/// <summary> /// Computes the y dimension (number of modules in a column) of the PDF417 Code /// based on vertices of the codeword area and estimated module size. /// </summary> /// <param name="topLeft"> of codeword area </param> /// <param name="topRight"> of codeword area </param> /// <param name="bottomLeft"> of codeword area </param> /// <param name="bottomRight"> of codeword are </param> /// <param name="moduleWidth"> estimated module size </param> /// <returns> the number of modules in a row. </returns> private static int computeYDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint bottomRight, float moduleWidth) { int leftColumnDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleWidth); int rightColumnDimension = MathUtils.round(ResultPoint.distance(topRight, bottomRight) / moduleWidth); return((leftColumnDimension + rightColumnDimension) >> 1); }
/// <summary> /// Computes the dimension (number of modules in a row) of the PDF417 Code /// based on vertices of the codeword area and estimated module size. /// </summary> /// <param name="topLeft"> of codeword area </param> /// <param name="topRight"> of codeword area </param> /// <param name="bottomLeft"> of codeword area </param> /// <param name="bottomRight"> of codeword are </param> /// <param name="moduleWidth"> estimated module size </param> /// <returns> the number of modules in a row. </returns> private static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, ResultPoint bottomRight, float moduleWidth) { int topRowDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleWidth); int bottomRowDimension = MathUtils.round(ResultPoint.distance(bottomLeft, bottomRight) / moduleWidth); return(((((topRowDimension + bottomRowDimension) >> 1) + 8) / 17) * 17); }
/// <summary> /// <p>Estimates module size (pixels in a module) based on the Start and End /// finder patterns.</p> /// /// <param name="vertices">an array of vertices:</param> /// vertices[0] x, y top left barcode /// vertices[1] x, y bottom left barcode /// vertices[2] x, y top right barcode /// vertices[3] x, y bottom right barcode /// vertices[4] x, y top left codeword area /// vertices[5] x, y bottom left codeword area /// vertices[6] x, y top right codeword area /// vertices[7] x, y bottom right codeword area /// <returns>the module size.</returns> /// </summary> private static float computeModuleWidth(ResultPoint[] vertices) { float pixels1 = ResultPoint.distance(vertices[0], vertices[4]); float pixels2 = ResultPoint.distance(vertices[1], vertices[5]); float moduleWidth1 = (pixels1 + pixels2) / (17 * 2.0f); float pixels3 = ResultPoint.distance(vertices[6], vertices[2]); float pixels4 = ResultPoint.distance(vertices[7], vertices[3]); float moduleWidth2 = (pixels3 + pixels4) / (18 * 2.0f); return((moduleWidth1 + moduleWidth2) / 2.0f); }
/// <summary> /// <p>Computes the dimension (number of modules on a size) of the QR Code based on the position /// of the finder patterns and estimated module size.</p> /// </summary> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private static int computeDimension(com.google.zxing.ResultPoint topLeft, com.google.zxing.ResultPoint topRight, com.google.zxing.ResultPoint bottomLeft, float moduleSize) throws com.google.zxing.NotFoundException private static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize) { int tltrCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleSize); int tlblCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; switch (dimension & 0x03) // mod 4 { case 0: dimension++; break; // 1? do nothing case 2: dimension--; break; case 3: throw NotFoundException.NotFoundInstance; } return(dimension); }
private static ResultPoint[] GetBarCodeRect(Point[] scanLine, ResultPoint[] whiterect, float dx) { ResultPoint pt0 = new ResultPoint(scanLine[0].X, scanLine[0].Y); // Начало линии сканирования (заканчивается за 3 штрихполоски до начала штрихкода) ResultPoint pt1 = new ResultPoint(scanLine[1].X, scanLine[1].Y); // Конец линии сканирования (заканчивается за 3 штрихполоски до конца штрихкода) // whiterect п/у ограничивающий одну или несколько полосок штрих кода, // сделаем его направленным, повернем вдоль скан линии, чтобы можно было ширину расширить whiterect = RotateRect(whiterect, scanLine); // Проекция точки x,y на найденную полоску штрихкода. ResultPoint proj = Projection(pt1, whiterect[1], whiterect[0]); float vLen = ResultPoint.distance(pt1, proj); ResultPoint v = new ResultPoint(pt1.X - proj.X, pt1.Y - proj.Y); // Вектор b перпендикулярный вектору a против часовой стрелки равен b = (-ay, ax). // Найдем правую нормаль к вертикальной полоске float height = ResultPoint.distance(whiterect[1], whiterect[0]); ResultPoint vOrto = new ResultPoint(-(whiterect[1].Y - whiterect[0].Y) / height, (whiterect[1].X - whiterect[0].X) / height); var lastBarCodeLine = new ResultPoint[2] { new ResultPoint(whiterect[0].X + v.X - vOrto.X * dx, whiterect[0].Y + v.Y - vOrto.Y * dx), new ResultPoint(whiterect[1].X + v.X - vOrto.X * dx, whiterect[1].Y + v.Y - vOrto.Y * dx) }; ResultPoint proj0 = Projection(pt0, whiterect[1], whiterect[0]); ResultPoint v0 = new ResultPoint(pt0.X - proj0.X, pt0.Y - proj0.Y); var firstBarCodeLine = new ResultPoint[2] { new ResultPoint(whiterect[0].X + v0.X + vOrto.X * dx, whiterect[0].Y + v0.Y + vOrto.Y * dx), new ResultPoint(whiterect[1].X + v0.X + vOrto.X * dx, whiterect[1].Y + v0.Y + vOrto.Y * dx) }; return(new ResultPoint[] { firstBarCodeLine[0], firstBarCodeLine[1], lastBarCodeLine[0], lastBarCodeLine[1] }); }
/// <summary> <p>Computes the dimension (number of modules on a size) of the QR Code based on the position /// of the finder patterns and estimated module size.</p> /// </summary> protected internal static int computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize) { int tltrCentersDimension = round(ResultPoint.distance(topLeft, topRight) / moduleSize); int tlblCentersDimension = round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; switch (dimension & 0x03) { // mod 4 case 0: dimension++; break; // 1? do nothing case 2: dimension--; break; case 3: throw ReaderException.Instance; } return(dimension); }
/// <summary> <p>Computes the dimension (number of modules on a size) of the QR Code based on the position /// of the finder patterns and estimated module size.</p> /// </summary> private static bool computeDimension(ResultPoint topLeft, ResultPoint topRight, ResultPoint bottomLeft, float moduleSize, out int dimension) { int tltrCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleSize); int tlblCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; switch (dimension & 0x03) { // mod 4 case 0: dimension++; break; // 1? do nothing case 2: dimension--; break; case 3: return(true); } return(true); }
/// <summary> /// </summary> /// <returns>the 3 best <see cref="FinderPattern" />s from our list of candidates. The "best" are /// those that have been detected at least CENTER_QUORUM times, and whose module /// size differs from the average among those patterns the least /// </returns> private FinderPattern[][] selectMultipleBestPatterns() { List <FinderPattern> possibleCenters = PossibleCenters; int size = possibleCenters.Count; if (size < 3) { // Couldn't find enough finder patterns return(null); } /* * Begin HE modifications to safely detect multiple codes of equal size */ if (size == 3) { return(new FinderPattern[][] { new FinderPattern[] { possibleCenters[0], possibleCenters[1], possibleCenters[2] } }); } // Sort by estimated module size to speed up the upcoming checks possibleCenters.Sort(new ModuleSizeComparator()); /* * Now lets start: build a list of tuples of three finder locations that * - feature similar module sizes * - are placed in a distance so the estimated module count is within the QR specification * - have similar distance between upper left/right and left top/bottom finder patterns * - form a triangle with 90° angle (checked by comparing top right/bottom left distance * with pythagoras) * * Note: we allow each point to be used for more than one code region: this might seem * counterintuitive at first, but the performance penalty is not that big. At this point, * we cannot make a good quality decision whether the three finders actually represent * a QR code, or are just by chance layouted so it looks like there might be a QR code there. * So, if the layout seems right, lets have the decoder try to decode. */ List <FinderPattern[]> results = new List <FinderPattern[]>(); // holder for the results for (int i1 = 0; i1 < (size - 2); i1++) { FinderPattern p1 = possibleCenters[i1]; if (p1 == null) { continue; } for (int i2 = i1 + 1; i2 < (size - 1); i2++) { FinderPattern p2 = possibleCenters[i2]; if (p2 == null) { continue; } // Compare the expected module sizes; if they are really off, skip float vModSize12 = (p1.EstimatedModuleSize - p2.EstimatedModuleSize) / Math.Min(p1.EstimatedModuleSize, p2.EstimatedModuleSize); float vModSize12A = Math.Abs(p1.EstimatedModuleSize - p2.EstimatedModuleSize); if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) { // break, since elements are ordered by the module size deviation there cannot be // any more interesting elements for the given p1. break; } for (int i3 = i2 + 1; i3 < size; i3++) { FinderPattern p3 = possibleCenters[i3]; if (p3 == null) { continue; } // Compare the expected module sizes; if they are really off, skip float vModSize23 = (p2.EstimatedModuleSize - p3.EstimatedModuleSize) / Math.Min(p2.EstimatedModuleSize, p3.EstimatedModuleSize); float vModSize23A = Math.Abs(p2.EstimatedModuleSize - p3.EstimatedModuleSize); if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) { // break, since elements are ordered by the module size deviation there cannot be // any more interesting elements for the given p1. break; } FinderPattern[] test = { p1, p2, p3 }; ResultPoint.orderBestPatterns(test); // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal FinderPatternInfo info = new FinderPatternInfo(test); float dA = ResultPoint.distance(info.TopLeft, info.BottomLeft); float dC = ResultPoint.distance(info.TopRight, info.BottomLeft); float dB = ResultPoint.distance(info.TopLeft, info.TopRight); // Check the sizes float estimatedModuleCount = (dA + dB) / (p1.EstimatedModuleSize * 2.0f); if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) { continue; } // Calculate the difference of the edge lengths in percent float vABBC = Math.Abs((dA - dB) / Math.Min(dA, dB)); if (vABBC >= 0.1f) { continue; } // Calculate the diagonal length by assuming a 90° angle at topleft float dCpy = (float)Math.Sqrt((double)dA * dA + (double)dB * dB); // Compare to the real distance in % float vPyC = Math.Abs((dC - dCpy) / Math.Min(dC, dCpy)); if (vPyC >= 0.1f) { continue; } // All tests passed! results.Add(test); } // end iterate p3 } // end iterate p2 } // end iterate p1 if (results.Count != 0) { return(results.ToArray()); } // Nothing found! return(null); }
// L2 distance private static int distance(ResultPoint a, ResultPoint b) { return(MathUtils.round(ResultPoint.distance(a, b))); }
/// <summary> /// Найти штрихкод code128 и его ограничивающий п/у /// </summary> /// <param name="fileName">Имя файла с картинкой png, jpeg</param> public static BarcodeResult ScanCode128(Image img, string debugFileName = null) { BarcodeResult barcodeResult = null; Bitmap bmp = new Bitmap(img); // create a barcode reader instance var reader = new BarcodeReader(); reader.Options.ReturnCodabarStartEnd = true; reader.Options.TryHarder = true; reader.Options.PossibleFormats = new BarcodeFormat[] { BarcodeFormat.CODE_128 }; reader.AutoRotate = true; // Сейчас код ищет только один штрихкод. // Найти несколько штрихкодов можно с помощью метода DecodeMultiple, но по умолчанию возвращаются только разные штрихкоды. // Если мы хотим получить положение одного и того же штрихкода вставленного несколько раз, то нужно будет переписывать метод DecodeMultiple. // Есть ещё проблема с разноповернутыми штрихкодами если их несколько, то распознаются только однонаправленные, // возможно стоит вручную поворачивать и перезапускать алгоритм. // int scannerOrientation = 0; var result = reader.Decode(bmp); if (result != null) { barcodeResult = new BarcodeResult(); barcodeResult.Code = result.Text; barcodeResult.Type = BarcodeType.ToString(result.BarcodeFormat); try { var orient = result.ResultMetadata[ResultMetadataType.ORIENTATION]; scannerOrientation = (int)orient; // only vertical/horizontal } catch (Exception /*ex*/) { } if (result.ResultPoints.Length >= 2) { // var luminanceSource = new ZXing.BitmapLuminanceSource(bmp); var binarizer = new ZXing.Common.HybridBinarizer(luminanceSource); var bitMatrix = binarizer.BlackMatrix; // result.ResultPoints - точкии линии сканера, для которой был распознан штрих код // для barcode128 эти точки находятся внутри штрихкода видимо за start и stop элементами. // нам нужно получить по этим точкам ограничивающий п/у для всего штрихкода // но это точки в координатах повернутого листа на scannerOrientation градусов, // поэтому преобразуем их в начальную и конечную координату сканера в координатак картинки img Point[] scanLine = GetScanLine(result.ResultPoints, scannerOrientation, img.Width, img.Height, bitMatrix); // Возьмем конечную точку скан линии int x = scanLine[1].X; int y = scanLine[1].Y; // Вычислим магический белый квадрат - предположительное место штрихкода. // Искать будет с точки x,y, возвращает 4 точки, типа ограничивающего штрих код белую п/у. // Использующийся алгоритм не до конца понятен, возвращает п/у содержащий одну, несколько полосок, или весь штрих код. // Поэтому единственная полезная информация которая может быть извлечена высота штрихкода (координаты одной вертикальной полосы) var whiterectDetector = ZXing.Common.Detector.WhiteRectangleDetector.Create(bitMatrix, 1, x, y); var whiteRect = whiterectDetector.detect(); if (whiteRect == null) { return(barcodeResult); // не удалось вычислить ограничивающий п/у. } // Посчитаем длину первой черной полоски после правой конечной точки scan линии. int blackLineWidth = CalcBlackLineWidth(bitMatrix, scanLine); // Вычислим по имеющимся данным ограничивающий п/у для штрихкода int dx = blackLineWidth * 3; // 3 полоски по бокам добавим (для 128 баркода) ResultPoint[] barcodeRect = GetBarCodeRect(scanLine, whiteRect, dx); // Вычислим п/у для создания штрихкода и его ориентацию(поворот). // (п/у без поворота, от левой нижней точки и его угол поворота в градусах) //barcodeResult.Left = (int)barcodeRect[1].X; //barcodeResult.Bottom = (int)barcodeRect[1].Y; //barcodeResult.Right = barcodeResult.Left + (int)ResultPoint.distance(barcodeRect[0], barcodeRect[2]); //barcodeResult.Top = barcodeResult.Bottom - (int)ResultPoint.distance(barcodeRect[0], barcodeRect[1]); barcodeResult.Left = (int)barcodeRect[0].X; barcodeResult.Top = (int)barcodeRect[0].Y; barcodeResult.Right = barcodeResult.Left + (int)ResultPoint.distance(barcodeRect[0], barcodeRect[2]); barcodeResult.Bottom = barcodeResult.Top + (int)ResultPoint.distance(barcodeRect[0], barcodeRect[1]); /* } * else if (scannerOrientation == 180) * { * barcodeResult.Left = (int)barcodeRect[0].X; * barcodeResult.Bottom = (int)barcodeRect[0].Y; * barcodeResult.Right = barcodeResult.Left + (int)ResultPoint.distance(barcodeRect[0], barcodeRect[2]); * barcodeResult.Top = barcodeResult.Bottom - (int)ResultPoint.distance(barcodeRect[0], barcodeRect[1]); * }*/ //barcodeResult.Orientation = -Orientation(barcodeRect[0], barcodeRect[2]); barcodeResult.Orientation = Orientation(barcodeRect[0], barcodeRect[2]); if (!string.IsNullOrEmpty(debugFileName)) { // Закрасим область белым, чтобы можно было ещё искать barcode var g = Graphics.FromImage(img); g.FillPolygon(new SolidBrush(Color.Pink), ToDrawingRect(barcodeRect)); DebugSaveImage(debugFileName, img, barcodeRect, whiteRect, scanLine); } } } return(barcodeResult); }