简介:这份资源是一套基于Gradio搭建的YOLOv8目标检测服务实战项目,面向具备一定Python基础、希望快速落地算法服务的学习者与开发者,可用于课程设计、毕业项目或工程原型验证。压缩包共10个文件,约14.09MB,包含4个py源码文件、2个pyc编译文件、2个mp4演示视频、1个md说明文档以及1个onnx模型文件,覆盖从模型加载、推理封装到Web界面交互的完整链路。项目以YOLODet推理模块与main入口为核心,配合utils工具函数,将YOLOv8模型封装为可交互的检测服务,并附带运行效果录屏,便于对照理解服务启动与检测流程。目前已有224人学习下载,适合想掌握目标检测服务化部署、Gradio界面搭建与ONNX推理实践的读者参考,可据此快速复现并二次开发自己的检测应用。
1. 从一份 YOLOv8 检测服务源码说起:Gradio 把模型变成能点的网页
你手里可能已经有一份训练好的 YOLOv8 权重,best.pt躺在runs/detect/train/weights/里,命令行yolo predict也能跑出框。但同事、客户、导师不会用命令行,他们要的是打开浏览器、拖一张图进去、几秒后看到框和置信度。这就是「基于 Gradio 搭建的 YOLOv8 目标检测服务」要解决的事:用几十行 Python 把推理逻辑包成一个网页,附带的项目源码和流程教程,本质是把「模型 → 接口 → 界面」这条链路一次性打通。
它适合三类人:刚跑完 YOLOv8 训练想验证效果的学生、要把检测能力塞进内部工具的后端工程师、以及做毕业设计或课程项目需要交付可演示系统的开发者。读完你能拿到一条可复现的路径:环境怎么配、Gradio 界面怎么接模型、参数怎么调、部署时哪里会翻车。下面按「先跑通最小服务 → 再拆解参数 → 最后处理踩坑」的顺序展开。
2. 最小可运行服务:Gradio 接 YOLOv8 的完整代码与逐行说明
2.1 环境准备:CPU 和 GPU 两条路怎么选
先明确一件事:Gradio 只是前端壳,真正吃资源的是 YOLOv8 推理。如果你手头是 GTX1660Ti 这类显卡,走 GPU 路线;如果是纯 CPU 机器(比如 Ubuntu20.04 的云主机),也能跑,只是单张图延迟从几十毫秒涨到几百毫秒。常见做法是先装 PyTorch,再装 ultralytics 和 gradio。
# 创建独立环境,避免和系统 Python 打架 conda create -n yolo_gradio python=3.10 -y conda activate yolo_gradio # GPU 版本(CUDA 11.8 示例,按自己驱动改) pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # CPU 版本(没有显卡就用这条) # pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 核心依赖 pip install ultralytics gradio opencv-python pillow这里三个包的分工要清楚:ultralytics提供 YOLO 类和推理接口,gradio负责生成网页和文件上传组件,opencv-python用于画框和格式转换。版本上不用刻意锁死,ultralytics 8.x 系列 API 稳定,gradio 4.x 和 5.x 的Interface用法基本一致。装完用python -c "from ultralytics import YOLO; import gradio; print('ok')"验证,能打印 ok 就说明依赖没冲突。
提示:如果 pip 装 torch 特别慢,先换国内镜像源再装,别在超时上耗半小时。
2.2 推理函数:从上传图片到返回带框图
Gradio 的核心是一个普通 Python 函数,输入是图片,输出也是图片。YOLOv8 的predict返回的Results对象里已经带了绘制好的图像,直接取plot()就行,不用自己写画框逻辑。
import gradio as gr from ultralytics import YOLO import numpy as np # 加载模型,只加载一次,放在全局 model = YOLO("best.pt") # 换成你自己的权重路径 def detect(image, conf_threshold, iou_threshold): """ image: gradio 传入的 numpy 数组 (H, W, 3),RGB conf_threshold: 置信度阈值 iou_threshold: NMS 的 IoU 阈值 """ if image is None: return None # YOLOv8 接受 numpy 数组,内部会做 BGR/RGB 处理 results = model.predict( source=image, conf=conf_threshold, iou=iou_threshold, imgsz=640, verbose=False ) # results[0].plot() 返回 BGR 的 numpy 图,转成 RGB 给 gradio annotated = results[0].plot() annotated = annotated[:, :, ::-1] # BGR -> RGB return annotated逻辑说明:model.predict的source可以直接吃 numpy 数组,省去存临时文件的步骤。conf和iou作为函数参数暴露出来,是为了后面在界面上加滑块。plot()画出来的图默认是 BGR 通道顺序,而 Gradio 的 Image 组件按 RGB 显示,所以必须做一次通道翻转,否则颜色会发蓝——这是最常见的翻车点之一。
参数说明:imgsz=640是推理分辨率,和训练时保持一致效果最好;如果训练用的是 1280,这里也要改成 1280,否则小目标检测精度会掉。verbose=False只是关掉控制台刷屏,不影响结果。
2.3 界面组装:滑块、示例图和启动参数
有了推理函数,用gr.Interface把输入输出拼起来。输入有两个:图片和两个滑块;输出一个:带框图片。
demo = gr.Interface( fn=detect, inputs=[ gr.Image(type="numpy", label="上传图片"), gr.Slider(0.1, 0.9, value=0.25, step=0.05, label="置信度阈值"), gr.Slider(0.1, 0.9, value=0.45, step=0.05, label="IoU 阈值"), ], outputs=gr.Image(type="numpy", label="检测结果"), title="YOLOv8 目标检测服务", description="上传图片,调整阈值,查看检测框", examples=[["example.jpg", 0.25, 0.45]], # 可选,放一张示例图 ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860)server_name="0.0.0.0"让服务监听所有网卡,局域网内其他机器能访问;只在本机用就写127.0.0.1。server_port默认 7860,被占用就换一个。examples里的图片路径要真实存在,否则启动时会报错。跑起来后浏览器打开http://本机IP:7860,拖图进去就能看到框。
注意:
gr.Image(type="numpy")传进来的是 RGB 数组,如果你的模型训练时用的是 BGR(比如 OpenCV 读图),这里要手动转一下,否则精度会异常。
3. 参数调优与模型替换:让服务适配你自己的数据集
3.1 置信度与 IoU 阈值:两个滑块背后的取舍
界面上那两个滑块不是摆设,它们直接决定检测结果的松紧。置信度阈值(conf)控制「多确定才算检测到」,调低会冒出大量误检框,调高会漏掉模糊目标。IoU 阈值控制 NMS 阶段「多重叠才算同一个目标」,调低会抑制相邻目标,调高会让同一物体出现多个框。
| 参数 | 典型范围 | 调低的现象 | 调高的现象 | 建议起点 |
|---|---|---|---|---|
| conf | 0.1 ~ 0.9 | 误检增多,背景被框 | 漏检增多,小目标消失 | 0.25 |
| iou | 0.1 ~ 0.9 | 密集目标被合并 | 同一目标重复框 | 0.45 |
实操建议:先用默认 0.25 / 0.45 跑一批测试图,统计漏检和误检哪个更严重。漏检多就把 conf 降到 0.15 试试,误检多就升到 0.4。密集场景(比如人群、货架)把 iou 降到 0.3 左右,能减少重复框。这两个值没有万能解,跟你的数据集分布强相关。
3.2 换成自己训练的权重:路径、类别和 imgsz 对齐
项目源码里默认加载的可能是yolov8n.pt这种官方权重,类别是 COCO 的 80 类。你要用自己的数据,改一行就够:
# 替换成自己训练产出的权重 model = YOLO("runs/detect/train/weights/best.pt")但换权重之后有三件事必须对齐。第一,类别名。best.pt里已经存了训练时的names字典,plot()会自动用正确的类别名,不用手动改。第二,imgsz。训练时如果设的是 640,推理也保持 640;训练用 1280 而推理用 640,小目标召回会明显下降。第三,预处理。YOLOv8 训练时默认做了 letterbox 缩放,predict内部会自动处理,你不需要在 Gradio 函数里再手动 resize,多此一举反而会引入形变。
如果你用的是 labelme 标注再转 YOLO 格式的数据集,确认data.yaml里的nc和names与权重一致。曾经遇到过一个血泪经验:权重是 3 类,但界面上显示的框标签全是错位的,查了半天发现是data.yaml里 names 顺序和训练时不一致,重新导出权重才解决。
3.3 批量图片与视频输入:Gradio 组件的替换方式
单图检测跑通后,很多人想直接支持视频或整个文件夹。Gradio 换组件就行,推理函数稍作调整。
def detect_video(video_path, conf_threshold): # video_path 是 gradio 传进来的临时文件路径 results = model.predict( source=video_path, conf=conf_threshold, stream=True, # 视频必须开 stream,否则内存爆 verbose=False ) # 这里简化处理:逐帧写出到临时视频,实际项目用 cv2.VideoWriter for r in results: frame = r.plot() # 省略写帧逻辑 return output_path关键点是stream=True。视频如果不开流式,ultralytics 会把所有帧读进内存再处理,几分钟的视频就能把内存吃满。批量图片则可以把gr.Image换成gr.Files,函数里循环调用model.predict,返回一个 zip 或文件列表。注意 Gradio 对返回文件有格式要求,图片列表要用gr.Gallery组件接收。
4. 部署与排错:Gradio 服务上线的五个真实坑
4.1 坑一:模型每次请求都重新加载,首屏慢到怀疑人生
现象:第一次点检测等十几秒,后面就快了,但服务重启后又慢。原因:YOLO("best.pt")写在了detect函数内部,每次调用都重新加载权重。解决:把模型加载提到函数外面,作为全局变量,服务启动时加载一次。如果显存紧张,可以在launch前加torch.cuda.empty_cache(),但别在每次推理后清,反而拖慢速度。
4.2 坑二:局域网访问不了,浏览器一直转圈
现象:本机127.0.0.1:7860能开,同事的电脑打不开。原因:launch()默认只绑127.0.0.1。解决:改成server_name="0.0.0.0"。如果还不行,检查服务器防火墙是否放行 7860 端口,Ubuntu 上用sudo ufw allow 7860。另外公司网络如果有端口限制,换 80 或 8080 这类常用端口试试。
4.3 坑三:上传大图后服务卡死或返回空白
现象:几 MB 的高清图上传后,界面一直 loading,最后报错或返回黑图。原因:YOLOv8 默认会把输入缩放到imgsz,但超大图在预处理阶段仍然占内存,CPU 推理时尤其明显。解决:在detect函数开头加一个尺寸检查,超过 4000 像素的图先等比缩小。
from PIL import Image import numpy as np def detect(image, conf_threshold, iou_threshold): if image is None: return None h, w = image.shape[:2] max_side = 2000 if max(h, w) > max_side: scale = max_side / max(h, w) image = np.array(Image.fromarray(image).resize((int(w*scale), int(h*scale)))) # 后续推理不变4.4 坑四:GPU 显存溢出,报 CUDA out of memory
现象:跑几张图后报显存不足。原因:Gradio 默认可能并发处理多个请求,每个请求都占一份显存。解决:在launch里限制并发,demo.queue(max_size=1)或者demo.launch(max_threads=1)。另外推理完可以手动del results,但 Python 的垃圾回收不一定及时,最稳的还是限制并发数。如果显卡只有 4GB 显存,imgsz降到 416 也能跑,精度损失可接受。
4.5 坑五:中文标签显示成方块
现象:检测框上的类别名是中文时,图上显示成方框乱码。原因:OpenCV 的putText不支持中文,而plot()底层用的就是它。解决:两个办法。一是训练时类别名用英文,显示时再映射成中文;二是自己写画框逻辑,用 PIL 的ImageDraw配合中文字体文件。
from PIL import Image, ImageDraw, ImageFont def draw_chinese(image, boxes, labels): img = Image.fromarray(image) draw = ImageDraw.Draw(img) font = ImageFont.truetype("SimHei.ttf", 20) # 字体文件放项目目录 for box, label in zip(boxes, labels): x1, y1, x2, y2 = box draw.rectangle([x1, y1, x2, y2], outline="red", width=2) draw.text((x1, y1-25), label, fill="red", font=font) return np.array(img)字体文件要随项目一起分发,Linux 服务器上如果没有中文字体,把SimHei.ttf或NotoSansCJK放到代码同级目录,用相对路径加载。
5. 进阶技巧:用队列和身份验证把服务变成可交付的内部工具
服务能跑之后,下一步是让它「像个产品」。Gradio 自带队列和身份验证,不用额外写后端。队列解决并发排队问题,身份验证解决「不想让所有人随便访问」的问题。
先看队列。默认情况下 Gradio 对每个请求开一个线程,GPU 服务很容易被并发打爆。加上demo.queue()后,请求会排队处理,配合concurrency_count控制同时处理的数量。
demo = gr.Interface(...) demo.queue(concurrency_count=1, max_size=10) # 同时处理1个,最多排10个 demo.launch(server_name="0.0.0.0", server_port=7860)concurrency_count=1对单卡服务最稳,max_size是队列上限,超过就拒绝新请求。如果你的服务是 CPU 推理,可以设成 2 或 4,取决于核数。
再看身份验证。Gradio 的launch支持auth参数,传入用户名密码对即可。
demo.launch( server_name="0.0.0.0", server_port=7860, auth=("admin", "your_password"), # 浏览器会弹基础认证框 auth_message="请输入账号密码" )这样打开网页会先弹一个 HTTP 基础认证框,输入正确才能进界面。注意这是明文传输的 Basic Auth,内网用没问题,公网暴露一定要套 HTTPS,否则密码等于裸奔。如果要做多用户管理,Gradio 的auth不够用,得换成 FastAPI 挂载 Gradio 或者用 Nginx 做前置认证。
验证服务是否真的可用,我一般做三步。第一步,用curl测端口通不通:curl -I http://127.0.0.1:7860,返回 200 说明服务活着。第二步,用一张训练集里的图跑一遍,确认框的位置和标签正确。第三步,用一张训练集外的图跑,看泛化表现,如果框乱飞,说明模型过拟合或者阈值不对。这三步走完,基本能判断这个服务能不能交付。
最后说一个我自己的习惯:每次改完推理函数,先在本地用python app.py跑一遍,确认没有语法错误和路径问题,再传到服务器。服务器上只做部署,不做调试。这样能省掉大量「在服务器上改一行等半天」的时间。另外权重文件不要提交到 Git,用.gitignore排除,部署时单独上传,避免仓库膨胀。
希望帮到你。
本文还有配套的精品资源,点击获取