Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
596 views
in Technique[技术] by (71.8m points)

c# - How to find a black square with any angle of rotation in the image using emgu cv

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:

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...