YOLOv10实时目标检测:CUDA/PyTorch环境配置与摄像头部署避坑
简介:这份教程面向计算机视觉初学者与Python开发者,围绕YOLOv10实时目标检测系统的搭建,串联环境准备、模型训练、评估与视频流检测等环节,帮助读者理解从数据标注到应用验证的完整流程。压缩包内共1个doc文件,约28KB,以目录化文档承载核心内容,便于按章节查阅和对照实践。已有207人学习,适合希望快速熟悉YOLOv10目标检测开发路径的读者。文档涵盖硬件与软件要求、PyTorch和OpenCV等库的安装配置、COCO或自定义数据集的准备与标注、训练参数设置、mAP等评估指标及可视化方法,并给出摄像头视频流读取与检测框绘制的示例思路,可帮助读者建立可复现的实验框架,并在此基础上扩展更多检测场景与优化方向。
1. 摄像头能亮,框却画不出来
某开发者把 yolov10 仓库克隆下来,pip 一路装完,python train.py一敲,屏幕上跳出 CUDA out of memory;退回 CPU 推理,一张 640×640 的图要等三秒多,摄像头画面卡成 PPT。这类现场我见过太多次。yolov10 实时目标检测这套环境,难点从来不在"装没装上",而在版本对没对上——驱动支持的 CUDA 档位、PyTorch 轮子的 cu 标签、Python 小版本,错一个就 GPU 认不出来,或者训练十个 epoch 后崩在显存上。这份开发案例把环境准备、训练、评估、实时检测串成了一条线,适合手里有一张 NVIDIA 显卡、想从零跑通摄像头画面的开发者。下面按我自己的装机顺序,把命令、参数和翻车点摊开写。
2. 装前先对表:CUDA、Python 与 PyTorch 的三方版本锁定
装深度学习环境最忌讳"先装再说"。我一般的顺序是:先看卡,再看驱动,然后定 CUDA 档位,最后反推 Python 和 PyTorch,中间任何一步跳过,后面都要花两三倍时间回滚。这一章不装东西,只把表对清楚。
2.1 硬件门槛:4 核 CPU 只是底线,显存才是分水岭
案例里列的四条硬件要求是:CPU 至少 4 核、NVIDIA GPU(推荐 RTX 系列)、内存至少 8GB、存储至少 10GB。照着这份清单配机器不算错,但它只回答了"能不能启动",没回答"能不能训练"。真正卡人的是显存和显卡算力这两件事。
| 项目 | 案例给出的要求 | 我实际会留的余量 | 卡点在哪 |
|---|---|---|---|
| CPU | 至少 4 核 | 8 核以上 | 数据加载的 dataloader 进程吃 CPU |
| GPU | NVIDIA,RTX 系列推荐 | 显存 8GB 起 | 640 输入、batch 16 大约吃 6~7GB 显存 |
| 内存 | 至少 8GB | 16GB | 数据集缓存、多进程读图、Matplotlib 画图 |
| 存储 | 至少 10GB | 50GB | 数据集解压 + 多份权重 + 训练日志 |
显卡算力这一条得单独说。PyTorch 新版轮子对老架构的支持是在收紧的,算力过低的卡装上之后torch.cuda.is_available()能返回 True,但一跑卷积就报 no kernel image is available for execution on the device。笔记本上的低功耗独显跑推理勉强能用,训练基本别指望。另外,只有 CPU 的机器并不是完全不能跑,只是实时检测会退化成"准实时"——640 输入下单帧推理几百毫秒到一秒都有可能,摄像头预览会明显滞后。
跑之前先确认卡被系统认出来了:
# 查看显卡型号、显存、驱动版本,右上角那行 CUDA Version 后面会用到 nvidia-smi这条命令输出的右上角有一个CUDA Version: xx.x,很多人误以为那是"本机装的 CUDA 版本",其实它是当前驱动最高能支持的 runtime 版本。这个数字决定了你后面能选哪一档 PyTorch 轮子,记住它。
2.2 Python 3.7 到 3.10:为什么我一般不装最新版
案例写的是 Python 3.7 及以上。3.7 这个版本现在已经停止维护,很多科学计算包早就不发 cp37 的轮子了,pip 会退回到源码编译,在 Windows 上十有八九编译不过。3.11、3.12 这类较新的版本也会撞上同一个问题——包的最新版未必提供了对应 ABI 的预编译轮子。
我一般锁 3.9 或 3.10,这两个版本在 PyTorch、OpenCV、NumPy 这一圈里轮子最全,出了问题也最好搜到现成的解决方案。先看本机装了什么:
# Windows 上用 python --version,Linux/macOS 上区分 python 和 python3 python --version python3 -V # 机器上同时装了多个解释器时,确认当前 shell 指向哪一个 which python # Windows: where python如果输出是 3.11 以上,别急着卸,用 conda 单独建一个 3.10 的环境就行,系统 Python 不要动——很多系统工具依赖它,删了会连带把包管理器搞坏,这是我早年踩过的血泪教训。
2.3 驱动、CUDA Toolkit 与 PyTorch 自带 cu 库,别混为一谈
新手最容易在这里绕圈:看到教程说要装 CUDA,就去官网下了几个 G 的 CUDA Toolkit,装完发现 PyTorch 还是认不到 GPU。原因在于这是三个不同的东西:
- 显卡驱动:操作系统层面的,
nvidia-smi能出结果就说明驱动正常。 - CUDA Toolkit:包含 nvcc 编译器,只有你要自己编译 CUDA 算子、或者跑某些需要现场编译的扩展时才需要。
- PyTorch 轮子自带的 CUDA runtime 和 cuDNN:pip 装
cu118、cu121这类标签的包时,运行库是打包在轮子里的,跟系统有没有装 Toolkit 无关。
所以常规路线是:只装驱动,不装 Toolkit,直接装对应 cu 标签的 PyTorch。案例给的cu113属于比较早的档位,现在这个档位对应的轮子版本已经很老,硬装要么找不到包,要么被降级到一个跟新版代码不兼容的 torch。
驱动版本和可选轮子标签大致这样对应,具体以 PyTorch 官方安装矩阵为准:
nvidia-smi右上角显示 | 我一般选的轮子标签 |
|---|---|
| 11.8 | cu118 |
| 12.1 | cu118 或 cu121 |
| 12.4 及以上 | cu121 或更高档位 |
高版本驱动向下兼容低档位轮子,反过来不行。所以驱动新、轮子选低一档,是最省事的组合。
还有一条容易忽略的:Windows 上原生环境和 WSL2 是两套驱动栈,WSL2 里能跑不代表原生 Windows 能跑,反之亦然。团队协作时把这一条写进环境说明,能省掉一半"我这儿明明能跑"的争论。
3. 按顺序装:conda 环境、PyTorch 轮子与推理依赖
对完表就可以动手了。这一章的四步是有严格顺序的,尤其是第二步和第三步,顺序反了会出现 pip 把已经装好的 torch 覆盖成 CPU 版这种玄学问题。
3.1 建一个不污染系统的 conda 环境
# 创建名为 yolov10 的环境,显式指定 Python 小版本 conda create -n yolov10 python=3.10 -y # 激活环境 conda activate yolov10 # 确认 pip 和 python 指向同一个解释器 python -c "import sys; print(sys.executable)" pip -V三条命令里最关键是最后那条sys.executable。它会打印当前 python 的绝对路径,正常应该落在.../envs/yolov10/python.exe或.../envs/yolov10/bin/python下面。如果打印出来的是系统路径,说明 conda 没激活成功,后面 pip 装的东西全都会进系统环境,等你发现问题时已经污染了一片。
pip -V的输出里也会带一个路径,它必须和sys.executable的目录一致。这两个对不上,就是"pip 装完 import 还是找不到"的根源。激活环境后建议每次新开终端都先跑一遍conda activate yolov10,别嫌烦,省下的排错时间远超这几秒。
3.2 选对 PyTorch 安装源:--index-url和--extra-index-url差一个字,结果差很多
案例给的命令是:
pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113 pip install opencv-python matplotlib这条命令本身能跑通,但有两个问题。第一,cu113档位现在对应的 torch 版本很老;第二,--extra-index-url是"额外源",pip 会同时去 PyPI 找同名包,而 PyPI 上的torch默认是 CPU 版。两个源里版本号相同时,pip 不一定选哪个,结果就是你以为装了 GPU 版,实际装的是 CPU 版。
按第 2 章确认的档位改成--index-url,让它只认这一个源:
# 有 NVIDIA 显卡,按 nvidia-smi 显示的档位选,这里以 cu121 为例 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # 没有独显、或者只想先跑通 CPU 推理 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu参数说明:--index-url是替换默认源,--extra-index-url是追加源。装 PyTorch 这类有 CPU/GPU 同名包的情况,我一律用前者。装完立刻验证,别等到跑训练时才发现:
# 期望输出 True 和显卡数量,False 就回第 2 章查驱动 python -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.device_count())" # 打印当前显卡名,确认是独显不是核显 python -c "import torch; print(torch.cuda.get_device_name(0))"如果版本号里带+cpu后缀,说明装错了,先pip uninstall torch torchvision torchaudio -y再按上面重装。这个后缀是最直观的判断依据。
3.3 补齐 OpenCV、Matplotlib 与标注工具
# 图像读写、画框、摄像头取流 pip install opencv-python # 训练曲线和评估结果可视化 pip install matplotlib # 数据加载与配置解析,训练脚本会直接依赖 pip install numpy pyyaml tqdm pillow # 数据集标注工具,只在本地标注时用得到 pip install labelImgopencv-python和opencv-contrib-python不要同时装,两者提供同名模块,后装的会覆盖先装的,import 时表现得很随机。需要 SIFT 这类扩展算法才装 contrib 版,普通的读帧画框用基础版足够。
另外,如果这套环境装在没有图形界面的服务器上,把opencv-python换成opencv-python-headless。带 GUI 的版本依赖一堆 X11 库,在容器里 import 会直接报libGL.so.1: cannot open shared object file,换成 headless 版就没事。代价是不能用cv2.imshow,得改成写视频文件或者推流。
labelImg在部分系统上还需要额外的 Qt 依赖,装不上不影响主流程,标注可以换到有桌面的机器上做,标注结果是一堆 txt,拷过来就能用。
3.4 三条自检命令:把环境黑匣子打开
装完先别急着拉代码,跑这三条:
# 1. torch 能不能看见 GPU,cuDNN 版本是否正常 python -c "import torch; print('torch', torch.__version__); print('cuda', torch.cuda.is_available()); print('cudnn', torch.backends.cudnn.version())" # 2. OpenCV 能不能正常 import,版本是多少 python -c "import cv2; print('cv2', cv2.__version__)" # 3. 做一次真实的小矩阵运算,确认 kernel 能跑起来 python -c "import torch; a=torch.randn(1000,1000,device='cuda'); print((a@a).sum().item())"第一条期望看到cuda True和一个 cuDNN 版本号。第二条只要打印出版本就行,报 DLL 或 so 找不到就回 3.3 节看是不是装成了带 GUI 的版本。第三条最关键——它不只是检查is_available(),而是真的在 GPU 上跑了一次矩阵乘法,前面提到的 "no kernel image" 那种情况,只有到这一步才会暴露。
三条都过了,环境这关就算过了。后面再出问题,基本都能定位在代码、数据或权限上,范围一下缩小很多。
4. 拉代码、放权重、改配置:从仓库到摄像头画面的完整链路
环境通了,接下来是把仓库、权重、配置、脚本这条链路接上。这一章按"先能推理,再能训练"的顺序排,反过来的话你会在还没验证模型能不能加载的情况下,花几小时排查训练不收敛。
4.1 克隆仓库后的目录结构与权重落位
# 仓库地址用项目方给出的实际地址替换,这里用占位符 git clone <repo-url> yolo-v10 cd yolo-v10 # 看目录结构,确认入口脚本和配置文件都在 ls -la一个典型的目录长这样,不同分支会有出入,但核心几项跑不掉:
yolo-v10/ ├── train.py # 训练入口 ├── evaluate.py # 评估入口 ├── configs/ # 模型与训练配置 │ └── yolov10.yaml ├── weights/ # 权重文件存放位置 │ └── yolov10.pt └── data/ # 数据集配置 └── data.yaml权重文件yolov10.pt从项目方发布的地址下载后,放在仓库根目录或weights/下都行,关键是训练和推理命令里的--weights路径要跟实际位置对上。相对路径是相对当前工作目录算的,不是相对脚本位置,所以在yolo-v10/外面执行python yolo-v10/train.py --weights yolov10.pt会找不到文件。这类报错信息往往是FileNotFoundError,看着像权重没下下来,实际是路径错了。
下载完建议校验一下文件完整性,权重文件不完整时加载会报 unpickling 相关的错:
# 看文件大小是否合理,几百 MB 量级 ls -lh weights/yolov10.pt # 有官方校验值时对一下 sha256sum weights/yolov10.pt4.2 训练参数与数据集 YAML:每一项改动都会在显存和收敛上体现
案例给的配置是这样的:
# config.yaml batch_size: 16 learning_rate: 0.001 epochs: 50 img_size: 640四个参数逐项过一遍,这些是我实际会调整的地方:
batch_size直接决定显存占用,几乎线性相关。640 输入、8GB 显存下,16 是能跑但不宽裕的值,显存紧张的机器降到 8 甚至 4,配合梯度累积补回等效批量。这个值设大了,第一个 epoch 就 OOM;设太小,BN 层统计不准,mAP 会掉得莫名其妙。
learning_rate取 0.001 偏保守。YOLO 系列在 SGD 优化器下常用 0.01 起步配合 warmup,0.001 更适合 Adam 系。案例没指定优化器,我一般先按默认跑,看前几个 epoch 的 loss 曲线——loss 剧烈震荡就往下调一个数量级,loss 几乎不动就往上调。
epochs设 50 在自建小数据集上很容易过拟合。我一般把值设大一点(比如 200),靠验证集 mAP 连续若干轮不涨就早停,比拍脑袋定 50 靠谱。
img_size必须是 32 的整数倍,因为主干网络的下采样倍率决定了这个约束。填 600 会直接报错或者被静默取整,训练出来的模型输入尺寸和你以为的不一致。640 是通用值,小目标多的话可以提到 1280,但显存占用涨得很快。
数据集的data.yaml是另一个必改文件:
# data.yaml path: ../datasets/mydata # 数据集根目录,相对本文件或绝对路径 train: images/train # 训练图目录,相对 path val: images/val # 验证图目录 nc: 3 # 类别数 names: ['person', 'car', 'bike'] # 类别名,顺序即 class idnc和names必须和标注文件里的 class id 严格对应。标注时 person 是 0、car 是 1,这里写成['car', 'person', 'bike'],模型能训练、loss 也能降,但预测出来的框全贴着错误的标签——这种错误在指标上几乎看不出来,只有肉眼看检测结果才会发现,属于最阴的一类。
数据集目录结构也要对齐 YOLO 格式:
mydata/ ├── images/ │ ├── train/ xxx.jpg │ └── val/ yyy.jpg └── labels/ ├── train/ xxx.txt # 与图片同名,每行 "类别 中心x 中心y 宽 高",全部归一化到 0~1 └── val/ yyy.txt标完先抽查几个 txt,确认坐标都在 0 到 1 之间。有工具导出的格式是绝对像素坐标,混进去训练时不会报错,只是 loss 收敛得特别慢、框到处都是。
4.3 训练命令与日志:从 loss 曲线判断是不是白跑
# 单卡训练,--device 0 指定第一张卡 python train.py \ --data data.yaml \ --cfg yolov10.yaml \ --weights yolov10.pt \ --img-size 640 \ --batch 16 \ --epochs 50 \ --device 0参数含义:--data指向数据集配置,--cfg是模型结构配置,--weights是预训练权重(从它开始微调,比从零训练收敛快得多),--img-size与配置文件里的值保持一致,--device填0表示第一张卡,填cpu强制走 CPU(排错时用得上)。
启动后头几十行输出要重点看三件事:模型参数量和 GFLOPs、数据集里训练集和验证集的图片数量、显存占用。图片数量对不上,多半是data.yaml里的相对路径没解析对,训练会拿空数据集跑,loss 一直是常数。显存占用接近上限的话,趁早把 batch 降下来,不然跑到一半 OOM 更难受。
训练日志和权重一般落在runs/目录下,每轮一个子目录,里面有权重、results.csv、混淆矩阵和 PR 曲线。results.csv是后面画曲线的数据源,别急着删。
4.4 实时检测脚本:从 VideoCapture 到画框的每一行
案例给的推理脚本是这样的,我加了注释:
import cv2 import torch # 加载模型。torch.hub.load 要求仓库里有 hubconf.py, # 没有的话会报找不到 entrypoint,改用仓库自己的推理入口或直接加载本地权重 model = torch.hub.load('yourusername/yolo-v10', 'yolov10', pretrained=True) model.eval() # 切推理模式,关掉 dropout、固定 BN 统计量 cap = cv2.VideoCapture(0) # 0 是默认摄像头,外接 USB 相机通常从 1、2 开始 while True: ret, frame = cap.read() if not ret: break # 读帧失败,可能是设备被占用或已断开 # 预处理:缩放、归一化、HWC 转 CHW、补 batch 维 img = cv2.resize(frame, (640, 640)) img = img / 255.0 img = img.transpose((2, 0, 1)) img = torch.tensor(img).float().unsqueeze(0) # 推理 results = model(img) # 逐框绘制:坐标、置信度、类别 for *box, conf, cls in results.xyxy[0]: label = f'{model.names[int(cls)]} {conf:.2f}' cv2.rectangle(frame, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255, 0, 0), 2) cv2.putText(frame, label, (int(box[0]), int(box[1]) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) cv2.imshow('YOLOv10 Detection', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()几个需要说明的点。cap.read()返回的ret必须判断,摄像头被其他程序占用、或者设备号填错时它返回 False,不判断就会在cv2.resize那里报 NoneType 错误,看着像 resize 的问题,实际是取流失败。
img / 255.0之后再transpose和unsqueeze,这个顺序不能乱,转成 tensor 之后再除也能跑,但会多一次显存拷贝。推理前记得把张量搬到 GPU 上(img = img.cuda()),否则模型在 GPU、输入在 CPU,会直接报设备不一致。
results.xyxy[0]这种取法依赖具体返回结构。不同版本的推理入口返回的可能是 tensor、可能是带.boxes属性的对象,硬套一种写法,换个权重就崩。我一般的做法是先在单张图上跑一次,print(type(results))和print(results)看清楚结构再写解析代码,比照着教程猜快得多。
5. 避坑排查:五个装完跑不起来的现场
这一章全是真实现场。每条按"现象、原因、解决"写,遇到对应的直接对号入座。
5.1 现象:torch.cuda.is_available()返回 False
原因:按概率从高到低排——装成了 CPU 版轮子(版本号带+cpu);显卡驱动太老,支持的 CUDA 档位低于轮子要求;--extra-index-url导致 pip 从 PyPI 拿了 CPU 版;在 WSL2 里装的原生 Windows 版 Python,两套驱动栈对不上。
解决:先看版本号后缀,带+cpu就卸了按 3.2 节用--index-url重装;再看nvidia-smi是否正常输出,报NVIDIA-SMI has failed说明驱动本身有问题,先修驱动;驱动正常但档位太低,就换低一档的轮子标签。三步走完还是 False,跑python -c "import torch; print(torch.version.cuda)",输出为 None 就是 CPU 版无疑。
5.2 现象:训练第一个 epoch 就CUDA out of memory
原因:batch_size 相对显存设大了;img_size提到了 1280 但没同步降 batch;同一张卡上还跑着别的进程没退干净;数据加载的 worker 数太多,每个进程都占一份显存上下文。
解决:先把 batch 减半试,能跑就把--batch 8写进命令;确认没有僵尸进程,nvidia-smi看显存占用,有残留就kill -9掉对应 PID;还是不够就把img_size降到 512 或 416。另外有个容易忽略的点:PyTorch 的缓存分配器不会立刻把显存还给系统,报 OOM 时实际空闲显存可能比nvidia-smi显示的更多,遇到"明明还有显存却 OOM"的情况,在训练脚本开头加torch.cuda.empty_cache()试一次。
5.3 现象:cap.read()一直返回 False,或者画面卡住不动
原因:设备号不对(笔记本内置摄像头是 0,外接 USB 相机往往是 1 或 2);摄像头被浏览器、会议软件占用;Linux 下当前用户没有/dev/video*的读权限;imshow后没有waitKey导致窗口不刷新。
解决:写个小循环遍历设备号,看哪个能读出帧:
# 逐个试设备号,能读到帧就打印分辨率并退出 python -c " import cv2 for i in range(5): cap = cv2.VideoCapture(i) ok, frame = cap.read() print(i, ok, frame.shape if ok else None) cap.release() "有输出 True 的那个编号就是该用的。全是 False 就检查是不是被其他程序占用,Linux 下把用户加进video组再重新登录。
5.4 现象:检测框位置整体偏移,或者标签全是错的类别
原因:预处理里做了 resize 但画框时用的是原图坐标,两套坐标系没对齐;data.yaml里的names顺序和标注时的 class id 不一致;标注文件里混进了绝对像素坐标,没归一化。
解决:画框前把检测结果按原图宽/640、原图高/640缩放回原图坐标系;names列表顺序逐项核对标注工具的类别配置;抽查几个 label txt,确认所有数值都落在 0~1 之间,超了就是没归一化,用脚本批量除以图片宽高修正。
5.5 现象:import cv2或import torch报 DLL load failed
原因:Windows 上缺 Visual C++ 运行库;装的是带 GUI 的 OpenCV 但系统没有图形环境;conda 环境里混进了系统 site-packages 的同名包;多个 Python 版本共存,pip 和 python 指向不同解释器。
解决:先跑 3.1 节的sys.executable和pip -V对比路径;服务器或容器环境换成opencv-python-headless;Windows 装一遍微软的 VC++ 运行库;还不行就pip list | grep -i opencv(Windows 用findstr)看是不是装了多个 OpenCV 包,卸到只剩一个。
6. 装完怎么验证:三条命令加一张对照表把系统跑通
环境装完不等于系统跑通。我习惯按"能加载、能推理、能实时、指标对得上"四层往下验,每层都有明确的通过标准,任何一层不过就停在那儿查,不往下走。
第一层,环境自检,就是 3.4 节那三条命令,重点看 GPU 上真实跑一次矩阵乘法的结果。
第二层,单图推理。拿一张确认有目标的图,跑一次前向,看输出的框数量和置信度是否合理:
# 单图推理,--conf 过滤低置信度框,先看 0.25 以上的 python detect.py --weights weights/yolov10.pt --source test.jpg --img-size 640 --conf 0.25如果一张明显有人的图输出零个框,先别怀疑模型,把--conf降到 0.05 再看——有框但置信度低,说明预处理或类别映射有问题;依然零框,说明权重没加载成功。
第三层,实时链路。跑摄像头并统计帧率,只统计推理加绘制的耗时,不含imshow的等待:
# 跑 100 帧,输出平均单帧耗时和等效 FPS import time, cv2, torch cap = cv2.VideoCapture(0) times = [] for i in range(100): ok, frame = cap.read() if not ok: break t0 = time.time() # 这里放你的预处理 + model(frame) + 画框 times.append(time.time() - t0) cap.release() avg = sum(times) / len(times) print(f'avg {avg*1000:.1f} ms, fps {1/avg:.1f}')第四层,指标复核。用验证集跑一遍评估,把 mAP 和训练日志最后一轮的数值对一下:
python evaluate.py --weights best.pt --data data.yaml| 验证项 | 命令或动作 | 通过标准 | 不通过先查 |
|---|---|---|---|
| GPU 可用 | torch.cuda.is_available() | 返回 True 且版本号无+cpu | 3.2 节轮子源 |
| 单图推理 | detect.py --source test.jpg | 框数量与肉眼一致 | --conf阈值、权重路径 |
| 实时帧率 | 100 帧计时脚本 | 单帧耗时稳定,无逐帧递增 | 是否每帧都在建 tensor、有没有torch.no_grad() |
| 评估指标 | evaluate.py --weights best.pt | mAP 与训练日志末轮接近 | data.yaml的 val 路径 |
帧率那一项有个隐蔽的坑:推理时忘了套torch.no_grad(),每帧都会建计算图,显存一路涨,前几十帧看着正常,后面越来越慢直到 OOM。看到耗时逐帧递增,先补这一句。
从那以后我每次装完这套环境,不管多急,都会把单图推理和 100 帧计时这两步强制走一遍——玄学问题大多在这两步就现原形了,等接上业务代码再发现,排查成本要翻好几倍。希望帮到你。
本文还有配套的精品资源,点击获取