Search code examples
pythonopencvcontour

Check if two contours intersect?


I have 2 contours (cont1 and cont2) received from cv2.findContours(). How do I know if they intersect or not? I don't need coordinates, I only need a boolean True or False.

I have attempted different ways and already tried to do a check with

if ((cont1 & cont2).area() > 0):

... but got the error that the array has no method "Area()"

...
cont1array = cv2.findContours(binary1, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]
cont2array = cv2.findContours(binary2, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]
...

for cont1 in cont1array:
  for cont2 in cont2array:
    print("cont1")
    print(cont1)
    print(type(cont1))
    print("cont2")
    print(cont2)
    print(type(cont2))
>   if cont1 and cont2 intersect: #i dont know how check intersect
      print("yes they intersect")
    else:
      print("no they do not intersect")

# cont1
# [[172 302]
#  [261 301]
#  [262 390]
#  [173 391]]
# <class 'numpy.ndarray'>
# cont2
# [[  0   0]
#  [  0 699]
#  [499 699]
#  [499   0]]
# <class 'numpy.ndarray'>

Solution

  • Once you have the two contours from cv2.findContours(), you can use a bitwise AND operation to detect intersection. Specifically, we can use np.logical_and(). The idea is to create two separate images for each contour and then use the logical AND operation on them. Any points that have a positive value (1 or True) will be points of intersection. So since you're only looking to obtain a boolean value of whether there is intersection, we can check the intersected image to see if there is a single positive value. Essentially, if the entire array is False then there was no intersection between the contours. But if there is a single True, then the contours touched and thus intersect.

    def contourIntersect(original_image, contour1, contour2):
        # Two separate contours trying to check intersection on
        contours = [contour1, contour2]
    
        # Create image filled with zeros the same size of original image
        blank = np.zeros(original_image.shape[0:2])
    
        # Copy each contour into its own image and fill it with '1'
        image1 = cv2.drawContours(blank.copy(), [contours[0]], 0, 1)
        image2 = cv2.drawContours(blank.copy(), [contours[1]], 1, 1)
        
        # Use the logical AND operation on the two images
        # Since the two images had bitwise and applied to it,
        # there should be a '1' or 'True' where there was intersection
        # and a '0' or 'False' where it didnt intersect
        intersection = np.logical_and(image1, image2)
        
        # Check if there was a '1' in the intersection
        return intersection.any()
    

    Example

    Original Image

    enter image description here

    Detected Contour

    enter image description here

    We now pass the two detected contours to the function and obtain this intersection array:

    [[False False False ... False False False]
     [False False False ... False False False]
     [False False False ... False False False]
     ...
     [False False False ... False False False]
     [False False False ... False False False]
     [False False False ... False False False]]
    

    We check the intersection array to see if True exists. We will obtain a True or 1 where the contours intersect and False or 0 where they do not.

    return intersection.any()
    

    Thus we obtain

    False

    Full code

    import cv2
    import numpy as np
    
    def contourIntersect(original_image, contour1, contour2):
        # Two separate contours trying to check intersection on
        contours = [contour1, contour2]
    
        # Create image filled with zeros the same size of original image
        blank = np.zeros(original_image.shape[0:2])
    
        # Copy each contour into its own image and fill it with '1'
        image1 = cv2.drawContours(blank.copy(), contours, 0, 1)
        image2 = cv2.drawContours(blank.copy(), contours, 1, 1)
        
        # Use the logical AND operation on the two images
        # Since the two images had bitwise AND applied to it,
        # there should be a '1' or 'True' where there was intersection
        # and a '0' or 'False' where it didnt intersect
        intersection = np.logical_and(image1, image2)
        
        # Check if there was a '1' in the intersection array
        return intersection.any()
    
    original_image = cv2.imread("base.png")
    image = original_image.copy()
    
    cv2.imshow("original", image)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv2.imshow("gray", gray)
    blurred = cv2.GaussianBlur(gray, (5,5), 0)
    cv2.imshow("blur", blurred)
    threshold = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
    cv2.imshow("thresh", threshold)
    
    contours = cv2.findContours(threshold.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # Depending on OpenCV version, number of arguments return by cv.findContours 
    # is either 2 or 3
    contours = contours[1] if len(contours) == 3 else contours[0]
    
    contour_list = []
    for c in contours:
        contour_list.append(c)
        cv2.drawContours(image, [c], 0, (0,255,0), 2)
    
    print(contourIntersect(original_image, contour_list[0], contour_list[1]))
    cv2.imshow("contour", image)
    cv2.waitKey(0)