【问题标题】:Approximate photo of a simple drawing using lines使用线条的简单绘图的近似照片
【发布时间】:2014-06-04 23:22:10
【问题描述】:

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

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

start end(一行的开始和结束的坐标)
1. (0,0); (0,10)(垂直线)
2. (0,10); (15, 15)
3. (15,15); (0, 20)
4.(0,20); (0,30)

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

【问题讨论】:

    标签: opencv line detection approximation


    【解决方案1】:

    试试这个,

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

    2. Find contours.

    3. approxPolyDP 用于找到的轮廓。

    查看一些参考资料:

    【讨论】:

    • 谢谢!这实际上可能是它。我会检查一下并稍后发布我的结果:)
    • 我按照你的建议做了,这正是我所需要的。再次感谢:)
    【解决方案2】:

    也许你可以做这个。

    1. 假设一个完美的二值化:
    2. 运行 HoughLinesP
    3. (未实现)尝试对检测到的行进行分组

    我使用了这个代码:

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

    编辑:

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

    给出这个结果:

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

    【讨论】:

    • 谢谢。这是一种非常有趣的方法,我认为它实际上可以工作。但是,对于我的情况,这种分组可能在计算上过于昂贵。
    • 然后也许尝试一些细化并尝试 HoughLinesP 而不是 HoughLines,因为它更适合不完美的线条。但是,您可能只想选择最长的“相似”行,而不是分组,我猜这并不是计算成本高。
    • 是的,但您仍然必须创建“相似”行的组,所以它变得更加复杂。我会尝试使用 Haris 提出的方法,但我会记住你的。
    • @user3704596 用一个简单的类似行聚类编辑了我的答案,只是为了完成
    • 感谢您的努力!我实际上检查了您的解决方案,它确实工作得很好。不过,我决定选择另一个,因为它立即为我提供了近似线,这使我可以轻松计算相邻线之间的角度。谢谢!
    【解决方案3】:

    你应该试试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;
    }
    

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

    【讨论】:

    • 感谢您的意见。我决定试试你的想法,但正如我担心的那样,它似乎不合适。调整一些参数后,您可以在这里看到我的结果:dropbox.com/s/kmlge9eyse0fc8f/symbols.png;我先用 otsu 阈值对其进行二值化,但它似乎不影响结果。我猜的主要问题是因为边缘我必须平行线,但即使不是这种情况,它也会检测最直的线 - 调整参数可能会有所帮助,但它不是通用的。还有其他想法吗?
    猜你喜欢
    • 1970-01-01
    • 2018-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多