Search code examples
pythonopencvexternalcontour

OpenCV2_findContours: can i get just one big external contour? (the image has several particles)


i am new with CV2 with python.
i have many images, they have many big and small curvy structures.
and i have to get the biggest big one contour among all.
but i failed.
my codes and images are below...

import cv2 as cv

img_color = cv.imread('ex1.png')
img_gray = cv.cvtColor(img_color, cv.COLOR_BGR2GRAY)
ret, img_binary = cv.threshold(img_gray, 127, 255, 0)
#dbg contours, hierarchy = cv.findContours(img_binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
_, contours, hierarchy = cv.findContours(img_binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

for cnt in contours:
    cv.drawContours(img_color, [cnt], 0, (255, 0, 0), 3)  # blue
#dbg cv.imshow("result", img_color)
#dbg cv.waitKey(0)
cv.imwrite('save_image1.png', img_color)

for cnt in contours:
    hull = cv.convexHull(cnt)
    cv.drawContours(img_color, [hull], 0, (0, 0, 255), 5)
#dbg cv.imshow("result", img_color)
#dbg cv.waitKey(0)
cv.imwrite('save_image2.png', img_color)

example of input image ("ex1.png") is like...
enter image description here
the result output image ("save_image2.png") is like...
enter image description here
...

but, what i want to retrieve is like below... (any of blue or red, i can use them ;)
enter image description here
i mean, the result contour must be just big one which includes everything.

thank you for reading util here(; )


Solution

  • You asked for outline/contour of all regions rather than just the largest. So here is how to do that in Python/OpenCV.

    • Read the input
    • Convert to gray
    • Threshold to binary
    • Get all the points where the value is greater than 0 and transpose (since numpy use y,x convention and OpenCV wants x,y)
    • Compute the convex hull of the points
    • Draw a poly line on a copy of the input
    • Draw a white filled polygon on a black image
    • Get the contour of the white filled polygon
    • Draw the contour on a copy of the input
    • Save results

    Input:

    enter image description here

    import cv2
    import numpy as np
    
    img_color = cv2.imread('ex1.png')
    img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)
    img_binary = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
    
    # get convex hull
    points = np.column_stack(np.where(img_binary.transpose() > 0))
    hull = cv2.convexHull(points)
    
    # draw convex hull on input image in green
    result = img_color.copy()
    cv2.polylines(result, [hull], True, (0,0,255), 2)
    
    # draw white filled hull polygon on black background
    mask = np.zeros_like(img_binary)
    cv2.fillPoly(mask, [hull], 255)
    
    # get the largest contour from result2
    contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    big_contour = max(contours, key=cv2.contourArea)
    
    # draw contour on copy of input
    contr = img_color.copy()
    contr = cv2.drawContours(contr, [big_contour], 0, (0,0,255), 2)
    
    # save result2
    cv2.imwrite('ex1_convex_hull.png', result)
    cv2.imwrite('ex1_convex_hull_contour.png', contr)
    
    # show result2
    cv2.imshow('result', result)
    cv2.imshow('contr', contr)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    Resulting convex hull:

    enter image description here

    Contour of convex hull:

    enter image description here