Pigs counting when crossing a line using OpenCV

I’m trying to count the number of piglets that enter and leave a zone. This is important because, in my project, there is a balance underneath the zone that computes the weight of the animals. My goal is to find the pig’s weight, so, to achieve that, I will count the number of piglets that enter the zone, and if this number is zero, I have the pig’s weight, and according to the number of piglets that get in I will calculate the weight of each as well.

But the weight history is for the future. Currently, I need help in the counting process.

The video can be seen here. The entrance occurs from the minute 00:40 until 02:00 and the exit starts on the minute 03:54 and goes all the way through the video because the piglets start, at this point, to enter and exit the zone.

I’ve successfully counted the entrance with the code below. I defined a region of interest, very small, and filter the pigs according to their colors. It works fine until the piglets start to move around and get very active, leaving and entering the zone all the time.

I’m out of ideas to proceed with this challenge. If you have any suggestions, please, tell me!

Thanks!!

import cv2

FULL_VIDEO_PATH = "PATH TO THE FULL VIDEO"

MAX_COLOR = (225, 215, 219)
MIN_COLOR = (158, 141, 148)

def get_centroid(x, y, w, h):
    x1 = int(w / 2)
    y1 = int(h / 2)

    cx = x + x1
    cy = y + y1

    return cx, cy

def filter_mask(frame):
    # create a copy from the ROI to be filtered
    ROI = (frame[80:310, 615:620]).copy()

    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))

    # create a green rectangle on the structure that creates noise
    thicker_line_filtered = cv2.rectangle(ROI, (400, 135), (0, 165), (20, 200, 20), -1)

    closing = cv2.morphologyEx(thicker_line_filtered, cv2.MORPH_CLOSE, kernel)
    opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)
    dilation = cv2.dilate(opening, kernel, iterations=2)

    # Filter the image according to the colors
    segmented_line = cv2.inRange(dilation, MIN_COLOR, MAX_COLOR)

    # Resize segmented line only for plot
    copy = cv2.resize(segmented_line, (200, 400))

    cv2.imshow('ROI', copy)

    return segmented_line

def count_pigs():
    cap = cv2.VideoCapture(FULL_VIDEO_PATH)

    ret, frame = cap.read()

    total_pigs = 0
    frames_not_seen = 0
    last_center = 0
    is_position_ok = False
    is_size_ok = False
    total_size = 0
    already_counted = False


    while ret:
        # Window interval used for counting
        count_window_interval = (615, 0, 620, 400)

        # Filter frame
        fg_mask = filter_mask(frame)

        # Draw a line on the frame, which represents when the pigs will be counted
        frame_with_line = cv2.line(frame, count_window_interval[0:2], count_window_interval[2:4],(0,0,255), 1)

        contours, _ = cv2.findContours(fg_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

        # If no contour is found, increments the variable
        if len(contours) == 0:
            frames_not_seen += 1
            # If no contours are found within 5 frames, set last_center to 0 to generate the position difference when
            # a new counter is found.
            if frames_not_seen > 5:
                last_center = 0

        for c in contours:
            frames_not_seen = 0

            # Find the contour coordinates
            (x, y, w, h) = cv2.boundingRect(c)

            # Calculate the rectangle's center
            centroid = get_centroid(x, y, w, h)

            # Get the moments from the contour to calculate its size
            moments = cv2.moments(c)

            # Get contour's size
            size = moments['m00']

            # Sum the size until count the current pig
            if not already_counted:
                total_size += size

            # If the difference between the last center and the current one is bigger than 80 - which means a new pig
            # enter the counting zone - set the position ok and set the already_counted to False to mitigate noises
            # with significant differences to be counted
            if abs(last_center - centroid[1]) > 80:
                is_position_ok = True
                already_counted = False

            # Imposes limits to the size to evaluate if the contour is consistent
            # Min and Max value determined experimentally
            if 1300 < total_size < 5500:
                is_size_ok = True

            # If all conditions are True, count the pig and reset all of them.
            if is_position_ok and is_size_ok and not already_counted:
                is_position_ok = False
                is_size_ok = False
                already_counted = True
                total_size = 0
                total_pigs += 1

            last_center = centroid[1]

        frame_with_line = cv2.putText(frame_with_line, f'Pigs: {total_pigs}', (100, 370) , cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,0), 2)

        cv2.imshow('Frame', frame_with_line)

        cv2.moveWindow('ROI', 1130, 0)
        cv2.moveWindow('Frame', 0, 0)

        k = cv2.waitKey(15) & 0xff
        if k == 27:
            break
        elif k == 32:
            cv2.waitKey() & 0xff

        ret, frame = cap.read()

    cv2.destroyAllWindows()
    cap.release()


if __name__ == '__main__':
    count_pigs()

hysteresis.

give your boundary some width. inside that boundary, no subject changes its state. the state changes if the subject leaves the boundary one way or another.

that requires that you track your subjects, give them (temporary) identity, so you can associate information from the past with a detection in the current frame.

“nearest neighbor” assignment is a good start. requires some thresholds so you know when to create and destroy identities/tracks.

  • when a detection is truly new, you detect that because no known objects are near it.
  • when a known object disappears, no detection as been associated to it for several frames.

detections are noisy. blobs can split and merge.

1 Like

related: numpy - Pigs counting when crossing a line using OpenCV - Stack Overflow

Thanks, crackwitz for the suggestion! I will work on it.

Do you have any suggestions on how to process the image? I tried to filter using the piglet’s color but sometimes it might be tricky.

AI is always a good answer.

browse the dnn tag in this forum. there is mention of a “U net” that appears to be easy to train, but I wouldn’t know.

1 Like