检测七边形对象不正确

时间:2016-09-22 09:09:21

标签: c++ opencv shapes

我想检测一个七边形物体(箭头)。我使用Opencv进行简单的形状检测以检测七边形(7个顶点)。使用代码Im能够检测三角形,圆形,矩形而不是七边形对象(在图像中它是箭头)。这是我的代码

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>

 using namespace cv;
 using namespace std;

 Mat src; Mat src_gray;

  /**
      * Helper function to find a cosine of angle between vectors
      * from pt0->pt1 and pt0->pt2
      */
     static double angle(cv::Point pt1, cv::Point pt2, cv::Point pt0)
     {
        double dx1 = pt1.x - pt0.x;
        double dy1 = pt1.y - pt0.y;
        double dx2 = pt2.x - pt0.x;
        double dy2 = pt2.y - pt0.y;
        return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
     }

     /**
      * Helper function to display text in the center of a contour
      */
     void setLabel(cv::Mat& im, const std::string label, std::vector<cv::Point>& contour)
     {
        int fontface = cv::FONT_HERSHEY_SIMPLEX;
        double scale = 0.4;
        int thickness = 1;
        int baseline = 0;

        cv::Size text = cv::getTextSize(label, fontface, scale, thickness, &baseline);
        cv::Rect r = cv::boundingRect(contour);

        cv::Point pt(r.x + ((r.width - text.width) / 2), r.y + ((r.height + text.height) / 2));
        cv::rectangle(im, pt + cv::Point(0, baseline), pt + cv::Point(text.width, -text.height), CV_RGB(255,255,255), CV_FILLED);
        cv::putText(im, label, pt, fontface, scale, CV_RGB(0,0,0), thickness, 8);
     }

     int main()
     {

        cv::Mat src = cv::imread("shapes.png");
        if (src.empty())
            return -1;

        // Convert to grayscale
        cv::Mat gray;
        cv::cvtColor(src, gray, CV_BGR2GRAY);

        // Use Canny instead of threshold to catch squares with gradient shading
        cv::Mat bw;
        cv::Canny(gray, bw, 0, 50, 5);

        // Find contours
        std::vector<std::vector<cv::Point> > contours;
        cv::findContours(bw.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

        std::vector<cv::Point> approx;
        cv::Mat dst = src.clone();

        for (int i = 0; i < contours.size(); i++)
        {
            // Approximate contour with accuracy proportional
            // to the contour perimeter
            cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);

            // Skip small or non-convex objects
            if (std::fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
                continue;

            if (approx.size() == 3)
            {
                setLabel(dst, "TRI", contours[i]);    // Triangles
            }
            else if (approx.size() >= 4 && approx.size() <= 6)
            {
                // Number of vertices of polygonal curve
                int vtc = approx.size();

                // Get the cosines of all corners
                std::vector<double> cos;
                for (int j = 2; j < vtc+1; j++)
                    cos.push_back(angle(approx[j%vtc], approx[j-2], approx[j-1]));

                // Sort ascending the cosine values
                std::sort(cos.begin(), cos.end());

                // Get the lowest and the highest cosine
                double mincos = cos.front();
                double maxcos = cos.back();

                // Use the degrees obtained above and the number of vertices
                // to determine the shape of the contour
                if (vtc == 4 && mincos >= -0.1 && maxcos <= 0.3)
                    setLabel(dst, "RECT", contours[i]);
                else if (vtc == 5 && mincos >= -0.34 && maxcos <= -0.27)
                    setLabel(dst, "PENTA", contours[i]);
                else if (vtc == 6 && mincos >= -0.55 && maxcos <= -0.45)
                    setLabel(dst, "HEXA", contours[i]);
                 if (vtc ==7 && mincos >= 10.1 && maxcos <= -10.45)
                     setLabel(dst, "ARROW", contours[i]);
            }
            else
            {
                // Detect and label circles
                double area = cv::contourArea(contours[i]);
                cv::Rect r = cv::boundingRect(contours[i]);
                int radius = r.width / 2;

                if (std::abs(1 - ((double)r.width / r.height)) <= 0.2 &&
                    std::abs(1 - (area / (CV_PI * std::pow(radius, 2)))) <= 0.2)
                    setLabel(dst, "CIR", contours[i]);
            }
        }

        cv::imshow("src", src);
        cv::imshow("dst", dst);
        cv::waitKey(0);
        return 0;
     }

这是输入图像 Input

这就是输出 result

任何帮助?

1 个答案:

答案 0 :(得分:0)

在你的代码中:

// Skip small or non-convex objects
            if (std::fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
                continue;

取出!cv::isContourConvex(approx) ,这可能就是为什么你所拥有的七边形被忽略了,因为它们实际上是非凸的。

aprox.size() <= 6条件中的else if也可能过滤掉了肝素。

关于检查箭头形状,因为底座是方形的,你可以试试这样的东西:

 bool isRightCosin(double c){
    // This covers cosin of both convex and none-convex right angles, since 
    // there is no way to distinguish them
    return  -0.1 <= c && c <= 0.1
 }

 bool isArrow(std::vector<cv::Point> & approx){
    // It checks if the polygon is an heptagon and contains a square shaped 
    // sub-structure. Beware, some other shapes could pass the test:
    //
    //     /\              /\                 /\  
    //    /  \         ___/  \____           /  \
    //   /    \        |         |          /    \
    //  /_    _\       |         |         /  __  \
    //    |  |         |_________|        /  |  |  \
    //    |__|                           /___|  |___\


    int vtc = approx.size();
    if (vtc!=7) {
        return false;
    }

    // Get the cosines of all corners, with redundancy, to make the
    // algorithm easier 
    std::vector<double> cosines;
    for (int j = 0; j < vtc+5; j++){
        cosines.push_back(angle(approx[(j+2)%vtc], 
                                approx[j%vtc], 
                                approx[(j+1)%vtc]));
    }

    for (int j = 0; j < vtc; j++){
       if (isRightCosin(cosines[j]) && isRightCosin(cosines[j+1]) 
           isRightCosin(cosines[j+2]) && isRightCosin(cosines[j+3]){
            return true;
        } 
    return false;
 }