You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于OpenCV实现仔猪跨线计数以配合体重测算的方案优化问询

仔猪进出区域计数准确率优化需求

背景说明

我需要统计进出指定区域的仔猪数量,项目中该区域下方设有称重设备可采集动物总重量:最终目标是获取单头仔猪体重,当区域内仔猪数为0时可得到秤台自重,后续可结合进入区域的仔猪总数量换算单头仔猪的平均体重。
体重相关的历史数据统计为后续规划功能,当前核心需求是解决仔猪计数环节的准确率问题。

测试场景说明

现有测试视频中,00:40到02:00为仔猪入场阶段,03:54之后仔猪开始频繁进出该区域,该状态持续至视频结束。

现有方案与问题

我已通过代码实现了入场阶段的仔猪计数:定义了极小的感兴趣区域(ROI),通过颜色阈值过滤仔猪目标,该方案在仔猪活跃度较低的入场阶段运行正常,但当仔猪开始频繁活动、反复进出区域时计数准确度大幅下降。
目前暂无进一步的优化思路,恳请各位提供可行的技术建议。

现有实现代码

import cv2

FULL_VIDEO_PATH = "完整视频的存储路径"

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):
    # 复制待过滤的感兴趣区域(ROI)
    ROI = (frame[80:310, 615:620]).copy()

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

    # 在产生噪声的结构上绘制绿色矩形进行遮挡
    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)

    # 根据颜色阈值过滤图像
    segmented_line = cv2.inRange(dilation, MIN_COLOR, MAX_COLOR)

    # 调整分割结果尺寸仅用于展示
    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:
        # 计数窗口区间
        count_window_interval = (615, 0, 620, 400)

        # 过滤帧得到前景掩码
        fg_mask = filter_mask(frame)

        # 在帧上绘制计数触发线
        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 len(contours) == 0:
            frames_not_seen += 1
            # 若5帧内未检测到轮廓,重置上一个中心坐标为0,确保新目标出现时可正常计算位置差
            if frames_not_seen > 5:
                last_center = 0

        for c in contours:
            frames_not_seen = 0

            # 获取轮廓外接矩形坐标
            (x, y, w, h) = cv2.boundingRect(c)

            # 计算外接矩形的中心点
            centroid = get_centroid(x, y, w, h)

            # 计算轮廓矩用于获取轮廓面积
            moments = cv2.moments(c)

            # 获取轮廓面积
            size = moments['m00']

            # 累计当前仔猪的轮廓面积,直到完成计数
            if not already_counted:
                total_size += size

            # 若当前中心与上一个目标中心的差值超过80,判定为新仔猪进入计数区,位置校验通过,重置计数状态避免噪声干扰
            if abs(last_center - centroid[1]) > 80:
                is_position_ok = True
                already_counted = False

            # 通过面积阈值校验轮廓是否符合仔猪特征,阈值为实验测定值
            if 1300 < total_size < 5500:
                is_size_ok = True

            # 所有校验条件满足时完成仔猪计数,重置所有状态位
            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'仔猪数量: {total_pigs}', (100, 370) , cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,0), 2)

        cv2.imshow('帧画面', frame_with_line)

        cv2.moveWindow('ROI', 1130, 0)
        cv2.moveWindow('帧画面', 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()

内容的提问来源于stack exchange,提问作者Arthur Rodolfo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 07:06:02