如何避免 MinAreaRect 检测到的矩形旋转?

How to avoid rotation of rects, detected by MinAreaRect?

我正在尝试检测 Windows 表单上的文本字段,但是 CvInvoke.MinAreaRect(contour) returns 矩形,旋转了 -7.29419661 角度。

我的密码是

        Image<Bgr, Byte> a =
          new Image<Bgr, byte>(@"d:/Art/documents/Projects/InputFieldsDetector/Images/Form345_1.PNG");

        imageBox1.Image = a;

        UMat grayed = new UMat();
        CvInvoke.CvtColor(a, grayed, ColorConversion.Bgr2Gray);
        imageBox2.Image = grayed;

        UMat canny = new UMat();
        CvInvoke.Canny(grayed, canny, 50, 200, 3);
        imageBox3.Image = canny;

        VectorOfVectorOfPoint cnts = new VectorOfVectorOfPoint();

        UMat hierarchy = new UMat();
        CvInvoke.FindContours(canny, cnts, null, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);

        Image<Bgr, Byte> justCountor = a.Clone();

        List<string> sizes = new List<string>();

        int count = cnts.Size;
        for (int i = 0; i < count; i++)
        {
            VectorOfPoint contour = cnts[i];
            var area = CvInvoke.ContourArea(contour);
            //if (area > 10000 && area < 15000)
            if (area > 200 && area < 300)
            {
                sizes.Add(area.ToString());

                Point[] pts = contour.ToArray();
                var forDraw = CvInvoke.MinAreaRect(contour);
                // forDraw.Angle = 0;
                //forDraw.Center.Y += 10;

                justCountor.Draw(forDraw, new Bgr(Color.DarkOrange), 2);
            }
        }
        imageBox4.Image = justCountor;

        List<double> result = sizes.Select(x => double.Parse(x)).ToList();

        result.Sort();
        sizes = result.Select(x => x.ToString()).ToList();

        File.WriteAllLines("c:/temp/qqq.txt", sizes);

原图为:

如果我取消注释部分

            forDraw.Angle = 0;
            forDraw.Center.Y += 10;

检测到的矩形大小与字段大小相似...

请告诉我,为什么返回的矩形会旋转,如何解决?

您可以在 Canny 输出中看到算法将阴影解释为边界。解决这个问题的最简单方法是使用接近白色框背景的高值阈值对图像进行预过滤。

Image<Bgr, Byte> a =
              new Image<Bgr, byte>(@"d:/Art/documents/Projects/InputFieldsDetector/Images/Form345_1.PNG");

imageBox1.Image = a;

UMat grayed = new UMat();
CvInvoke.CvtColor(a, grayed, ColorConversion.Bgr2Gray);
imageBox2.Image = grayed;

UMat thresholded = new UMat();
CvInvoke.Threshold(grayed, thresholded, 128, 255, ThresholdType.Binary);
imageBox5.Image = thresholded;

UMat canny = new UMat();
CvInvoke.Canny(thresholded, canny, 50, 200, 3);
imageBox3.Image = canny;

VectorOfVectorOfPoint cnts = new VectorOfVectorOfPoint();

UMat hierarchy = new UMat();
CvInvoke.FindContours(canny, cnts, null, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);

Image<Bgr, Byte> justCountor = a.Clone(); 
List<string> sizes = new List<string>(); 
int count = cnts.Size;
for (int i = 0; i < count; i++)
{
    VectorOfPoint contour = cnts[i];
    var area = CvInvoke.ContourArea(contour);
    if (area > 200 && area < 300)
    {
        sizes.Add(area.ToString());
        Point[] pts = contour.ToArray();
        var forDraw = CvInvoke.MinAreaRect(contour);

        // forDraw.Angle = 0;
        //forDraw.Center.Y += 10;

        if (forDraw.Angle==0)
            justCountor.Draw(forDraw, new Bgr(Color.DarkOrange), 2);
    }
}
imageBox4.Image = justCountor;

List<double> result = sizes.Select(x => double.Parse(x)).ToList();
result.Sort();
sizes = result.Select(x => x.ToString()).ToList();
File.WriteAllLines("c:/temp/qqq.txt", sizes);