OpenCV 报错 Assertion failed (s >= 0) in cv::setSize 的完整解决方案

OpenCV 报错 Assertion failed (s >= 0) in cv::setSize 的完整解决方案

  • 在使用 OpenCV 进行图像处理或深度学习推理时,遇到过这样一个令人困惑的错误:
xml 复制代码
OpenCV(4.10.0) Error: Assertion failed (s >= 0) in cv::setSize, 
file ...\opencv\modules\core\src\matrix.cpp, line 246

这个错误信息看起来像是 OpenCV 内部的问题,但实际上它几乎总是由你自己的代码传递了无效的尺寸参数导致的。更换 ONNX 模型后触发该错误------详细分析问题根源、调试方法以及解决方案。

1. 问题重现

基于 OpenCV DNN 模块加载 ONNX 模型进行目标检测的程序,原本使用 best0528.onnx 运行正常。当你将模型替换为 best0531.onnx 后,程序在推理后处理阶段突然崩溃,抛出上述 setSize 断言失败。

关键现象:

  • 新旧模型的输入尺寸完全一致[1, 3, 320, 320]
  • 新旧模型的输出名称和形状也相同
    • cat_18[1,65,40,40]
    • cat_19[1,65,20,20]
    • cat_20[1,65,10,10]
    • cat_29[1,17,3,2100]
  • 模型推理本身没有报错,但后处理时崩溃

2. 原因分析

cv::setSize 是 OpenCV 中用于调整矩阵(Mat)大小的内部函数。断言 s >= 0 失败意味着代码尝试创建一个尺寸(宽度或高度)为负数或零的矩阵。

在深度学习推理的后处理中,最常见的触发场景是:

  1. 从模型输出解码目标框的坐标 (x1, y1, x2, y2) 时 ,由于计算错误导致 x2 <= x1y2 <= y1
  2. 随后使用这些坐标提取 ROI (例如 frame[y1:y2, x1:x2])或调用 cv::resize 时,因为宽度或高度为非正数而触发断言

具体到本例,经过调试发现:新模型输出的原始值中,边界框的宽度(w)或高度(h)出现了负数。当后处理代码使用 x1 = x - w/2x2 = x + w/2 转换时,负的 w 导致 x2 < x1,最终使 y2 < y1 的无效框传递给了 OpenCV 函数。

为什么更换模型后会出现负数宽高?可能原因:

  1. 模型输出含义改变:旧模型可能输出经过 Sigmoid 限制在 0,1 的值,而新模型直接输出未经缩放的原始坐标(可能为负)
  2. 缺少解码步骤 :某些模型(如基于 Anchor 的检测器)需要对输出应用 exp() 等变换才能得到正数宽高
  3. 训练或导出问题:新模型的边界框回归分支未约束宽高为正,导致推理时出现负值

3. 调试过程

第一步:确认输入输出结构一致

使用 ONNX Runtime 打印模型信息,排除结构不匹配:

python 复制代码
import onnxruntime as ort

for model_path in ["best0528.onnx", "best0531.onnx"]:
    sess = ort.InferenceSession(model_path)
    print(f"--- {model_path} ---")
    for inp in sess.get_inputs():
        print(f"输入: {inp.name}, 形状: {inp.shape}")
    for out in sess.get_outputs():
        print(f"输出: {out.name}, 形状: {out.shape}")

结果:两个模型输入输出完全相同,排除结构问题。

第二步:检查输出数值范围

使用 ONNX Runtime 推理相同输入,打印每个输出层的最小值和最大值:

python 复制代码
sess = ort.InferenceSession("best0531.onnx")
input_name = sess.get_inputs()[0].name
dummy_input = np.random.randn(1, 3, 320, 320).astype(np.float32)
outputs = sess.run(None, {input_name: dummy_input})

for i, o in enumerate(outputs):
    print(f"Output {i}: shape={o.shape}, min={o.min():.3f}, max={o.max():.3f}")

输出示例:

text 复制代码
# 旧模型 best0528
cat_29: min=-104.925, max=409.481

# 新模型 best0531  
cat_29: min=-62.510, max=410.704

结论:数值范围相似,但新模型的最小值为 -62.51,说明输出中确实存在负数,这很可能是宽高为负的来源。

第三步:定位触发崩溃的具体代码行

在 OpenCV 后处理代码周围添加异常捕获和打印:

python 复制代码
try:
    # 模型推理
    net.setInput(blob)
    outs = net.forward()
    
    # 后处理:解析输出 -> 计算 x1,y1,x2,y2
    for detection in ...:
        x1 = int(...); y1 = int(...); x2 = int(...); y2 = int(...)
        
        # 关键检查
        if x2 <= x1 or y2 <= y1:
            print(f"无效框: ({x1},{y1}) -> ({x2},{y2})")
            continue
        
        roi = frame[y1:y2, x1:x2]   # 如果跳过检查,这里会崩
        cv2.resize(roi, (64, 64))
        
except cv2.error as e:
    print("OpenCV 错误:", e)
    import traceback; traceback.print_exc()

通过调用栈,发现崩溃发生在 roi = frame[y1:y2, x1:x2] 这一行,原因是 y2 < y1

第四步:追踪到原始模型输出

打印从 cat_29 解码出的原始 (x, y, w, h) 值:

python 复制代码
# 假设 cat_29 形状为 [1,17,3,2100],其中某几个通道是 x,y,w,h
for i in range(5):
    x = cat_29[0, 10, 0, i]   # 示例索引,需根据实际模型调整
    y = cat_29[0, 11, 0, i]
    w = cat_29[0, 12, 0, i]
    h = cat_29[0, 13, 0, i]
    print(f"Box {i}: x={x:.3f}, y={y:.3f}, w={w:.3f}, h={h:.3f}")

发现部分框的 w 或 h 为负数(例如 w=-0.23),证实了根本原因。

4. 解决方案

根据问题根源,有以下几种解决方案(按推荐程度排序):

方案一:在后处理中对宽高取绝对值(快速修复)

如果模型输出的宽高本应为正数,但出现了负值(可能由于训练或导出异常),可以直接取绝对值:

python 复制代码
w = abs(w)
h = abs(h)
x1 = x - w/2
y1 = y - h/2
x2 = x + w/2
y2 = y + h/2

优点 :简单、立即生效

缺点:可能不是模型设计的本意,但通常不会对检测效果产生致命影响

方案二:应用正确的解码变换(根本解决)

查阅新模型的文档或导出脚本,确认是否需要对输出应用特殊变换(例如指数映射、Sigmoid、乘以 Anchor 尺寸等)。常见做法:

python 复制代码
# 例如 YOLO 风格的解码
w = torch.exp(w) * anchor_w
h = torch.exp(h) * anchor_h

如果无法获取文档,可以用旧模型作为参考:对同一输入分别用新旧模型推理,比较 w 和 h 的比例关系,推测变换公式。

方案三:使用 ONNX Runtime 替代 OpenCV DNN

OpenCV DNN 在某些算子的实现上存在限制,而 ONNX Runtime 更加稳定和规范。如果模型本身没有问题,仅仅是因为 OpenCV 解析导致数值异常,切换到 ONNX Runtime 可以绕过问题:

python 复制代码
import onnxruntime as ort
sess = ort.InferenceSession("best0531.onnx")
outputs = sess.run(None, {input_name: blob})
# 后续使用 outputs 进行后处理

优点 :避免 OpenCV DNN 的潜在 bug,支持更广泛的算子

缺点:需要额外安装库(onnxruntime 通常已安装)

方案四:添加防御性坐标修正

无论原因如何,在将坐标传递给 OpenCV 函数之前,确保 x1 <= x2y1 <= y2

python 复制代码
x1, x2 = min(x1, x2), max(x1, x2)
y1, y2 = min(y1, y2), max(y1, y2)
# 同时确保坐标在图像边界内
x1 = max(0, x1); y1 = max(0, y1)
x2 = min(img_w, x2); y2 = min(img_h, y2)

优点 :防御性强,避免崩溃

缺点:可能产生宽度为 0 的框(仍需跳过)

5. 总结

Assertion failed (s >= 0) in cv::setSize 错误的核心原因是传入了无效的矩阵尺寸。在更换 ONNX 模型后出现该错误,通常是因为新模型的输出数值分布发生了变化,导致后处理计算出的坐标反转或宽高为负。

通过系统性的调试------先确认模型结构,再检查输出数值范围,然后定位崩溃代码行,最后追溯到原始模型输出------可以快速找到根本原因。解决方案根据情况选择:快速修复用取绝对值,根本解决用正确解码,长期推荐切换到 ONNX Runtime。

相关推荐
网易Y3编辑器1 小时前
AI全流程创游丨网易Y3编辑器Full Mode与Patch Mode双模式架构深度解析
人工智能·架构·编辑器
Data-Miner1 小时前
休闲食品数据分析平台建设方案,70页ppt全解析
大数据·人工智能·数据分析
jijinduoduo1 小时前
Hermes AgentAI 智能体科技调研报告
人工智能·科技
老兵发新帖1 小时前
PaddleOCR在mac上运行时的挂死问题分析
人工智能
ishangy1 小时前
智慧港口中皮带跑偏AI检测技术如何提升运输安全?
人工智能·安全·ai视觉解决方案·智慧港口·ai视觉监控·皮带跑偏识别
zhangfeng11331 小时前
超算HPC环境下 codex app-server 完整解析(国超集群场景
人工智能
元拓数智1 小时前
跨库NL2SQL可信落地的核心:用IntaLink破解数据关系“迷雾”
数据库·人工智能·ai·nlp·agent·llama
零陵上将军_xdr1 小时前
大模型开发01- 大模型基础
人工智能
Aloudata1 小时前
宽表 vs 语义层:论 AI 时代语义编织对智能数据分析的重要性
大数据·人工智能·数据挖掘·数据分析·agent·语义层·语义编织