OpenCV检测轮廓交点

15
我有两个轮廓A和B,我想检查它们是否相交。A 和 B 都是 cv::Point 类型的向量,并且大小不同
为了检查相交,我尝试使用 bitwise_and。但因为输入的大小不同而引发异常。我该怎么解决这个问题?
编辑:
附加的图像应该能更好地说明问题。小车由蓝色轮廓跟踪,障碍物由粉色轮廓表示。我需要检查它们是否相交。 enter image description here
4个回答

15

一个简单但也许不是最有效的方法是使用drawContours创建两个图像:一个包含汽车轮廓,另一个包含障碍物轮廓。

然后将它们and在一起,任何仍然为正的点都将成为交点。

一些伪代码(我使用Python接口,所以无法正确使用C++语法,但应该足够简单,您可以进行转换):

import numpy as np # just for matrix manipulation, C/C++ use cv::Mat
# find contours.
contours,h = findContours( img, mode=RETR_LIST, method=CHAIN_APPROX_SIMPLE )
# Suppose this has the contours of just the car and the obstacle.

# create an image filled with zeros, single-channel, same size as img.
blank = np.zeros( img.shape[0:2] )

# copy each of the contours (assuming there's just two) to its own image. 
# Just fill with a '1'.
img1 = drawContours( blank.copy(), contours, 0, 1 )
img2 = drawContours( blank.copy(), contours, 1, 1 )

# now AND the two together
intersection = np.logical_and( img1, img2 )

# OR we could just add img1 to img2 and pick all points that sum to 2 (1+1=2):
intersection2 = (img1+img2)==2
如果我查看intersection,我将得到一个图像,在轮廓相交处为1,在其他地方为0。
或者您可以使用drawContours(blank.copy(),contours,0,1,thickness=-1)填充整个轮廓(不仅是轮廓而且还要填充内部),然后intersection图像将包含轮廓之间的交集区域。

当使用imshow()来显示交集时,我们如何查看它?在使用imshow()来显示交集时,会得到一张黑色的图像。但是当打印交集时,可以观察到0和1。有没有什么方法来可视化交集呢? - Sai krishna
1
@Saikrishna 使用灰度值。如果是为了可视化,请在20处画轮廓……这是可见但没有饱和的。交叉点将更亮(在40或更多)。 - Shravya Boggarapu
我认为在仅绘制轮廓边界时,使用4连通线而不是8连通线是必要的,因为否则可能会错过某些具有特定交角的两条对角线的交点。 - user202729
此外,第一种解决方案无法检测到一个轮廓包含另一个轮廓的情况。 - user202729
drawContours 只填充轮廓,不是整个区域。这样会导致轮廓之间的交叉。因此,我必须添加 cv2.FILLED 作为参数:img2 = cv2.drawContours(blank.copy(), contours, 0, 1, cv2.FILLED) - Jeppe

0

如果您先对向量进行排序,使用几乎任何一致的排序标准,然后您可以直接在向量上使用 std::set_intersection。如果轮廓相对于图像大小较短,则这可能比已接受答案更快。


1
这对于“压缩”的轮廓是错误的,即使它们没有共同的顶点,两个轮廓也可能相交。 - user202729
@user202729 是的。对于非压缩的8连通轮廓也是如此。但对于4连通轮廓则不然。 - Reunanen

0

我发现Clipper库在这种情况下非常有用。(将cv::Point向量转换为Clipper Path对象非常简单。)


0

基于 mathematical.coffee 的答案,测试过的 C++ 代码:

    vector< Point> merge_contours(vector <Point>& contour1, vector <Point>& contour2, int type){
    // get work area        
    Rect work_area = boundingRect( contour1 ) | boundingRect( contour2 );        
    
    Mat merged = Mat::zeros(work_area.size(), CV_8UC1);        
    Mat contour1_im = Mat::zeros(work_area.size(), CV_8UC1);
    Mat contour2_im = Mat::zeros(work_area.size(), CV_8UC1);
    
    //draw
    vector<vector<Point> > shifted1;
    shifted1.push_back(shift_contour(contour1, work_area.tl()));
    drawContours( contour1_im, shifted1, -1, 255, -1);
    vector<vector<Point> > shifted2;
    shifted2.push_back(shift_contour(contour2, work_area.tl()));
    drawContours( contour2_im, shifted2, -1, 255, -1);
    
    //imshow("contour1 debug", contour1_im);
    //imshow("contour2 debug", contour2_im);        
    
    if( type == 0 )
        // intersect
        bitwise_or( contour1_im, contour2_im, merged);
    else
        // unite
        bitwise_and( contour1_im, contour2_im, merged);        
    //imshow("merge contour debug", merged);
    
    // find
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours(merged,contours,hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
    
    if(contours.size() > 1){
        printf("Warn: merge_contours has output of more than one contours.");
    }
    
    return shift_contour(contours[0], work_area.tl() * -1);        
}

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