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

OpenCV人员计数功能失效:Python转Java后无法检测人员

排查Java版OpenCV人员计数无法检测的问题

我帮你梳理了代码里的几个关键错误,这些应该是导致人员无法被检测的核心原因:

1. 视频读取与宽高获取的低级错误

你获取视频高度时误用了cap.get(3)(这是OpenCV中获取帧宽度的参数),正确的帧高度应该用cap.get(4)。这个错误会让所有基于高度计算的阈值(计数线位置、面积阈值)全部失效,相当于整个计数逻辑的基础都错了。另外资源路径也可能存在编码问题,导致视频无法正常读取。

修正代码:

// 处理资源路径,避免URL编码问题
String videoPath = new File(getClass().getResource("/peopleCounter.avi").toURI()).getAbsolutePath();
VideoCapture cap = new VideoCapture(videoPath);

// 先校验视频是否成功打开
if (!cap.isOpened()) {
    System.err.println("无法加载视频文件,请检查路径!");
    return;
}

// 正确获取帧宽高
double width = cap.get(Videoio.CAP_PROP_FRAME_WIDTH);
double height = cap.get(Videoio.CAP_PROP_FRAME_HEIGHT);

2. 前景提取与形态学操作逻辑混乱

你的代码对前景掩码的处理顺序和目标变量完全搞错了:先对fgMask二值化后存到output,但后续形态学操作又用原始fgMask作为输入,最后找轮廓还是用未处理的fgMask——相当于完全没做噪声过滤,自然检测不到有效轮廓。

修正代码:

Mat fgMask = new Mat();
// 学习率建议用-1让算法自动调整,0.1太大容易导致背景更新过快,丢失前景
mog2.apply(frame, fgMask, -1);

// 直接在fgMask上做二值化和形态学操作,过滤噪声
Imgproc.threshold(fgMask, fgMask, 200, 255, Imgproc.THRESH_BINARY);
// 用getStructuringElement创建核,比手动创建更规范
Mat kernelOp = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
Mat kernelCl = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(11, 11));
Imgproc.morphologyEx(fgMask, fgMask, Imgproc.MORPH_OPEN, kernelOp);
Imgproc.morphologyEx(fgMask, fgMask, Imgproc.MORPH_CLOSE, kernelCl);

// 用处理后的fgMask提取轮廓
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(fgMask, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

3. 人员匹配逻辑完全错误

你用IntStream.range((int) up_limit, (int) down_limit).forEachOrdered(n -> { ... })遍历y坐标范围,这完全不符合人员匹配的逻辑——应该是对每个检测到的轮廓(中心坐标cx, cy),遍历已有的人员列表,判断是否属于同一个人,而不是循环遍历y轴数值。这个错误会导致人员状态更新混乱,甚至无法创建新的Person实例。

修正后的人员匹配逻辑:

for (int i = 0; i < contours.size(); i++) {
    double area = Imgproc.contourArea(contours.get(i));
    if (area > areaTH) {
        Moments M = Imgproc.moments(contours.get(i));
        double cx = M.get_m10() / M.get_m00();
        double cy = M.get_m01() / M.get_m00();
        Rect rectVal = Imgproc.boundingRect(contours.get(i));

        boolean isNewPerson = true;
        // 遍历已有人员,用欧氏距离判断是否为同一个目标(阈值可根据场景调整)
        for (Person pers : persons) {
            double distance = Math.sqrt(Math.pow(cx - pers.getX(), 2) + Math.pow(cy - pers.getY(), 2));
            if (distance < 50) {
                isNewPerson = false;
                pers.updateCoords((int) cx, (int) cy);
                // 判断移动方向并计数
                if (pers.goingUp(lineDown, lineUp)) {
                    countU.getAndIncrement();
                } else if (pers.goingDown(lineDown, lineUp)) {
                    countD.getAndIncrement();
                }
                // 更新人员完成状态
                if (pers.getState() == '1') {
                    if ("down".equals(pers.getDir()) && pers.getY() < down_limit) {
                        pers.setDone();
                    } else if ("up".equals(pers.getDir()) && pers.getY() < up_limit) {
                        pers.setDone();
                    }
                }
                break;
            }
        }

        // 新增人员
        if (isNewPerson) {
            Person p = new Person(pid.get(), (int) cx, (int) cy, maxPAge);
            persons.add(p);
            pid.getAndIncrement();
        }

        // 绘制标记
        Imgproc.circle(frame, new Point((int) cx, (int) cy), 5, new Scalar(0, 0, 255), -1);
        Imgproc.rectangle(frame, new Point(rectVal.x, rectVal.y), 
                          new Point(rectVal.x + rectVal.width, rectVal.y + rectVal.height), 
                          new Scalar(0, 10, 155), 2);
    }
}

4. 字符串比较错误

Java中字符串比较不能用==(比较的是对象引用),必须用equals()方法,否则永远不会匹配成功。比如原代码中的pers.getDir() == "down"要改成"down".equals(pers.getDir())(把常量放前面避免空指针)。

5. 文本绘制重复与重叠

原代码重复绘制了strUp,且strDown和strUp位置相同导致覆盖,修正后:

String strUp = "UP: " + countU;
String strDown = "DOWN: " + countD;
// 分别放在不同位置,避免覆盖
Imgproc.putText(frame, strUp, new Point(10, 40), font, 0.5, new Scalar(0, 0, 255), 2);
Imgproc.putText(frame, strDown, new Point(10, 70), font, 0.5, new Scalar(255, 0, 0), 2);

把这些错误修正后,应该就能正常检测到人员了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:17:02