Ignore chained contours in bubble detection

I have an image of a fluid that is being filled in a bottle. I’m happy with the detection of the ‘bubbles’ . But I need to ignore the chained detected bubbles.

import cv2
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage

ROI_LEFT = 200
ROI_RIGHT = 400
ROI_TOP = 200
ROI_BOTTOM = 400

def load_image(image_path, scale_factor=3):
    """Load an image from a file path, convert to grayscale, and resize"""
    image = cv2.imread(image_path, cv2.IMREAD_COLOR)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return gray_image

def crop_image(image, roi_left, roi_right, roi_top, roi_bottom):
    """Crop an image to a region of interest"""
    im_height, im_width = image.shape
    return image[im_height - roi_bottom:im_height - roi_top, roi_left:roi_right]

def resize_image(image, scale_factor):
    """Resize an image"""
    h, w = image.shape
    return cv2.resize(image, (h * scale_factor, w * scale_factor), interpolation=cv2.INTER_AREA)

def apply_gaussian_blur(image, kernel_size):
    """Apply Gaussian blur to reduce noise"""
    return cv2.GaussianBlur(image,  None, sigmaX=20, sigmaY=20)

def threshold_image(image):
    """Apply Otsu's thresholding"""
    _, thresh = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    return thresh

def detect_cells(image):
    """Detect cells using watershed transform"""
    thresh = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    distance_map = ndimage.distance_transform_edt(thresh)
    local_max = peak_local_max(distance_map, indices=False, min_distance=20, labels=thresh)
    markers = ndimage.label(local_max, structure=np.ones((3, 3)))[0]
    labels = watershed(-distance_map, markers, mask=thresh)
    return labels

def calculate_cell_area(labels, image, max_area=10000):
    """Calculate the area of each cell, filter out areas larger than a threshold, and return the total area and number of cells"""
    total_area = 0
    num_cells = 0
    for label in np.unique(labels):
        if label == 0:
            continue
        mask = np.zeros(image.shape, dtype="uint8")
        mask[labels == label] = 255
        cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        cnts = cnts[0] if len(cnts) == 2 else cnts[1]
        for c in cnts:
            area = cv2.contourArea(c)
            if area > max_area:
                continue
            total_area += area
            num_cells += 1
            cv2.drawContours(image, [c], 0, (36,255,12), 2)
    
    return total_area, num_cells

image = load_image('492.png')
img = crop_image(image, ROI_LEFT, ROI_RIGHT, ROI_TOP, ROI_BOTTOM)
img = resize_image(img, 5)

img_blur = apply_gaussian_blur(img, 5)
division = cv2.divide(img, img_blur, scale=255)
thresh = threshold_image(division)

labels = detect_cells(thresh)
total_area = calculate_cell_area(labels, img)

print("Total cell area:", total_area)

fig, ax = plt.subplots(1, 2, figsize=(12, 4))
ax[0].imshow(img, cmap='gray')  # Grayscale image
ax[1].imshow(labels, cmap='bone')  # Labeled image
plt.show()