Пример #1
0
        private static List <Line> RecognizeLines(Bitmap bw, RecognitionOptions options)
        {
            List <Point>   edgePoints = EdgePointExtraction.ExtractEdgePoints(bw);
            List <RawLine> rawLines   = PseudoHoughTransform.RecognizeLines(edgePoints, options);

            return(LineFilter.FilterLines(edgePoints, rawLines, options));
        }
Пример #2
0
        /*
         * Convenience function to recognize set of lines in points.
         */
        public static List <RawLine> RecognizeLines(List <Point> points, RecognitionOptions options)
        {
            int[,] hough = HoughTransform(points, options);
            List <Point> houghPeaks = FindHoughPeaks(hough, options);

            return(ExtractRawLines(houghPeaks, options));
        }
Пример #3
0
        /*
         * Convenience function to recognize table in an image.
         */
        public static Option <Table> RecognizeTable(Bitmap sourceImage, List <double> columnWidthsHintOrNull = null)
        {
            Bitmap bw    = ImageUtil.ToBlackAndWhite(sourceImage);
            Bitmap rotBw = ImageUtil.RotateCounterClockwise(bw);

            var horizOptions = RecognitionOptions.HorizontalOptions();

            horizOptions.imageWidth  = bw.Width;
            horizOptions.imageHeight = bw.Height;

            var vertOptions = RecognitionOptions.VerticalOptions();

            vertOptions.imageWidth  = rotBw.Width;
            vertOptions.imageHeight = rotBw.Height;

            List <Line> horizLines = RecognizeLines(bw, horizOptions);
            List <Line> vertLines  = RecognizeLines(rotBw, vertOptions);

            if (horizLines.Count == 0 || vertLines.Count == 0)
            {
                return(new None <Table>());
            }
            else
            {
                var lnorm = new LineNormalization(horizLines, vertLines, sourceImage);
                return(TableBuilder.NewBuilder(lnorm, Options.Create(columnWidthsHintOrNull)).table);
            }
        }
Пример #4
0
        public static RecognitionOptions CommonOptions()
        {
            var options = new RecognitionOptions();

            options.maxAngleFactor    = 0.03f;
            options.houghWindowWidth  = 20;
            options.houghWindowHeight = 10;
            return(options);
        }
Пример #5
0
        /*
         * Locate peaks in A/DO space array.
         * For each point, searches some narrow window of points around it,
         * and if there is no point bigger than current point, records it as a peak.
         * If there are several points with same value, records their average as a peak.
         */
        public static List <Point> FindHoughPeaks(int[,] hough, RecognitionOptions options)
        {
            int width  = hough.GetLength(0);
            int height = hough.GetLength(1);

            int maxHough = 0;

            foreach (var h in hough)
            {
                if (h > maxHough)
                {
                    maxHough = h;
                }
            }

            List <Point> thresholdedPoints = new List <Point>();

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if ((double)hough[x, y] / maxHough > options.houghThreshold)
                    {
                        thresholdedPoints.Add(new Point(x, y));
                    }
                }
            }

            List <Point>    peaks      = new List <Point>();
            HashSet <Point> usedPoints = new HashSet <Point>();

            foreach (Point pt in thresholdedPoints)
            {
                if (!usedPoints.Contains(pt))
                {
                    List <Point> adjPoints =
                        thresholdedPoints
                        .Where(p =>
                               Math.Abs(p.X - pt.X) < options.houghWindowWidth &&
                               Math.Abs(p.Y - pt.Y) < options.houghWindowHeight).ToList();

                    int maxAdj = adjPoints.Select(p => hough[p.X, p.Y]).Max();
                    if (hough[pt.X, pt.Y] == maxAdj)
                    {
                        List <Point> adjPeaks = adjPoints.Where(p => hough[p.X, p.Y] == maxAdj).ToList();
                        int          avgX     = (int)Math.Round(adjPeaks.Select(p => p.X).Average());
                        int          avgY     = (int)Math.Round(adjPeaks.Select(p => p.Y).Average());
                        peaks.Add(new Point(avgX, avgY));
                        foreach (var adj in adjPoints)
                        {
                            usedPoints.Add(adj);
                        }
                    }
                }
            }
            return(peaks);
        }
Пример #6
0
        /*
         * Get array of angles that we want to test.
         * Creates array of angles, that differ by 1px at distance of image width.
         */
        private static double[] GetAngleMap(RecognitionOptions options)
        {
            int maxDy = (int)Math.Ceiling(options.imageWidth * options.maxAngleFactor);

            double[] angleMap = new double[maxDy * 2 + 1];
            for (int dy = -maxDy; dy <= maxDy; dy++)
            {
                angleMap[maxDy + dy] = (double)dy / (double)options.imageWidth;
            }
            return(angleMap);
        }
Пример #7
0
        /*
         * Extract raw lines from peaks in A/DO space.
         */
        public static List <RawLine> ExtractRawLines(List <Point> houghPeaks, RecognitionOptions options)
        {
            double[]       angleMap = GetAngleMap(options);
            List <RawLine> lines    = new List <RawLine>();

            foreach (var pt in houghPeaks)
            {
                lines.Add(new RawLine {
                    yInt = pt.Y, k = angleMap[pt.X]
                });
            }
            return(lines);
        }
Пример #8
0
        /*
         * Main function, that performs Pseudo-Hough transform as described above.
         * Returns A/DO space array.
         */
        public static int[,] HoughTransform(List <Point> points, RecognitionOptions options)
        {
            double[] angleMap = GetAngleMap(options);
            int[,] hough = new int[angleMap.Length, options.imageHeight];

            for (int a = 0; a < angleMap.Length; a++)
            {
                foreach (Point pt in points)
                {
                    int y = (int)Math.Round(pt.Y - pt.X * angleMap[a]);
                    if (y >= 0 && y < options.imageHeight)
                    {
                        hough[a, y]++;
                    }
                }
            }

            return(hough);
        }
Пример #9
0
        public static bool HasCyclicPatterns(bool[] linePoints, int from, int to, RecognitionOptions options)
        {
            for (int w = options.cyclicPatternsMinWidth; w <= options.cyclicPatternsMaxWidth; w++)
            {
                int[] acc = new int[w];
                for (int i = from; i <= to; i++)
                {
                    if (linePoints[i])
                    {
                        acc[(i - from) % w]++;
                    }
                }

                int    threshold         = (to - from) / w / 5;
                double cyclicPatternSize = (double)acc.Where(a => a < threshold).Count() / w;
                if (cyclicPatternSize > 0.2)
                {
                    return(true);
                }
            }
            return(false);
        }
Пример #10
0
        public static Bitmap CyclicPatternsInLines(List <Point> edgePoints, List <RawLine> rawLines, RecognitionOptions options)
        {
            rawLines = rawLines.OrderBy(l => l.yInt).ToList();

            List <Bitmap> images = new List <Bitmap>();

            foreach (var rawLine in rawLines)
            {
                bool[] linePoints = new bool[options.imageWidth];
                foreach (var pt in edgePoints)
                {
                    if (Math.Abs(rawLine.yInt - (pt.Y - pt.X * rawLine.k)) < 2)
                    {
                        linePoints[pt.X] = true;
                    }
                }
                var lines = SegmentDetector.GetSegments(linePoints, rawLine);
                if (lines.Count > 0)
                {
                    images.Add(CyclicPatternsImage(linePoints, lines.First().p1.X, lines.Last().p2.X));
                }
            }

            return(ImageUtil.VerticalConcat(images));
        }
Пример #11
0
        /*
         * Forms solid lines from set of raw lines.
         * Discards lines that have cyclic patterns in them.
         */
        public static List <Line> FilterLines(List <Point> edgePoints, List <RawLine> rawLines, RecognitionOptions options)
        {
            List <Line> lines = new List <Line>();

            foreach (var rawLine in rawLines)
            {
                // convert rawLine to list of black/white pixels
                bool[] linePoints = new bool[options.imageWidth];
                foreach (var pt in edgePoints)
                {
                    if (Math.Abs(rawLine.yInt - (pt.Y - pt.X * rawLine.k)) < 2)
                    {
                        linePoints[pt.X] = true;
                    }
                }

                var segments = SegmentDetector.GetSegments(linePoints, rawLine);

                if (segments.Count > 0)
                {
                    Line solidLine = SegmentDetector.GetSolidLine(segments);

                    if (!options.detectCyclicPatterns ||
                        !CyclicPatternDetector.HasCyclicPatterns(linePoints, solidLine.p1.X, solidLine.p2.X, options))
                    {
                        lines.Add(solidLine);
                    }
                }
            }

            return(lines);
        }
Пример #12
0
        private void RunOCR(Bitmap sourceImage)
        {
            this.sourceImagePV.Image = sourceImage;

            Bitmap bw = ImageUtil.ToBlackAndWhite(sourceImage);

            this.bwImagePV.Image = bw;
            Bitmap rotBw = ImageUtil.RotateCounterClockwise(bw);

            this.rotBwImagePV.Image = rotBw;

            var horizOptions = RecognitionOptions.HorizontalOptions();

            horizOptions.imageWidth  = bw.Width;
            horizOptions.imageHeight = bw.Height;

            var vertOptions = RecognitionOptions.VerticalOptions();

            vertOptions.imageWidth  = rotBw.Width;
            vertOptions.imageHeight = rotBw.Height;

            List <Point> horizEdgePoints = EdgePointExtraction.ExtractEdgePoints(bw);

            this.edgePointsPV.Image = EdgePointExtraction.DrawPoints(bw, horizEdgePoints);
            int[,] horizHough       = PseudoHoughTransform.HoughTransform(horizEdgePoints, horizOptions);
            List <Point>   horizHoughPeaks      = PseudoHoughTransform.FindHoughPeaks(horizHough, horizOptions);
            Bitmap         horizHoughPlainImage = PseudoHoughTransform.HoughTransformImage(horizHough);
            Bitmap         horizHoughImage      = PseudoHoughTransform.HoughTransformImageWithPeaks(horizHough, horizHoughPeaks);
            List <RawLine> horizRawLines        = PseudoHoughTransform.ExtractRawLines(horizHoughPeaks, horizOptions);
            List <Line>    horizLines           = LineFilter.FilterLines(horizEdgePoints, horizRawLines, horizOptions);

            List <Point> vertEdgePoints = EdgePointExtraction.ExtractEdgePoints(rotBw);

            this.rotEdgePointsPV.Image = EdgePointExtraction.DrawPoints(rotBw, vertEdgePoints);
            int[,] vertHough           = PseudoHoughTransform.HoughTransform(vertEdgePoints, vertOptions);
            List <Point>   vertHoughPeaks      = PseudoHoughTransform.FindHoughPeaks(vertHough, vertOptions);
            Bitmap         vertHoughPlainImage = PseudoHoughTransform.HoughTransformImage(vertHough);
            Bitmap         vertHoughImage      = PseudoHoughTransform.HoughTransformImageWithPeaks(vertHough, vertHoughPeaks);
            List <RawLine> vertRawLines        = PseudoHoughTransform.ExtractRawLines(vertHoughPeaks, vertOptions);

            this.cyclicPatternsPV.Image = CyclicPatternDetector.CyclicPatternsInLines(vertEdgePoints, vertRawLines, vertOptions);

            RecognitionOptions vertNoFilterOptions = vertOptions;

            vertNoFilterOptions.detectCyclicPatterns = false;
            List <Line> vertUnfilteredLines = LineFilter.FilterLines(vertEdgePoints, vertRawLines, vertNoFilterOptions);
            List <Line> vertLines           = LineFilter.FilterLines(vertEdgePoints, vertRawLines, vertOptions);

            Bitmap rawLinesImage      = DrawLines(bw, horizLines, vertUnfilteredLines, 2);
            Bitmap filteredLinesImage = DrawLines(bw, horizLines, vertLines, 4);

            this.filteredLinesPV.Image = filteredLinesImage;

            this.houghPV.Image = ImageUtil.VerticalConcat(new List <Bitmap> {
                ImageUtil.HorizontalConcat(new List <Bitmap> {
                    rawLinesImage, horizHoughImage, horizHoughPlainImage
                }),
                ImageUtil.RotateClockwise(vertHoughImage),
                ImageUtil.RotateClockwise(vertHoughPlainImage)
            });

            var    lnorm = new LineNormalization(horizLines, vertLines, sourceImage);
            Bitmap normalizedLinesImage = DrawLines(bw, lnorm.normHorizLines, lnorm.normVertLines, 2);

            this.normalizedLinesPV.Image = normalizedLinesImage;
            var    tb = TableBuilder.NewBuilder(lnorm, new None <List <double> >());
            Bitmap tableRecognitionImage = tb.DebugImage(bw);

            this.tableRecognitionPV.Image = tableRecognitionImage;
            Option <Table> recognizedTable = tb.table;

            recognizedTable.ForEach(table => {
                Bitmap recognizedTableImage = new Bitmap(bw);
                Graphics g = Graphics.FromImage(recognizedTableImage);
                table.DrawTable(g, new Pen(Color.Red, 2));
                g.Dispose();
                this.recognizedTablePV.Image = recognizedTableImage;
            });
            if (recognizedTable.IsEmpty())
            {
                Console.WriteLine("no table was recognized");
            }

            this.recognizedTablePV.AddDoubleClickListener((pt, e) => {
                recognizedTable.ForEach(table => {
                    table.GetCellAtPoint(pt.X, pt.Y).ForEach(cell => {
                        table.GetCellImage(bw, cell.X, cell.Y).ForEach(cellImage => {
                            new GradeRecognitionDebugView(cellImage, "<gen>").ShowDialog();
                        });
                    });
                });
            });
        }