使用线的简单图画的近似照片

时间:2014-06-03 20:28:30

标签: opencv line detection approximation

作为输入,我有一张简单符号的照片,例如:https://www.dropbox.com/s/nrmsvfd0le0bkke/symbol.jpg

我想检测它中的直线,比如线的起点和终点。在这种情况下,假设符号的左上角是(0,0),则这些线的定义如下:

   开始结束(线的起点和终点的坐标)
1.(0,0); (0,10)(垂直线)
2.(0,10); (15,15)
3.(15,15); (0,20)
4.(0,20); (0,30)

我该怎么做(最好使用OpenCV)?我虽然关于霍夫线,但它们似乎对完美的细直线有好处,而在图纸中并非如此。我也可能会处理二值化图像。

3 个答案:

答案 0 :(得分:1)

尝试一下,

  1. 在阈值图片上应用thinning algorithm

  2. Find contours.

  3. approxPolyDP找到轮廓。

  4. 参见参考文献:

答案 1 :(得分:1)

也许你可以解决这个问题。

  1. 假设完美的二值化: enter image description here
  2. 运行HoughLinesP enter image description here
  3. (未实施)尝试对检测到的行进行分组
  4. 我使用了这段代码:

        int main()
        {
            cv::Mat image = cv::imread("HoughLinesP_perfect.png");
            cv::Mat gray;
            cv::cvtColor(image,gray,CV_BGR2GRAY);
    
            cv::Mat output; image.copyTo(output);
    
    
    
            cv::Mat g_thres = gray == 0;
    
    
            std::vector<cv::Vec4i> lines;
            //cv::HoughLinesP( binary, lines, 1, 2*CV_PI/180, 100, 100, 50 );
            //  cv::HoughLinesP( h_thres, lines, 1, CV_PI/180, 100, image.cols/2, 10 );
            cv::HoughLinesP( g_thres, lines, 1, CV_PI/(4*180.0), 50, image.cols/20, 10 );
    
            for( size_t i = 0; i < lines.size(); i++ )
            {
                cv::line( output, cv::Point(lines[i][0], lines[i][3]),
                        cv::Point(lines[i][4], lines[i][3]), cv::Scalar(155,255,155), 1, 8 );
            }
    
    
            cv::imshow("g thres", g_thres);
    
            cv::imwrite("HoughLinesP_out.png", output);
    
            cv::resize(output, output, cv::Size(), 0.5,0.5);
    
            cv::namedWindow("output"); cv::imshow("output", output);
    
            cv::waitKey(-1);
    
            std::cout << "finished" << std::endl;
    
            return 0;
    
        }
    

    编辑:

    使用简单的线聚类更新代码(`from minimum_distance函数取自SO):

    给出这个结果:

    enter image description here

    float minimum_distance(cv::Point2f v, cv::Point2f w, cv::Point2f p) {
          // Return minimum distance between line segment vw and point p
          const float l2 = cv::norm(w-v) * cv::norm(w-v);  // i.e. |w-v|^2 -  avoid a sqrt
          if (l2 == 0.0) return cv::norm(p-v);   // v == w case
          // Consider the line extending the segment, parameterized as v + t (w - v).
          // We find projection of point p onto the line.
          // It falls where t = [(p-v) . (w-v)] / |w-v|^2
          //const float t = dot(p - v, w - v) / l2;
          float t = ((p-v).x * (w-v).x + (p-v).y * (w-v).y)/l2;
    
          if (t < 0.0) return cv::norm(p-v);       // Beyond the 'v' end of the segment
          else if (t > 1.0) return cv::norm(p-w);  // Beyond the 'w' end of the segment
          const cv::Point2f projection = v + t * (w - v);  // Projection falls on the segment
          return cv::norm(p - projection);
        }
    
        int main()
        {
            cv::Mat image = cv::imread("HoughLinesP_perfect.png");
            cv::Mat gray;
            cv::cvtColor(image,gray,CV_BGR2GRAY);
    
            cv::Mat output; image.copyTo(output);
    
    
    
            cv::Mat g_thres = gray == 0;
    
    
            std::vector<cv::Vec4i> lines;
            cv::HoughLinesP( g_thres, lines, 1, CV_PI/(4*180.0), 50, image.cols/20, 10 );
    
            float minDist = 100;
    
            std::vector<cv::Vec4i> lines_filtered;
            for( size_t i = 0; i < lines.size(); i++ )
            {
                bool keep = true;
                int overwrite = -1;
                cv::Point2f a(lines[i][0], lines[i][6]);
                cv::Point2f b(lines[i][7], lines[i][3]);
    
                float lengthAB = cv::norm(a-b);
    
    
                for( size_t j = 0; j < lines_filtered.size(); j++ )
                {
                    cv::Point2f c(lines_filtered[j][0], lines_filtered[j][8]);
                    cv::Point2f d(lines_filtered[j][9], lines_filtered[j][3]);
    
                    float distCDA =  minimum_distance(c,d,a);
                    float distCDB =  minimum_distance(c,d,b);
    
                    float lengthCD = cv::norm(c-d);
    
    
                    if((distCDA < minDist) && (distCDB < minDist))
                    {
                        if(lengthCD >= lengthAB)
                        {
                            keep = false;
                        }
                        else
                        {
                            overwrite = j;
                        }
                    }
    
                }
    
                if(keep)
                {
                    if(overwrite >= 0)
                    {
                        lines_filtered[overwrite] = lines[i];
    
                    }
                    else
                    {
                        lines_filtered.push_back(lines[i]);
                    }
                }
            }
    
    
            for( size_t i = 0; i < lines_filtered.size(); i++ )
            {
                cv::line( output, cv::Point(lines_filtered[i][0], lines_filtered[i][10]),
                        cv::Point(lines_filtered[i][11], lines_filtered[i][3]), cv::Scalar(155,255,155), 2, 8 );
            }
    
    
    
    
            cv::imshow("g thres", g_thres);
    
            cv::imwrite("HoughLinesP_out.png", output);
    
            cv::resize(output, output, cv::Size(), 0.5,0.5);
    
            cv::namedWindow("output"); cv::imshow("output", output);
    
            cv::waitKey(-1);
    
            std::cout << "finished" << std::endl;
    
            return 0;
    
        }
    

答案 2 :(得分:0)

你应该试试Hough Line Transform。以下是this website

的示例
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    Mat src = imread("building.jpg", 0);

    Mat dst, cdst;
    Canny(src, dst, 50, 200, 3);
    cvtColor(dst, cdst, CV_GRAY2BGR);

    vector<Vec2f> lines;
    // detect lines
    HoughLines(dst, lines, 1, CV_PI/180, 150, 0, 0 );

    // draw lines
    for( size_t i = 0; i < lines.size(); i++ )
    {
        float rho = lines[i][0], theta = lines[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000*(-b));
        pt1.y = cvRound(y0 + 1000*(a));
        pt2.x = cvRound(x0 - 1000*(-b));
        pt2.y = cvRound(y0 - 1000*(a));
        line( cdst, pt1, pt2, Scalar(0,0,255), 3, CV_AA);
    }

    imshow("source", src);
    imshow("detected lines", cdst);

    waitKey();
    return 0;
}

有了这个,你应该能够调整并获得你正在寻找的礼物(顶点)。