How to find a black square with any angle of rotat

2019-02-07 13:30发布

I need to find the coordinates of three black squares in the test form. I took the example code from the site emgu.com and slightly changed it, but he does not find what I need. The size of image is A4, and the size of the test form is A5. I am hope for your help :) I nearly forgot, the size of squares 30 pixels.

private void DetectRectangles(Image<Gray, byte> img)
    {
                        var size = new Size(3, 3);
            CvInvoke.GaussianBlur(img, img, size, 0);
            CvInvoke.AdaptiveThreshold(img, img, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 75, 100);
            UMat cannyEdges = new UMat();
            CvInvoke.Canny(img, cannyEdges, 180, 120);
            var boxList = new List<RotatedRect>();

            using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
            {
                CvInvoke.FindContours(cannyEdges, contours, null, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);
                int count = contours.Size;
                for (int i = 0; i < count; i++)
                {
                    using (VectorOfPoint contour = contours[i])
                    using (VectorOfPoint approxContour = new VectorOfPoint())
                    {
                        CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.05, true);
                        var area = CvInvoke.ContourArea(approxContour);
                        if (area > 800 && area < 1000)
                        {
                            if (approxContour.Size == 4)
                            {
                                bool isRectangle = true;
                                Point[] pts = approxContour.ToArray();
                                LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                                for (int j = 0; j < edges.Length; j++)
                                {
                                    double angle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
                                    if (angle < 75 || angle > 94)
                                    {
                                        isRectangle = false;
                                        break;
                                    }
                                }

                                if (isRectangle)
                                    boxList.Add(CvInvoke.MinAreaRect(approxContour));
                            }
                        }
                    }
                }
            }
            var resultimg = new Image<Bgr,byte>(img.Width, img.Height);
            CvInvoke.CvtColor(img, resultimg, ColorConversion.Gray2Bgr);
            foreach (RotatedRect box in boxList)
            {
                CvInvoke.Polylines(resultimg, Array.ConvertAll(box.GetVertices(), Point.Round), true, new Bgr(Color.Red).MCvScalar, 2);
            }
            imageBox1.Image = resultimg;
            resultimg.Save("result_img.jpg");       }

Input image:

1条回答
做自己的国王
2楼-- · 2019-02-07 13:46

Since you are looking for a very specific object, you can use the following algorithm:

  1. Invert the image, so that the foreground becomes white, and the background black.
  2. Find contours of connected components
  3. For each contours

    a. Compute the minimum area rectangle box

    b. Compute the area of box: barea

    c. Compute the area of the contour: carea

    d. Apply some constraint to be sure your contour is the square you're looking for

The constraints of step 3d are:

  1. The ratio barea / carea should be high (let's say higher then 0.9), meaning that the contour belongs to an almost rectangular blob.

  2. The aspect ratio of box should be almost 1, meaning that the box is basically a square

  3. The size of the square should be almost 30, to reject other smaller or bigger squares in the image.

The result I get running this is:

enter image description here

Here is the code. Sorry, it's C++, but since it's all OpenCV function calls you should be able to port it easily to C#. At least, you can use it as a reference:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;

int main()
{
    // Load image
    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    // Create the output image
    Mat3b out;
    cvtColor(img, out, COLOR_GRAY2BGR);

    // Create debug image
    Mat3b dbg = out.clone();

    // Binarize (to remove jpeg arifacts)
    img = img > 200;

    // Invert image
    img = ~img;

    // Find connected components
    vector<vector<Point>> contours;
    findContours(img.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    vector<RotatedRect> squares;

    // For each contour
    for (int i = 0; i < contours.size(); ++i)
    {
        // Find rotated bounding box
        RotatedRect box = minAreaRect(contours[i]);

        // Compute the area of the contour
        double carea = contourArea(contours[i]);
        // Compute the area of the box
        double barea = box.size.area();

        // Constraint #1
        if ((carea / barea) > 0.9)
        {
            drawContours(dbg, contours, i, Scalar(0, 0, 255), 7);

            // Constraint #2
            if (min(box.size.height, box.size.width) / max(box.size.height, box.size.width) > 0.95)
            {
                drawContours(dbg, contours, i, Scalar(255, 0, 0), 5);

                // Constraint #3
                if (box.size.width > 25 && box.size.width < 35)
                {
                    drawContours(dbg, contours, i, Scalar(0, 255, 0), 3);

                    // Found the square!
                    squares.push_back(box);
                }
            }
        }

        // Draw output
        for (int i = 0; i < squares.size(); ++i)
        {
            Point2f pts[4];
            squares[i].points(pts);

            for (int j = 0; j < 4; ++j)
            {
                line(out, pts[j], pts[(j + 1) % 4], Scalar(0,255,0), 5);
            }
        }
    }

    // Resize for better visualization
    resize(out, out, Size(), 0.25, 0.25);
    resize(dbg, dbg, Size(), 0.25, 0.25);

    // Show images
    imshow("Steps", dbg);
    imshow("Result", out);
    waitKey();

    return 0;
}
查看更多
登录 后发表回答