Unable to find and draw biggest contour

Question:

I want to find and draw biggest contour on this frame.my framee

I can find the contours. but when i try to find the biggest contour, it shows a small contour on frame as a biggest frame. when i just draw contours i can see clearly that there is a big contour around the object
my result

here is my try:

import cv2
import numpy as np
#capture.release()
cv2.destroyAllWindows()
capture = cv2.VideoCapture(1)
panel = np.zeros([100, 700], np.uint8)
cv2.namedWindow('panel')
def nothing(x):
    pass

#some code about trackbar and panel here

while(True):

    ret, frame = capture.read()

    #some code about trackbar and panel here      
    roi = frame[s_r: e_r, s_c: e_c]
    roi =  cv2.GaussianBlur(roi, (5, 5), 0) #sigma = 0
    hsv = cv2.cvtColor( roi, cv2.COLOR_RGB2HSV)    

    #some code about trackbar and panel here


    mask = cv2.inRange(hsv, lower_green, upper_green)
    mask_inv = cv2.bitwise_not(mask)

    bg = cv2.bitwise_and( roi,  roi, mask=mask)
    fg = cv2.bitwise_and( roi,  roi, mask=mask_inv)
    gray = cv2.cvtColor(fg, cv2.COLOR_BGR2GRAY)
    contours, hierarchy = cv2.findContours(gray,cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    max_area = 0
    for contour in contours:
        area = cv2.contourArea(contour)
        if area > max_area:
            area = max_area
            x,y,w,h = cv2.boundingRect(contour)

    cv2.rectangle(fg,(x,y),(x+w,y+h),(0,255,0),2)

    cv2.imshow('bg', bg)
    cv2.imshow('fg', fg)
    cv2.imshow('panel', panel)

    if cv2.waitKey(30) == 27: #siradaki frame'e gecmeden once 30 ms bekle
        break

capture.release()
cv2.destroyAllWindows()
Asked By: beyblade41

||

Answers:

Python/OpenCV solution:

In your loop over contours, simply do your own finding of the largest area and corresponding boundingRect of that contour.

max_area = 0
for contour in contours:
   area = cv2.contourArea(contour)
   if area > max_area:
      area = max_area
      x,y,w,h = cv2.boundingRect(contour)

Alternately, just get the outer contour (presumably only one), if that is relevant to your image. (In the future, please show your input image).

contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

Answered By: fmw42

This method seems to work for me in Python/OpenCV. I do a simple threshold. Then use morphology to smooth and fill in some. Then I get the contours and filter on area. Then I draw the contour on the image. Note that it is sensitive to the size of the morphology kernel and needs to be at least 10 or 11. But if you make it too big, it will change the shape of the region.

Input:

enter image description here

import cv2
import numpy as np

# load image
img = cv2.imread("green_and_red_regions.png")

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold image
thresh = cv2.threshold(gray,4,255,0)[1]

# apply morphology open to smooth the outline
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# find contours
cntrs = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]

# Contour filtering to get largest area
area_thresh = 0
for c in cntrs:
    area = cv2.contourArea(c)
    if area > area_thresh:
        area = area_thresh
        big_contour = c

# draw the contour on a copy of the input image
results = img.copy()
cv2.drawContours(results,[big_contour],0,(0,0,255),2)


# write result to disk
cv2.imwrite("greengreen_and_red_regions_threshold.png", thresh)
cv2.imwrite("green_and_red_regions_big_contour.png", results)

cv2.imshow("THRESH", thresh)
cv2.imshow("RESULTS", results)
cv2.waitKey(0)
cv2.destroyAllWindows()

Thresholded and Smoothed Image:

enter image description here

Contour Outline Drawn On Input:

enter image description here

Answered By: fmw42