static public void DeskewAsColumnOfBlocks(ref Bitmap bitmap, int blockMaxHeight, int minBlockSpan)
        {
            using (Image <Rgb, byte> image = bitmap.ToImage <Rgb, byte>())
            {
                bitmap.Dispose();

                //return image.ToBitmap();
                Image <Rgb, byte>  deskewedimage = new Image <Rgb, byte>(image.Size);
                Image <Gray, byte> image2        = image.Convert <Gray, byte>();
                CvInvoke.BitwiseNot(image2, image2);
                //CvInvoke.Blur(image2, image2, new Size(3, 3), new Point(0, 0));
                CvInvoke.GaussianBlur(image2, image2, new Size(25, 25), 5);//remove small spots
                CvInvoke.Threshold(image2, image2, 125, 255, ThresholdType.Otsu | ThresholdType.Binary);
                Mat se = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(5, 5), new Point(-1, -1));
                CvInvoke.Dilate(image2, image2, se, new Point(-1, -1), 1, BorderType.Constant, CvInvoke.MorphologyDefaultBorderValue);
                //CvInvoke.Erode(image2, image2, se, new Point(-1, -1), 5, BorderType.Constant, CvInvoke.MorphologyDefaultBorderValue);

                //CvInvoke.BitwiseNot(image2, image2);
                //return image2.ToBitmap();

                VectorOfVectorOfPoint cs = new VectorOfVectorOfPoint();
                Mat h = new Mat();
                CvInvoke.FindContours(image2, cs, h, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);
                if (cs.Size < 1)
                {
                    return;
                }

                Array          hierarchy = h.GetData();
                List <Contour> contours  = new List <Contour>();
                for (int i = 0; i < cs.Size; i++)
                {
                    int p = (int)hierarchy.GetValue(0, i, Contour.HierarchyKey.Parent);
                    if (p < 1)
                    {
                        contours.Add(new Contour(hierarchy, i, cs[i]));
                    }
                }
                if (contours.Where(a => a.ParentId < 0).Count() < 2)//the only parent is the whole page frame
                {
                    contours.RemoveAll(a => a.ParentId < 0);
                }
                else
                {
                    contours.RemoveAll(a => a.ParentId >= 0);
                }

                contours = contours.OrderBy(a => a.BoundingRectangle.Bottom).ToList();
                for (int blockY = 0; blockY < image.Height;)
                {
                    int blockBottom = image.Height - 1;
                    Tuple <Contour, Contour> lastSpan = null;
                    for (; contours.Count > 0;)
                    {
                        Contour c = contours[0];
                        contours.RemoveAt(0);
                        if (contours.Count > 0)
                        {
                            Contour minTop = contours.Aggregate((a, b) => a.BoundingRectangle.Top < b.BoundingRectangle.Top ? a : b);
                            if (c.BoundingRectangle.Bottom + minBlockSpan <= minTop.BoundingRectangle.Top)
                            {
                                lastSpan = new Tuple <Contour, Contour>(c, minTop);
                            }
                        }

                        if (c.BoundingRectangle.Bottom > blockY + blockMaxHeight && lastSpan != null)
                        {
                            blockBottom = lastSpan.Item1.BoundingRectangle.Bottom + minBlockSpan / 2;
                            break;
                        }
                    }

                    Rectangle         blockRectangle = new Rectangle(0, blockY, image2.Width, blockBottom + 1 - blockY);
                    Image <Rgb, byte> blockImage     = image.Copy(blockRectangle);
                    blockImage        = deskew(blockImage);
                    deskewedimage.ROI = blockRectangle;
                    blockImage.CopyTo(deskewedimage);
                    deskewedimage.ROI = Rectangle.Empty;
                    // break;
                    blockY = blockBottom + 1;
                }
                bitmap = deskewedimage?.ToBitmap();
            }
        }
 public Match(Contour templateContour, Contour pageContour)
 {
     TemplateContour = templateContour;
     PageContour     = pageContour;
 }