OpenCV Contour and Shape Analysis

imagePath = 'shapes.jpg'
image = cv2.imread(imagePath)
# Convert to grayscale
imageGray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

# Display image
plt.imshow(imageGray);

# Find all contours in the image.
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# Number of contours.
print("Number of contours found = {}".format(len(contours)))

# Hierarchy.
print("\nHierarchy : \n{}".format(hierarchy))

# Create a copy of the original image.
imageCopy1 = image.copy()
# Draw all the contours.
cv2.drawContours(imageCopy1, contours, -1, (0,0,255), 3)
plt.imshow(imageCopy1[:,:,::-1]);
source = './intruder_2.mp4'  # Or specify 'source' as the index associated with your camera system.

video_cap_2 = cv2.VideoCapture(source)
if not video_cap_2.isOpened():
    print('Unable to open: ' + source)

frame_w = int(video_cap_2.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_h = int(video_cap_2.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(video_cap_2.get(cv2.CAP_PROP_FPS))

size = (frame_w, frame_h)
frame_area = frame_w * frame_h

video_out_alert_file_2 = 'video_out_alert_2.mp4'
video_out_alert_2 = cv2.VideoWriter(video_out_alert_file_2, cv2.VideoWriter_fourcc(*'XVID'), fps, size)

bg_sub = cv2.createBackgroundSubtractorKNN(history=200)

ksize = (5, 5)        # Kernel size for erosion.
max_contours = 3      # Number of contours to use for rendering a bounding rectangle.
frame_count = 0
min_contour_area_thresh = 0.01 # Minimum fraction of frame required for maximum contour.

yellow = (0, 255, 255)
red = (0, 0, 255)

# Process video frames.
while True:
    
    ret, frame = video_cap_2.read()
    frame_count += 1
    if frame is None:
        break
    
    # Stage 1: Create a foreground mask for the current frame.
    fg_mask = bg_sub.apply(frame)

    # Stage 2: Stage 1 + Erosion.
    fg_mask_erode = cv2.erode(fg_mask, np.ones(ksize, np.uint8))

    # Stage 3: Stage 2 + Contours.
    contours_erode, hierarchy = cv2.findContours(fg_mask_erode, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    if len(contours_erode) > 0:

        # Sort contours based on area.
        contours_sorted = sorted(contours_erode, key = cv2.contourArea, reverse=True)
        
        # Contour area of largest contour.
        contour_area_max = cv2.contourArea(contours_sorted[0])
        
        # Compute fraction of total frame area occupied by largest contour.
        contour_frac = contour_area_max / frame_area
        
        # Confirm contour_frac is greater than min_contour_area_thresh threshold.
        if contour_frac > min_contour_area_thresh:
            
            # Compute bounding rectangle for the top N largest contours.
            for idx in range(min(max_contours, len(contours_sorted))):
                xc, yc, wc, hc = cv2.boundingRect(contours_sorted[idx])
                if idx == 0:
                    x1 = xc
                    y1 = yc
                    x2 = xc + wc
                    y2 = yc + hc
                else:
                    x1 = min(x1, xc)
                    y1 = min(y1, yc)
                    x2 = max(x2, xc + wc)
                    y2 = max(y2, yc + hc)

            # Draw bounding rectangle for top N contours on output frame.
            cv2.rectangle(frame, (x1, y1), (x2, y2), yellow, thickness = 2)
            drawBannerText(frame, 'Intrusion Alert', text_color = red)
            
            # Write alert video to file system. 
            video_out_alert_2.write(frame)

video_cap_2.release()
video_out_alert_2.release()

from moviepy.editor import VideoFileClip

# Load output video.
clip = VideoFileClip(video_out_alert_file_2)
clip.ipython_display(width = 1000)

References