2016-09-22 6 views
2

Я хотел бы обнаружить объект гептагона (стрелка). Используя 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

Любая помощь?

+0

Что библиотека говорит о формах, необходимых для выпуклых? Я имею в виду, возможно, функции, которые вы используете, работают только для выпуклых геометрий. – eguaio

+0

Итак, для не выпуклых фигур, как стрелка, что мне использовать? – user3035413

+0

Я просто спрашивал, но это, похоже, не проблема. – eguaio

ответ

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

Я взял его, но все еще не работает. Как насчет сравнения значений момента каждой фигуры? Или нужно использовать другой подход? – user3035413

+0

Я отредактировал ответ, посмотрите, что произойдет, стирая условие <= 6. Если это тоже не сработает, я бы построил все полигоны 'aprox', чтобы увидеть, что происходит. – eguaio

+0

Хорошо. Если я использую if (approx.size() == 7), то можно найти все семиугольники. Но при использовании косинусов всех углов, он отфильтровывает гептагоны. Но я хочу обнаружить только стрелку не всех семиугольников. Итак, как обнаружить стрелу от других семиугольников? – user3035413