How to fix this python code to count duplicate sample in the images?
04:49 21 Oct 2025

I want to count samples in the image and measure the length of each sample as I show below. But I am facing a big problem that when sample is overlapping it cannot make an accurate count, for example sample count should actually be 2 but its overlapped so the result goes to 4. What should I do? Or can someone please fix my code?

Here is my code:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# === Load and preprocess image ===
image = cv2.imread('GF sample/4.jpg')  # ⬅️ Load input image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # Convert to grayscale
blur = cv2.GaussianBlur(gray, (11, 11), 0)      # Apply Gaussian blur to reduce noise
canny = cv2.Canny(blur, 10, 150, 3)              # Edge detection using Canny
dilated = cv2.dilate(canny, (1, 1), iterations=0)  # Slightly enhance edges

# === Image dimensions ===
height, width = gray.shape

# === Find external contours ===
cnts, hierarchy = cv2.findContours(
    dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# === Calibration (based on your scale)
MICRONS_PER_PIXEL = 1.585  # ⬅️ Adjust based on scale bar (1000 µm = 631 px)

# === Filtering thresholds
MIN_AREA = 1                # Eliminate tiny specks
MIN_WIDTH_HEIGHT = 15       # Filter very small bounding boxes
MARGIN = 5                 # Avoid counting fibers near the image border

# === Output image and result storage
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
font = cv2.FONT_HERSHEY_SIMPLEX
filtered_cnt = []
fiber_lengths_um = []

# === Loop through contours and apply filters
for c in cnts:
    area = cv2.contourArea(c)
    x, y, w, h = cv2.boundingRect(c)

    if (area > MIN_AREA and
        w > MIN_WIDTH_HEIGHT and h > MIN_WIDTH_HEIGHT and
        x > MARGIN and y > MARGIN and
        (x + w) < (width - MARGIN) and
        (y + h) < (height - MARGIN)):

        # Keep only filtered contours
        filtered_cnt.append(c)

        # Estimate length using minAreaRect
        rect = cv2.minAreaRect(c)
        (w_rect, h_rect) = rect[1]
        length_px = max(w_rect, h_rect)
        real_length_um = length_px * MICRONS_PER_PIXEL
        fiber_lengths_um.append(real_length_um)

# === Label and draw only the accepted contours ===
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.5
thickness = 1
color = (255, 0, 0)  # red

for i, c in enumerate(filtered_cnt, 1):
    M = cv2.moments(c)
    if M["m00"] != 0:
        cx = int(M["m10"] / M["m00"])
        cy = int(M["m01"] / M["m00"])

        # Draw contour
        cv2.drawContours(rgb, [c], -1, (0, 255, 0), 1)

        # Get label text size
        label = str(i)
        (text_width, text_height), baseline = cv2.getTextSize(label, font, font_scale, thickness)

        # Shift to center
        text_x = int(cx - text_width / 2)
        text_y = int(cy + text_height / 2)

        # Draw label centered
        cv2.putText(rgb, label, (text_x, text_y), font, font_scale, color, thickness)


# === Show results ===
plt.figure(figsize=(14, 10))
plt.imshow(rgb)
plt.title(f" Counted Glass Fibers: {len(filtered_cnt)}")
plt.axis('off')
plt.show()

# === Print lengths of fibers ===
print("\n📏 List of Detected Fibers (µm):")
for idx, length in enumerate(fiber_lengths_um, 1):
    print(f"{idx}. {length:.2f} µm")

# === Summary statistics ===
print(f"\n📊 Total detected: {len(filtered_cnt)} fibers")
print(f"📉 Min: {min(fiber_lengths_um):.2f} µm")
print(f"📈 Max: {max(fiber_lengths_um):.2f} µm")
print(f"📌 Average: {np.mean(fiber_lengths_um):.2f} µm")

my result

enter image description here

Original image

enter image description here

python image-processing computer-vision