protected virtual void CheckCenterLine(Bitmap image)
        {
            var tmpImage = image.Copy(new Rectangle(0, 0, image.Width, image.Height));

            var tmpImageData = tmpImage.LockBits(new Rectangle(new System.Drawing.Point(0, 0), tmpImage.Size),
                                                 ImageLockMode.ReadWrite,
                                                 tmpImage.PixelFormat);

            Drawing.Line(tmpImageData,
                         new IntPoint(0, 0),
                         new IntPoint(0, tmpImageData.Height),
                         Color.White);

            tmpImage.UnlockBits(tmpImageData);

            OCRLessonReport.Imaging.HoughLineTransformation lineTransform = new OCRLessonReport.Imaging.HoughLineTransformation();

            HoughLineRequestSettings vSettings = new HoughLineRequestSettings
            {
                HorizontalLines = false,
                VerticalLines   = true
            };

            var hWidth  = tmpImage.Width / 2;
            var hHeight = tmpImage.Height / 2;

            lineTransform.ProcessImage(tmpImage, vSettings);

            HoughLine[] vLines = lineTransform.GetLinesByRelativeIntensity(0.9);

            var vLineCoordinates = vLines.Select(line => hWidth + line.Radius).Where(x => x > 1).OrderBy(x => x).ToArray();

            if (vLineCoordinates.Length > 0)
            {
                var x0 = vLineCoordinates.FirstOrDefault() - 1;
                var x1 = vLineCoordinates.LastOrDefault() + 2;

                if (x1 - x0 > 10)
                {
                    return;
                }

                var imageData = image.LockBits(new Rectangle(new System.Drawing.Point(0, 0), image.Size),
                                               ImageLockMode.ReadWrite,
                                               image.PixelFormat);

                Drawing.FillRectangle(imageData, new Rectangle(x0, 0, x1 - x0, image.Height), Color.Black);

                image.UnlockBits(imageData);
            }
        }
        protected virtual double DetectRotation(Bitmap image)
        {
            //Hough line transformation
            OCRLessonReport.Imaging.HoughLineTransformation lineTransform = new OCRLessonReport.Imaging.HoughLineTransformation();

            HoughLineRequestSettings settings = new HoughLineRequestSettings
            {
                HorizontalLines   = false,
                VerticalLines     = true,
                VerticalDeviation = Settings.SheetEdgeVerticalMaxAngle
            };

            lineTransform.ProcessImage(image, settings);

            HoughLine[] lines = lineTransform.GetLinesByRelativeIntensity(Settings.SheetEdgeSensitivity);

            if (lines.Length < 1)
            {
                throw new Exception("Can't detect left edge");
            }

            Func <HoughLine, double> getAngle = line =>
            {
                double angle = 0;

                if (line.Theta > 90 && line.Radius > 0)
                {
                    angle = 180 - line.Theta;
                }
                else if (line.Theta > 90 && line.Radius < 0)
                {
                    angle = 180 - line.Theta;
                }
                else if (line.Theta < 90 && line.Radius > 0)
                {
                    angle = -line.Theta;
                }
                else if (line.Theta < 90 && line.Radius < 0)
                {
                    angle = -line.Theta;
                }

                return(angle);
            };

            double avgAngle = lines.Select(l => getAngle(l)).Average();

            return(avgAngle);
        }
        protected virtual Bitmap ProcessCell(Bitmap cellImg, bool check = false)
        {
            //Build horizontal hough lines
            OCRLessonReport.Imaging.HoughLineTransformation lineTransform = new OCRLessonReport.Imaging.HoughLineTransformation();

            HoughLineRequestSettings vSettings = new HoughLineRequestSettings
            {
                HorizontalLines = false,
                VerticalLines   = true
            };

            HoughLineRequestSettings hSettings = new HoughLineRequestSettings
            {
                HorizontalLines = true,
                VerticalLines   = false
            };

            lineTransform.ProcessImage(cellImg, vSettings);

            HoughLine[] vLines = lineTransform.GetLinesByRelativeIntensity(Settings.CellSensitivity);

            lineTransform.ProcessImage(cellImg, hSettings);

            HoughLine[] hLines = lineTransform.GetLinesByRelativeIntensity(Settings.CellSensitivity);

            var hWidth  = cellImg.Width / 2;
            var hHeight = cellImg.Height / 2;

            var vLineCoordinates = vLines.Select(line => hWidth + line.Radius);
            var hLineCoordinates = hLines.Select(line => hHeight - line.Radius);

            var xEdges = GetCellEdges(vLineCoordinates, cellImg.Width, Settings.CellXEdgeWith, Settings.CellEdgeCutting);
            var yEdges = GetCellEdges(hLineCoordinates, cellImg.Height, Settings.CellYEdgeWith, Settings.CellEdgeCutting);

            cellImg = cellImg.Copy(new Rectangle(xEdges.Item1, yEdges.Item1, xEdges.Item2 - xEdges.Item1, yEdges.Item2 - yEdges.Item1));

            //Check central line
            if (check)
            {
                CheckCenterLine(cellImg);
            }

            return(cellImg);
        }
        /// <summary>
        /// Process an image building Hough map.
        /// </summary>
        ///
        /// <param name="image">Source image to process.</param>
        /// <param name="rect">Image's rectangle to process.</param>
        ///
        /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format of the source image.</exception>
        ///
        public void ProcessImage(Bitmap image, Rectangle rect, HoughLineRequestSettings settings = null)
        {
            // check image format
            if (image.PixelFormat != PixelFormat.Format8bppIndexed)
            {
                throw new UnsupportedImageFormatException("Unsupported pixel format of the source image.");
            }

            // lock source image
            BitmapData imageData = image.LockBits(
                new Rectangle(0, 0, image.Width, image.Height),
                ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);

            try
            {
                // process the image
                ProcessImage(new UnmanagedImage(imageData), rect, settings);
            }
            finally
            {
                // unlock image
                image.UnlockBits(imageData);
            }
        }
        public virtual void ProccessImage(BackgroundWorker worker)
        {
            this.worker = worker;

            UpdateProgress(3);

            //Brightness and sharpen filters
            BrightnessCorrection cfilter = new BrightnessCorrection(Settings.Brightness);
            GaussianSharpen      filter  = new GaussianSharpen(4, 11);

            //Apply filters
            cfilter.ApplyInPlace(sourceBitmap);
            UpdateProgress(15);
            filter.ApplyInPlace(sourceBitmap);
            UpdateProgress(30);

            //Convert to gray
            var tmpImage = ConvertToGrayScale(sourceBitmap);

            UpdateProgress(35);

            //Cut edges
            tmpImage = CutEdgesAndInvert(tmpImage);
            UpdateProgress(40);

            //Get angle for rotating image
            var rotateAngle = DetectRotation(tmpImage);

            UpdateProgress(45);

            if (rotateAngle != 0)
            {
                RotateBilinear rotate = new RotateBilinear(rotateAngle, true);
                tmpImage = rotate.Apply(tmpImage);
            }

            //Build horizontal hough lines
            OCRLessonReport.Imaging.HoughLineTransformation lineTransform = new OCRLessonReport.Imaging.HoughLineTransformation();

            HoughLineRequestSettings settings = new HoughLineRequestSettings
            {
                HorizontalLines     = true,
                VerticalLines       = false,
                HorizontalDeviation = 2
            };

            lineTransform.ProcessImage(tmpImage, settings);

            //Get horizontal line
            HoughLine[] lines = lineTransform.GetLinesByRelativeIntensity(Settings.HorizontalSensitivity);

            //Get half width and height for future calculations
            int hWidth  = tmpImage.Width / 2;
            int hHeight = tmpImage.Height / 2;
            //Get line coordinates (Y axis only - horizontal lines)
            var lineCoordinates = lines.Select(line => hHeight - line.Radius);
            //Grouping coords by delta
            var groupedCoordinates = ImagingHelper.GroupingCoordinates(lineCoordinates, Settings.LineGroupingDelta);

            if (groupedCoordinates.Count <= Settings.HeaderStartLine)
            {
                throw new Exception("Invalid source. Can't be recognized");
            }

            int headerLineY0 = groupedCoordinates[Settings.HeaderStartLine];
            int headerLineY1 = groupedCoordinates[Settings.HeaderStartLine + 1];
            //Copy header to new image
            var headerImage = tmpImage.Copy(new Rectangle(0, headerLineY0, tmpImage.Width, headerLineY1 - headerLineY0));
            //Parse header to get header lines
            HoughLineRequestSettings headerSettings = new HoughLineRequestSettings
            {
                HorizontalLines   = false,
                VerticalLines     = true,
                VerticalDeviation = 1
            };

            lineTransform.ProcessImage(headerImage, headerSettings);

            Func <HoughLine, int, int> getRadius = (l, w) =>
            {
                if (l.Theta > 90 && l.Theta < 180)
                {
                    return(w - l.Radius);
                }
                else
                {
                    return(w + l.Radius);
                }
            };

            HoughLine[] headerLines = lineTransform.GetLinesByRelativeIntensity(Settings.VerticalSensitivity);
            //Get header vertical lines
            var headerLineCoordinates = headerLines.Select(line => getRadius(line, hWidth));
            //Grouped lines
            var groupedheaderLineCoordinates = ImagingHelper.GroupingCoordinates(headerLineCoordinates, Settings.LineGroupingDelta);
            //Build cell map
            List <TableCell> cellMap = new List <TableCell>();

            UpdateProgress(50);

            //Use tess engine for ocr
            using (TesseractEngine engine = new TesseractEngine(Settings.TessdataPath, Settings.TessdataLanguage))
            {
                //Parse top header
                var x0 = groupedheaderLineCoordinates.FirstOrDefault();
                var x1 = groupedheaderLineCoordinates.LastOrDefault();
                var y0 = groupedCoordinates[0];
                var y1 = groupedCoordinates[1];

                int fullProgress = (groupedheaderLineCoordinates.Count - 1) * (groupedCoordinates.Count - Settings.BottomStartLine - 1 - Settings.HeaderStartLine);
                int curProgress  = 0;

                var hImage = tmpImage.Copy(new Rectangle(x0, y0, x1 - x0, y1 - y0));
                hImage = ProcessCell(hImage);

                using (var page = engine.Process(hImage, PageSegMode.SingleBlock))
                {
                    cellMap.Add(new TableCell(0, 0, TableCellType.MainHeader, hImage, page.GetText(), false));
                }

                //Parse table
                for (int i = 0; i < groupedheaderLineCoordinates.Count - 1; i++)
                {
                    int subjectArea = (i < Settings.ColumnSubjectStart - 1) ? 0 : 1;

                    for (int j = Settings.HeaderStartLine; j < groupedCoordinates.Count - Settings.BottomStartLine - 1; j++)
                    {
                        int headerArea = (j == Settings.HeaderStartLine) ? 2 : 0;

                        TableCellType cellType = (TableCellType)(subjectArea + headerArea);

                        var cellImg = tmpImage.Copy(new Rectangle(groupedheaderLineCoordinates[i], groupedCoordinates[j],
                                                                  groupedheaderLineCoordinates[i + 1] - groupedheaderLineCoordinates[i],
                                                                  groupedCoordinates[j + 1] - groupedCoordinates[j]));

                        if (cellType == TableCellType.Text || cellType == TableCellType.Header || cellType == TableCellType.HeaderRotated)
                        {
                            cellImg = ProcessCell(cellImg, i == Settings.NameStartLine);

                            string text = String.Empty;

                            if (cellType == TableCellType.HeaderRotated)
                            {
                                cellImg.RotateFlip(RotateFlipType.Rotate90FlipNone);
                            }

                            using (var page = engine.Process(cellImg, PageSegMode.SingleBlock))
                            {
                                text = page.GetText();
                            }


                            cellMap.Add(new TableCell(i, j, cellType, cellImg, text, false));
                        }
                        else
                        {
                            cellImg = ProcessCell(cellImg);

                            BilateralSmoothing bfilter = new BilateralSmoothing();
                            bfilter.KernelSize    = 7;
                            bfilter.SpatialFactor = 10;
                            bfilter.ColorFactor   = 60;
                            bfilter.ColorPower    = 0.5;
                            bfilter.ApplyInPlace(cellImg);

                            cellImg = FilterColors(cellImg, Settings.FilteringColor, ByteColor.Black, ByteColor.White);

                            BlobCounter bcounter = new BlobCounter();
                            bcounter.ProcessImage(cellImg);

                            var blobs = bcounter.GetObjects(cellImg, false);

                            if (blobs.Length < 1)
                            {
                                continue;
                            }

                            var biggestBlob       = blobs.OrderBy(b => b.Area).LastOrDefault();
                            var biggestBlobsImage = biggestBlob.Image.ToManagedImage();

                            cellMap.Add(new TableCell(i, j, cellType, biggestBlobsImage, GetMask(biggestBlobsImage).ToString(), GetMask(biggestBlobsImage)));
                        }

                        curProgress++;
                        double reportProgress = (double)curProgress / (double)fullProgress * 50 + 50;
                        UpdateProgress((int)reportProgress);
                    }
                }
            }

            this.Cells = cellMap;

            UpdateProgress(100);
        }
        /// <summary>
        /// Process an image building Hough map.
        /// </summary>
        ///
        /// <param name="image">Source unmanaged image to process.</param>
        /// <param name="rect">Image's rectangle to process.</param>
        ///
        /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format of the source image.</exception>
        ///
        public void ProcessImage(UnmanagedImage image, Rectangle rect, HoughLineRequestSettings settings = null)
        {
            if (image.PixelFormat != PixelFormat.Format8bppIndexed)
            {
                throw new UnsupportedImageFormatException("Unsupported pixel format of the source image.");
            }

            // get source image size
            int width      = image.Width;
            int height     = image.Height;
            int halfWidth  = width / 2;
            int halfHeight = height / 2;

            // make sure the specified rectangle recides with the source image
            rect.Intersect(new Rectangle(0, 0, width, height));

            int startX = -halfWidth + rect.Left;
            int startY = -halfHeight + rect.Top;
            int stopX  = width - halfWidth - (width - rect.Right);
            int stopY  = height - halfHeight - (height - rect.Bottom);

            int offset = image.Stride - rect.Width;

            // calculate Hough map's width
            int halfHoughWidth = (int)Math.Sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
            int houghWidth     = halfHoughWidth * 2;

            houghMap = new short[houghHeight, houghWidth];

            // do the job
            unsafe
            {
                byte *src = (byte *)image.ImageData.ToPointer() +
                            rect.Top * image.Stride + rect.Left;

                // for each row
                for (int y = startY; y < stopY; y++)
                {
                    // for each pixel
                    for (int x = startX; x < stopX; x++, src++)
                    {
                        if (*src > 0)
                        {
                            // for each Theta value
                            for (int theta = 0; theta < houghHeight; theta++)
                            {
                                if (settings != null && !settings.AllowedThetas.Contains(theta))
                                {
                                    continue;
                                }

                                int radius = (int)Math.Round(cosMap[theta] * x - sinMap[theta] * y) + halfHoughWidth;

                                if ((radius < 0) || (radius >= houghWidth))
                                {
                                    continue;
                                }

                                houghMap[theta, radius]++;
                            }
                        }
                    }
                    src += offset;
                }
            }

            // find max value in Hough map
            maxMapIntensity = 0;
            for (int i = 0; i < houghHeight; i++)
            {
                for (int j = 0; j < houghWidth; j++)
                {
                    if (houghMap[i, j] > maxMapIntensity)
                    {
                        maxMapIntensity = houghMap[i, j];
                    }
                }
            }

            CollectLines();
        }
 /// <summary>
 /// Process an image building Hough map.
 /// </summary>
 ///
 /// <param name="imageData">Source image data to process.</param>
 /// <param name="rect">Image's rectangle to process.</param>
 ///
 /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format of the source image.</exception>
 ///
 public void ProcessImage(BitmapData imageData, Rectangle rect, HoughLineRequestSettings settings = null)
 {
     ProcessImage(new UnmanagedImage(imageData), rect, settings);
 }
 /// <summary>
 /// Process an image building Hough map.
 /// </summary>
 ///
 /// <param name="imageData">Source image data to process.</param>
 ///
 /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format of the source image.</exception>
 ///
 public void ProcessImage(BitmapData imageData, HoughLineRequestSettings settings = null)
 {
     ProcessImage(new UnmanagedImage(imageData),
                  new Rectangle(0, 0, imageData.Width, imageData.Height), settings);
 }
 /// <summary>
 /// Process an image building Hough map.
 /// </summary>
 ///
 /// <param name="image">Source image to process.</param>
 ///
 /// <exception cref="UnsupportedImageFormatException">Unsupported pixel format of the source image.</exception>
 ///
 public void ProcessImage(Bitmap image, HoughLineRequestSettings settings = null)
 {
     ProcessImage(image, new Rectangle(0, 0, image.Width, image.Height), settings);
 }