如何在OpenCV 2.4.2中绘制直线而不是线段

11

问题已经说得很清楚了,

我知道函数Line(),它可以在两个点之间绘制线段。

我需要绘制直线而不是线段,也使用线段的两个点。


[中文:从先前发布的答案进行编辑]

我尝试了您提供的解决方案,在水平线上表现良好,但在竖直线上仍然存在问题。

例如,以下是使用点[306,411]和[304,8](紫色)以及绘制的线(红色)的示例,在600x600像素的图像上。你有什么建议吗?

enter image description here

4个回答

10

我看到这是一个相当久远的问题。我曾经遇到完全相同的问题,并使用了这个简单的代码:

double Slope(int x0, int y0, int x1, int y1){
     return (double)(y1-y0)/(x1-x0);
}

void fullLine(cv::Mat *img, cv::Point a, cv::Point b, cv::Scalar color){
     double slope = Slope(a.x, a.y, b.x, b.y);

     Point p(0,0), q(img->cols,img->rows);

     p.y = -(a.x - p.x) * slope + a.y;
     q.y = -(b.x - q.x) * slope + b.y;

     line(*img,p,q,color,1,8,0);
}

首先,我计算出线段的斜率,然后将该线段“延长”至图像的边界。我计算直线在x=0和x=image.width处的新点。这些点有可能在图像外,这有点棘手,但解决方法非常简单。


1
这是openCV的正确答案。选中的答案是垃圾。我复制粘贴了这个,它可以工作。非常感谢。 - Kenyakorn Ketsombut
3
若 x1-x0 = 0,则会崩溃。 - John Shedletsky
很棒的概念。已转换为Python并回答了一个问题https://stackoverflow.com/a/59579392/9605907。非常感谢!! - Sreekiran A R

6

您需要编写一个函数来完成此操作。我建议您将您的线路以ax+by+c=0的形式表示,然后与图像的4个边相交。请注意,如果您有一个形如[a b c]的线路,则找到它与另一条线的交点只需将两者进行叉积即可。图像的边缘将是:

top_horizontal =    [0 1 0];
left_vertical   =   [1 0 0];
bottom_horizontal = [0 1 -image.rows];
right_vertical =    [1 0 -image.cols];

此外,如果您知道点之间的距离,您也可以沿着每个方向选择非常远的点,我认为传递给Line()的点不需要在图像上。

5
我遇到了同样的问题,后来发现在2.4.X版本的OpenCV中存在一个已经修复的已知bug
对于2.4.X版本,解决方法是在使用cv::clipLine()绘制线条之前将其剪辑。
这里有一个我自己编写的函数,在2.4.13 OpenCV上运行良好。
void Detector::drawFullImageLine(cv::Mat& img, const std::pair<cv::Point, cv::Point>& points, cv::Scalar color)
{
    //points of line segment
    cv::Point p1 = points.first;
    cv::Point p2 = points.second;

    //points of line segment which extend the segment P1-P2 to 
    //the image borders.
    cv::Point p,q;

    //test if line is vertical, otherwise computes line equation
    //y = ax + b
    if (p2.x == p1.x)
    {
        p = cv::Point(p1.x, 0); 
        q = cv::Point(p1.x, img.rows); 
    }
    else
    {
        double a = (double)(p2.y - p1.y) / (double) (p2.x - p1.x);
        double b =  p1.y - a*p1.x;

        p = cv::Point(0, b);
        q = cv::Point(img.rows, a*img.rows + b);

        //clipline to the image borders. It prevents a known bug on OpenCV
        //versions 2.4.X when drawing 
        cv::clipLine(cv::Size(img.rows, img.cols), p, q);
    }

    cv::line(img, p, q, color, 2);
}

cv::clipLine() 是 OpenCV 的正确解决方案。这应该被接受为答案。非常感谢。 - Arxeiss
这是最完整和可靠的答案。 - GntS
这个程序相关的内容应该翻译为:这个 q = cv::Point(img.rows, a*img.rows + b); 应该修改为 q = cv::Point(img.cols, a*img.cols + b); 吗? - GntS

2
这个答案是从pajus_cz的答案中分叉出来,并稍作改进。
我们有两个点,需要得到直线方程 y = mx + b ,以便绘制直线。
我们需要获取两个变量:
1- 斜率(m)
2- 我们可以在计算出斜率后使用已知的任意两个点中的任意一个来检索b,通过直线方程b = y - mx
请注意,保留 HTML 标记。
void drawStraightLine(cv::Mat *img, cv::Point2f p1, cv::Point2f p2, cv::Scalar color)
{
        Point2f p, q;
        // Check if the line is a vertical line because vertical lines don't have slope
        if (p1.x != p2.x)
        {
                p.x = 0;
                q.x = img->cols;
                // Slope equation (y1 - y2) / (x1 - x2)
                float m = (p1.y - p2.y) / (p1.x - p2.x);
                // Line equation:  y = mx + b
                float b = p1.y - (m * p1.x);
                p.y = m * p.x + b;
                q.y = m * q.x + b;
        }
        else
        {
                p.x = q.x = p2.x;
                p.y = 0;
                q.y = img->rows;
        }

        cv::line(*img, p, q, color, 1);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接