YOLOv11+Django+DeepSeek:从零构建水下鱼类检测系统
水下鱼类检测现在是个挺热闹的方向生态监测、水产养殖、海洋科研都在用。但真做过的朋友都知道模型训练出来只是第一步怎么把它包装成一个“别人能用的东西”才是麻烦事。这个项目标题其实已经把所有关键点都列出来了——YOLOv11做识别、Django做后端服务、DeepSeek做智能分析、配上登录界面和Web前端最后用ONNX解决部署问题。整套链路走下来才算是一个能给别人演示、能落地跑的系统而不是一个躺在Jupyter里的Demo。我打算按实际开发顺序来拆解这套系统把每一步怎么想、怎么做、踩了哪些坑都讲清楚。无论是想复现这个项目的同学还是打算自己做类似“模型Web”应用的朋友这篇文章应该都能帮你省不少时间。1. 整体方案设计与技术选型拆解1.1 这套系统到底解决什么问题先说个实际场景。假设你要给某海洋研究所做一套水下鱼类监测工具需求大概是研究人员登录系统上传一段水下视频或者图片系统自动识别画面里的鱼种、统计数量、标出位置最后生成一份带分析结论的报告。如果全靠人工看视频数鱼一小时素材能看一整天而且不同人统计口径还不一样。这就是这个项目要解决的核心痛点——把“人眼数鱼”变成“算法检测大模型分析”。拆开看系统其实需要四个能力一是目标检测能从水下画面里把鱼找出来二是Web服务得让用户通过浏览器访问不能让大家装Python环境三是智能分析单纯画个框还不够最好能生成“这张图里发现了3种鱼类总共12条其中某种鱼占比最高”这种结论四是访问控制系统不能裸奔得有登录功能。标题里选型其实都对应得挺准YOLOv11负责目标检测Django搭Web框架DeepSeek做语义分析ONNX管模型部署。这四个环节各司其职拼起来就是一条完整的产品链路。1.2 为什么是YOLOv11、Django、DeepSeek和ONNX这个组合先说YOLOv11。相比前代版本它在C3K2模块和SPPF结构上做了优化检测头也调整了。最关键的是它在保持高精度的同时模型体积和推理速度控制得不错。对于水下检测这种需要部署到边缘设备或普通服务器的场景YOLOv11的性价比很合适。如果你以前用的是YOLOv8迁移成本其实很低配置文件和数据格式基本通用。选它还有一个现实原因——社区生态成熟训练、导出、部署的资料特别多遇到问题搜起来方便。做项目最怕的是选了个小众模型报错了连个参考都没有。然后是Django。说实话做这种带管理后台、带登录鉴权的系统Django简直是开挂。它自带Admin后台、用户认证、Session管理、CSRF防护你不用自己写登录逻辑直接用django.contrib.auth就能搞定。ORM用起来也省心检测记录、用户信息直接映射成表。对于不熟悉前端的技术同学来说Django的模板系统能把数据直接渲染成页面省掉搭建前后端分离工程的复杂度。DeepSeek在这里不是用来做检测的而是接管“检测之后的语义分析”。为什么要加一个大模型进来因为光画框不是终点用户更想要结论。把YOLOv11检测到的鱼类名称、数量、置信度打包成文本喂给DeepSeek让它生成一份自然语言的分析报告。这一下就让系统从“工具”变成了“助手”。而且DeepSeek的接口调用成本和国内访问体验都挺实用不需要像ChatGPT那样折腾网络配置。最后说ONNX。训练好的PyTorch模型不能直接扔到生产环境用户电脑上未必装了PyTorch。ONNX是一种中间格式可以脱离PyTorch环境配合ONNX Runtime跑推理。在CPU上的速度比PyTorch直接跑好不少显存占用也更低。这个项目里把模型转成ONNX就是为了部署时省心。1.3 系统架构分层从工程角度看这套系统的代码组织大致分成三层模型层训练好的YOLOv11权重导出为ONNX封装成推理服务输入图像输出检测结果类别、坐标、置信度。服务层Django应用负责用户认证、文件上传、调用推理、调用DeepSeek生成报告、返回JSON数据给前端。展示层HTML模板JavaScript负责上传交互、结果可视化图片上画框、报告展示。这三个层级之间通过JSON数据通信。前端拿到检测结果后用Canvas在原图上绘制矩形框和标签不需要后端生成标注图片这样响应更快也节省存储空间。2. 模型侧从训练到ONNX导出的核心细节2.1 数据集准备与训练配置水下鱼类检测的训练数据集网上有公开的鱼类数据集比如某些海洋生物图像库但实际使用时通常要自己补充标注。水下画面和常规图像差异很大——光线偏蓝、对比度低、有悬浮颗粒、鱼群密集遮挡多。直接用通用目标检测数据集训练出来的模型在水下场景识别效果往往不理想这就是我前面说的“模型训练出来但不好用”的典型原因。标注格式上YOLO系列要求每张图配一个同名txt文件每行格式是类别id 中心点x 中心点y 宽 高坐标值都归一化到0~1之间。如果你手里是COCO格式的JSON标注记得先写脚本转成YOLO格式再训练。训练集和验证集按照8:2或9:1划分每个类别至少要有几百张实例图否则类别不平衡会让模型严重偏向多数的那个类。训练配置建议参考以下基准参数名推荐值说明输入尺寸640×640常规选择精度和速度平衡batch size16视显存调整显存不够就降到8配合梯度累积epochs100~200用早停机制看验证集mAP不再提升就停优化器SGD或AdamWYOLOv11官方默认SGDmomentum即可学习率0.01配合预热前几个epoch做warmup避免震荡数据增强mosaic、翻转、HSV扰动、随机仿射提升模型对水下颜色偏移的鲁棒性训练过程中需要注意水下图像的标注质量直接影响模型上限。你标注的鱼如果边缘不清晰、框不贴合模型学到的特征就是模糊的。建议所有标完的数据做一次人工复核特别检查被遮挡和密集场景下的标注是否准确。训练完成看两个指标验证集mAP50和mAP50-95mAP50达到0.8以上基本够用mAP50-95看类别难度到0.5以上算正常。2.2 ONNX导出与推理封装训练完得到best.pt权重文件导出ONNX非常简单。Ultralytics官方包自带导出方法from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) model.export(formatonnx, dynamicTrue, simplifyTrue, opset12)这里有几个参数值得说清楚。dynamicTrue表示动态输入尺寸因为用户上传的图片不一定是正方形如果固定输入尺寸推理时多一步强行缩放精度会受影响。simplifyTrue会调用ONNX Simplifier做计算图优化去掉一些冗余算子推理速度能提升10%到20%。opset版本建议12或更高版本太低某些新算子不支持。导出完成后用ONNX Runtime加载模型推理import numpy as np import onnxruntime as ort session ort.InferenceSession(best.onnx, providers[CUDAExecutionProvider, CPUExecutionProvider]) def preprocess(img, size640): # 等比例缩放 填充到640x640 h, w img.shape[:2] scale min(size / h, size / w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(img, (new_w, new_h)) canvas np.full((size, size, 3), 114, dtypenp.uint8) canvas[:new_h, :new_w] resized # BGR转RGB、归一化、转CHW、加batch维度 blob canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0 return blob[None, ...], scale, new_w, new_h预处理有个特别容易踩的坑YOLO训练时用的是RGB顺序、归一化到0~1但OpenCV读图出来是BGR、0~255。如果你忘了转通道顺序检测效果会变得非常奇怪——鱼全都检测不到或者置信度极低。转成CHW维度是为了配合ONNX模型的输入格式。推理结果里的坐标是相对于640×640填充图的需要根据缩放比例映射回原图坐标这个换算逻辑在画框时要用到。def postprocess(outputs, scale, pad_w, pad_h, orig_w, orig_h, conf_thres0.35): # outputs shape: [1, 84, 8400] - [batch, 5类别数, 候选框数] preds outputs[0].squeeze(0).transpose(1, 0) # [8400, 84] boxes preds[:, :4] scores preds[:, 4:] class_ids np.argmax(scores, axis1) confs scores[np.arange(len(scores)), class_ids] keep confs conf_thres boxes, class_ids, confs boxes[keep], class_ids[keep], confs[keep] # NMS抑制重复框 indices cv2.dnn.NMSBoxes( boxes.tolist(), confs.tolist(), conf_thres, nms_threshold0.45 ) results [] for i in indices.flatten(): x1, y1, x2, y2 boxes[i] # 还原到原图坐标 x1 (x1 - pad_w) / scale y1 (y1 - pad_h) / scale x2 (x2 - pad_w) / scale y2 (y2 - pad_h) / scale results.append({ bbox: [float(x1), float(y1), float(x2), float(y2)], class_id: int(class_ids[i]), confidence: float(confs[i]), }) return resultsNMS非极大值抑制这个步骤必不可少。同一群鱼可能被模型输出好几个重叠框NMS会保留置信度最高的那个把重叠度高的相近框去掉。如果省了这一步前端画框时画面会非常乱同一位置叠着三四个框。推理封装建议写成单例模式整个Django进程只加载一次ONNX模型。因为模型加载到内存里要几百MB每来一个请求重新加载一次系统直接卡死。我见过不少新手在视图函数里加载模型并发一上来就内存溢出。正确的做法是在应用启动时预加载模型或者用模块级全局变量缓存session对象。3. Django后端登录鉴权与检测API实现3.1 项目结构划分Django项目如果所有代码堆在views.py里项目一大就是灾难。我习惯按功能拆分类似这样fish_detection/ ├── manage.py ├── config/ # 项目配置 │ ├── settings.py │ └── urls.py ├── apps/ │ ├── accounts/ # 用户登录、注册、登出 │ ├── detection/ # 检测核心逻辑、模型推理调用 │ ├── analysis/ # DeepSeek报告生成 │ └── dashboard/ # 页面渲染 ├── static/ # JS、CSS、图片 ├── templates/ # HTML模板 ├── models/ # 存放ONNX权重等模型文件 └── utils/ # 通用工具如图像预处理之所以拆成多个app是为了让检测逻辑和用户逻辑互不干扰。后面如果你想加一个“检测历史记录”功能直接在detection里加两张表就行不用动accounts的代码。3.2 登录鉴权实现细节Django自带认证体系直接用最稳妥# settings.py INSTALLED_APPS [ ... django.contrib.auth, django.contrib.sessions, ] # 默认登录后跳转地址可以自定义 LOGIN_URL /login/ LOGIN_REDIRECT_URL /dashboard/在视图里用装饰器保护需要登录的页面from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate, login, logout from django.shortcuts import render, redirect def user_login(request): if request.method POST: username request.POST[username] password request.POST[password] user authenticate(request, usernameusername, passwordpassword) if user is not None: login(request, user) return redirect(/dashboard/) else: return render(request, login.html, {error: 用户名或密码错误}) return render(request, login.html) def user_logout(request): logout(request) return redirect(/login/) login_required def dashboard(request): return render(request, dashboard.html)有人可能会问为什么不自己写密码校验和Session管理因为Django自带的方案经过了大量安全检查包括常见的暴力破解防护扩展和Session固定攻击防护。自己写的登录逻辑很容易漏掉安全细节比如CSRF攻击、密码存储明文等问题。做实际项目能站在成熟框架的肩膀上就别自己造轮子。前端登录页面需要把CSRF token放到表单里form methodpost action/login/ {% csrf_token %} input typetext nameusername placeholder用户名 required input typepassword namepassword placeholder密码 required button typesubmit登 录/button /form如果去掉这个csrf_token表单提交会被Django拦截报403错误。这个很多人第一次接触Django时会困惑其实是为了防止恶意网站伪造请求。3.3 检测API与文件上传处理图片上传接口的核心逻辑是接收文件、保存到临时目录、调用推理服务、返回结果。用Django的request.FILES来处理十分方便import os from django.conf import settings from django.http import JsonResponse from django.views.decorators.http import require_POST from utils.inference import ObjectDetector detector ObjectDetector() # 模块加载时初始化一次 login_required require_POST def detect_image(request): upload_file request.FILES.get(image) if not upload_file: return JsonResponse({code: 400, msg: 未接收到文件}, status400) # 限制文件大小比如最大10MB if upload_file.size 10 * 1024 * 1024: return JsonResponse({code: 400, msg: 图片大小不能超过10MB}, status400) # 保存临时文件 temp_path os.path.join(settings.MEDIA_ROOT, temp, upload_file.name) with open(temp_path, wb) as f: for chunk in upload_file.chunks(): f.write(chunk) # 调用模型推理 img cv2.imread(temp_path) detections detector.detect(img) # 清理临时文件 os.remove(temp_path) return JsonResponse({code: 200, data: detections})文件上传有两个坑必须提一下。第一一定要用upload_file.chunks()而不是一次性读入内存。用户传个大视频文件时一次性读入直接能把服务器的内存撑爆。第二临时文件用完要清理否则用不了多久服务器磁盘全是垃圾文件。如果你希望保留用户的历史记录就在数据库里记录文件路径和检测结果而不是把原图一直堆在服务器上。视频检测和图片检测不太一样。视频是多帧连续画面如果每帧都丢给DeepSeek做分析成本太高。实际做法是抽帧处理——每隔几帧检测一次然后汇总整段视频里出现的鱼类种类和大致数量。这个逻辑可以根据需求灵活调整但基本思路是“抽帧检测结果聚合”。3.4 DeepSeek大模型接口集成DeepSeek在这里扮演的角色是“语义分析层”。检测结果出来以后只是一堆坐标和类别id对用户来说不够友好。我们需要让系统生成一段人话告诉他“这张图里有3条石斑鱼、2条小丑鱼总体看鱼群密度中等”。这就是DeepSeek的活儿。调用方式很直接用HTTP请求打它的API接口import requests import json def generate_report(detections, total_count): if detections: # 统计各类别数量 species_summary {} for d in detections: label d[class_name] species_summary[label] species_summary.get(label, 0) 1 summary_text , .join([f{k} {v}条 for k, v in species_summary.items()]) prompt ( f你是一个海洋生态分析助手。以下是水下图像的目标检测结果 f共检测到{total_count}条鱼其中{summary_text}。 f请根据这些数据生成一段简洁的中文生态观察报告 f包括鱼种组成、可能的生活习性、对水质环境的推测。不要编造检测结果中不存在的数据。 ) else: prompt 你是一个海洋生态分析助手。这张水下图像中没有检测到鱼类。请用一句话说明可能的原因。 try: resp requests.post( https://api.deepseek.com/v1/chat/completions, headers{ Authorization: fBearer {settings.DEEPSEEK_API_KEY}, Content-Type: application/json, }, json{ model: deepseek-chat, messages: [{role: user, content: prompt}], temperature: 0.5, max_tokens: 300, }, timeout30, ) resp.raise_for_status() return resp.json()[choices][0][message][content] except Exception as e: return 报告生成失败模型接口暂时不可用。这里有几个设计要点。温度参数调到0.5左右是为了让大模型既不过于发散也不过于保守。不要把temperature调太高否则报告可能胡编乱造出检测结果之外的鱼种。Prompt里必须明确约束“不要编造检测结果中不存在的数据”这一步可以很大程度上防止大模型幻觉。至于性能调用DeepSeek接口一般需要2到6秒。在Web层面不能让用户干等可以选两种方案一是同步等待接口返回后一起把检测框和报告展示出来二是异步处理先返回检测框报告在后台生成后通过轮询接口获取。如果追求体验异步方案更好但代码复杂度高一些。对于自己用或演示的项目同步方案够用。我这个项目实现的是同步返回但设置了超时保护避免接口故障时整个请求挂死。4. 前端交互与精美界面实现思路4.1 页面整体布局这个项目的界面设计目标很明确——让用户第一眼就知道系统是干嘛的操作路径清晰。整体页面分成登录页、控制台、检测页和检测结果展示四个核心区域。登录页走的是简洁路线居中卡片、系统Logo、用户名密码输入框背景用一张深海蓝调图片营造主题氛围。控制台展示最近检测记录、检测总量统计、各类别出现次数等汇总信息。检测页是核心操作区左侧是文件上传区支持拖拽上传右侧是结果展示区。用户上传图片后页面即时显示标注后的图像和DeepSeek生成的报告。4.2 前端实现与Canvas标注画检测框用Canvas实现思路是把原图画到Canvas上然后根据后端返回的坐标信息在Canvas上再叠加绘制矩形框和文字标签。这样可以不动原图文件纯前端完成标注展示。function drawDetections(image, detections) { const canvas document.getElementById(result-canvas); const ctx canvas.getContext(2d); canvas.width image.width; canvas.height image.height; // 按原图大小绘制 ctx.drawImage(image, 0, 0); // 遍历检测结果画框 detections.forEach(det { const [x1, y1, x2, y2] det.bbox; ctx.strokeStyle #00ffcc; ctx.lineWidth 3; ctx.strokeRect(x1, y1, x2 - x1, y2 - y1); // 绘制标签背景 ctx.fillStyle rgba(0, 255, 204, 0.7); ctx.fillRect(x1, y1 - 25, 140, 25); ctx.fillStyle #000; ctx.font bold 16px Arial; ctx.fillText(det.class_name det.confidence.toFixed(2), x1 5, y1 - 5); }); }画框有个细节需要注意字体大小和框的尺寸不是固定值大屏幕上画出来的框标签文字如果太小会看不清。经验做法是标签高度可以随框大小微调或者置顶一个固定字号。另外置信度保留两位小数就好不要显示一大串小数干扰视觉。4.3 异步上传与进度反馈用户体验上最怕的是点击上传后页面卡住用户以为系统挂了。优化方案是采用Ajax异步上传加上Loading动画和进度条。前端用fetch发送FormData到后端接口然后接收JSON响应async function uploadAndDetect(file) { const formData new FormData(); formData.append(image, file); showLoading(true); try { const response await fetch(/api/detect-image/, { method: POST, body: formData, headers: { X-CSRFToken: getCookie(csrftoken) } }); const result await response.json(); if (result.code 200) { renderResult(result.data); } else { showError(result.msg); } } catch (err) { showError(网络请求失败请稍后重试); } finally { showLoading(false); } }Ajax请求里同样要带上CSRF token否则Django会拒绝请求。这也是个容易踩的坑——测试接口用Postman没问题浏览器里Ajax却一直403多半就是没带token。视频检测的交互比图片麻烦一些。视频需要先上传到后端后端抽帧检测然后再把标注后的视频返回到前端。如果视频比较大这个过程可能要十几秒。前端就得显示进度百分比。简单方案是后端每处理完一帧就把进度写入一个临时状态表前端用轮询接口去查进度。复杂方案是用WebSocket实时推送进度但工程复杂度会明显上升。我的建议是先从轮询做起等性能瓶颈真的出现了再考虑升级。我自己做的时候视频处理用的是两步上传后返回一个任务ID前端每2秒请求一次该任务的检测结果检测完成就返回数据没完成就返回进度百分比。4.4 中文字体与界面美化注意事项Canvas绘制检测标签时默认字体可能不支持中文ctx.fillText画出来全是方块。解决方式是加载自定义字体或者直接使用系统已有中文字体ctx.font bold 16px Microsoft YaHei, PingFang SC, sans-serif;如果要在Django服务端直接用OpenCV画框输出图片也存在同样问题。cv2.putText默认用的Hershey字体不支持中文你需要用PIL加载中文字体再画文字。这也是我在调试时报错最多的地方做完这个项目我才意识到中文字体兼容性是做中文场景Web应用的隐藏坑。后端画框示例from PIL import Image, ImageDraw, ImageFont import numpy as np font ImageFont.truetype(static/fonts/simhei.ttf, 20) def draw_boxes_cn(img, detections): OpenCV图像转PIL绘制中文字体标签 img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) pil_img Image.fromarray(img_rgb) draw ImageDraw.Draw(pil_img) for det in detections: x1, y1, x2, y2 [int(v) for v in det[bbox]] draw.rectangle([x1, y1, x2, y2], outline(0, 255, 204), width3) label f{det[class_name]} {det[confidence]:.2f} draw.text((x1, max(0, y1 - 25)), label, fontfont, fill(0, 255, 204)) return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)5. 常见问题排查与调优实战记录5.1 ONNX推理结果与PyTorch不一致最常见的坑之一model.predict()效果很好但ONNX Runtime推理结果差很多甚至完全检测不到目标。我排查了一圈发现问题几乎都出在预处理不一致上。PyTorch推理时Ultralytics内部会自动做BGR转RGB、归一化、缩放填充而用ONNX Runtime推理时这些预处理必须自己写一旦遗漏了通道转换模型输入分布就不对了。还有一个隐蔽问题是letterbox填充值。YOLOv5及之后的版本缩放填充的默认填充色是灰色114也就是(114, 114, 114)。如果用0填充模型对填充区域的响应就会异常检测效果明显变差。这个细节我第一次没注意到调试了整整半天才发现是填充值的问题。注意用ONNX Runtime替换PyTorch推理后务必用同一张测试图做对比验证逐项检查预处理差异。别只跑一张图说结果正常要多测几张不同尺寸的图。5.2 视频检测时内存暴涨视频检测比图片吃内存得多。有人直接把整个视频文件读进内存再逐帧处理视频稍微一大服务进程内存直接跑到2GB以上最终被系统杀掉。正确做法是流式读取视频帧import cv2 cap cv2.VideoCapture(video_path) frame_count 0 while True: ret, frame cap.read() if not ret: break if frame_count % interval 0: # 每隔N帧检测一次 detections detector.detect(frame) # 汇总结果 frame_count 1 cap.release()此外检测完的视频或者大量临时帧不要全部保存在内存里要么处理完立即释放要么写到本地磁盘。这个问题的核心是批量数据会用完即弃而不是全局持有。5.3 DeepSeek接口超时与限流大模型接口虽然好用但稳定性受网络影响较大。排查过程中我遇到的典型错误有几种requests超时、HTTP 429限流、返回数据里没有choices字段。解决方案是调用必须设置timeout参数同时对返回结果做完整的结构校验。另外建议在项目里加一个简单的重试机制第一次失败间隔2秒重试一次最多重试三次。关于限流DeepSeek接口有每分钟请求次数限制具体以官方文档为准。如果多个用户同时使用系统每秒并发请求可能触发限流。一个简单的缓解方案是加内存队列或者Redis队列把分析请求排队处理避免一次性打满接口限额。不过如果只是单人使用或内部演示这块可以暂时不做复杂处理。5.4 Django并发与性能优化Django自带开发服务器runserver性能很差只能用于本地调试。如果部署到服务器必须用gunicorn或uWSGI。这里有个容易踩的坑gunicorn默认每个worker只处理一个请求如果模型推理耗时2秒4个worker的情况下并发能力也才4个请求。建议调大worker数和线程数比如gunicorn config.wsgi:application -w 4 --threads 8让每个worker支持多线程并发。模型推理是CPU密集或GPU密集操作如果跑在CPU上多线程反而可能因为GIL限制导致推理速度下降因为ONNX Runtime内部有自己的线程池。一个比较实用的配置是ONNX Runtime用intra_op_num_threads4控制CPU线程数Django worker数不要开太大避免上下文切换开销。我做性能压测时发现CPU环境4个worker是最优平衡点再往上性能反而下降了。5.5 常见问题速查表问题现象可能原因解决方案登录后访问任意页面跳回登录页Session过期或请求未携带Session Cookie检查浏览器Cookie是否被禁用确认Django的SESSION_COOKIE_AGE配置Ajax上传图片返回403请求头缺少X-CSRFToken在Ajax请求中携带CSRF token或使用csrf_exempt不推荐ONNX推理检测不到任何鱼预处理通道顺序不对或letterbox填充值不正确复核BGR转RGB、归一化、填充值114模型加载占满服务器内存每个请求都重新加载模型改为模块级单例加载启动时预加载一次DeepSeek报告出现编造的鱼种大模型幻觉或temperature过高Prompt中明确约束降低temperature到0.5以下画框文字全是方块Canvas字体不支持中文指定中文字体如Microsoft YaHei、PingFang SC视频检测卡死内存溢出或一次性读入视频改为流式读取逐帧处理及时释放资源6. 部署上线与项目扩展方向6.1 生产环境部署流程Django项目部署其实有个固定的套路。用gunicorn跑Django应用前面再挂Nginx做反向代理和静态文件服务。# 安装依赖 pip install gunicorn django onnxruntime requests # 启动gunicorn绑定时指定端口 gunicorn config.wsgi:application -w 4 --threads 8 -b 0.0.0.0:8000Nginx配置片段server { listen 80; server_name your_domain; # 替换为实际域名或IP # 静态文件 location /static/ { alias /path/to/project/static/; } # 媒体文件上传的图片等 location /media/ { alias /path/to/project/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }部署时还有两件事别漏掉。第一DEBUG模式一定设为False否则Django会把详细的报错信息直接暴露给用户非常危险。第二启动前先执行python manage.py collectstatic把静态文件收集到一个目录否则Nginx找不到CSS和JS文件页面样式会全部丢失。6.2 关于“支持RTSP视频流接入”的思考项目做完后当前版本以图片和本地视频为主。如果要完全贴合水下监测的实时场景扩展方向最值得做的就是接入RTSP视频流也就是把水下摄像头的实时画面直接送进检测系统里。这个的需求匹配度非常高。实现思路是FFmpeg拉流然后逐帧喂给ONNX模型。Django后端可以切换到用外部的任务队列来处理流数据每个摄像头对应一个后台线程或进程检测结果通过WebSocket推送到前端。如果你用的是USB摄像头测试阶段直接用OpenCV读取本地摄像头画面验证明白之后再切到RTSP同样改一行URL就能搞定。6.3 检测结果入库与统计分析现在检测完的结果如果不记录就浪费了一部分数据价值。我建议加两张表一张存检测任务包括用户、上传时间、图片路径、检测状态另一张存检测明细包括任务ID、类别、置信度、坐标。这样在控制台就能做统计某段时间内哪个鱼种出现最多、检测总量趋势图等。Django的ORM做这类统计聚合很方便。from django.db.models import Count from apps.detection.models import DetectionRecord def get_species_statistics(user): # 按类别统计当前用户检测到的鱼类数量 stats ( DetectionRecord.objects.filter(useruser) .values(class_name) .annotate(totalCount(id)) .order_by(-total) ) return list(stats)有了历史数据积累后续还能做更多事情比如异常检测——某天鱼群数量突然大幅下降系统主动提醒用户注意水质变化这就是从“识别工具”到“分析平台”的进化方向。最后再分享一个我个人的体会。做这种“算法Web”的完整项目最耗时间的不是算法本身反而是那些不起眼的工程细节——模型导出格式对不上、前端字体乱码、Ajax请求被CSRF拦截、服务器并发一高就内存溢出。每一个坑单看都不难凑在一起却让人挠头。建议大家在动手前先列一个完整的技术栈清单把部署环境的版本、依赖项都固定下来少在环境问题上耗精力。系统跑通的成就感还是很值钱的尤其是当你把源码、模型、Web界面打包完整丢给别人就能直接用的时候。

相关新闻

AGI不是更聪明的AI,而是目标生成机制的范式革命

AGI不是更聪明的AI,而是目标生成机制的范式革命

1. 这不是科幻,是正在发生的工程演进——AGI到底在说什么“什么是 AGI,我们如何通向它——一份写给普通人的路线图”这个标题里,“AGI”三个字母最近频繁出现在科技媒体、投资人会议甚至咖啡馆闲聊中,但它常被混同于“更聪明的Cha…

2026/10/17 8:40:24 阅读更多 →
纯CPU跑大语言模型:免费资源下的量化部署与推理实践

纯CPU跑大语言模型:免费资源下的量化部署与推理实践

经常有人问我,没有独立显卡、只有几G内存的机器到底能不能跑大语言模型。我这次直接把手里能用的免费CPU资源翻了个底朝天,在纯CPU环境下把Qwen3.5、DeepSeek-R1、Gemma 3、Llama 3.2这几个热门开源模型都跑通了一遍。整个过程没有花一分钱,也…

2026/10/17 8:40:24 阅读更多 →
AGI本质是认知范式迁移,不是更聪明的Siri

AGI本质是认知范式迁移,不是更聪明的Siri

1. AGI不是“更聪明的Siri”,而是认知范式的彻底迁移很多人第一次听说AGI,是在某篇科技媒体标题里——“AGI即将诞生”“人类最后的堡垒即将失守”。接着点进去,发现通篇在讲大模型参数量破万亿、多模态理解能力提升、推理链长度翻倍……于是…

2026/10/17 8:40:24 阅读更多 →

最新新闻

WSL+tmux从零配置到实战:打造Windows高效命令行环境

WSL+tmux从零配置到实战:打造Windows高效命令行环境

最近帮朋友把一台Windows系统配成日常开发环境,发现很多人其实对WSL和tmux这两个名字都熟悉,但真正组合起来用的不多。我在Windows 10/11上用WSL跑了挺久,命令行工作从git到批量改文件、编译、挂机跑脚本,基本都搬进了tmux里。这篇…

2026/10/17 9:28:39 阅读更多 →
Linux home目录迁移实战:rsync、权限与挂载全解析

Linux home目录迁移实战:rsync、权限与挂载全解析

1. 迁移home目录这件事,远比你想的要折腾很多人第一次听到“迁移home目录”,脑子里浮现的画面大概就是拖拽文件夹,等进度条走完,重启,收工。我当初也是这么想的,直到有一次在一台用了三年的开发机上动手&am…

2026/10/17 9:28:39 阅读更多 →
SBTI科学碳目标解读:企业如何完成从碳盘查到目标获批的减排路径

SBTI科学碳目标解读:企业如何完成从碳盘查到目标获批的减排路径

不知道从什么时候开始,供应链问卷、招标文件、投资者沟通材料里,越来越多的企业开始主动或被动地聊起“SBTI”这三个字母。我印象很深的一件事是,早几年给客户做碳排放规划,方案做完了,对方一句“先放一放,…

2026/10/17 9:28:39 阅读更多 →
ClickHouse日志平台实战:从ES迁移、表结构设计到查询调优

ClickHouse日志平台实战:从ES迁移、表结构设计到查询调优

做大数据的人,迟早要跟日志打交道。业务日志、访问日志、服务日志、安全审计日志,哪一类都是量大、格式杂、查询需求刁钻。我最早用Elasticsearch做日志平台,后来数据量上来之后,集群规模、存储成本、查询毛刺这些问题接踵而至&am…

2026/10/17 9:28:39 阅读更多 →
2026年AI大模型API聚合平台排名揭晓:这三家平台表现出彩,助你开发无忧

2026年AI大模型API聚合平台排名揭晓:这三家平台表现出彩,助你开发无忧

进入2026年,大模型的迭代节奏快得让人应接不暇,GPT-5.4、Claude4.6、Gemini3.1Pro 几乎每个月都有新版本放出来。对开发者而言,逐家注册账号、分别保管密钥、各自编写适配逻辑的传统做法,已经很难跟上这种月更速度,API…

2026/10/17 9:28:39 阅读更多 →
不完全信息Epsilon纳什均衡在航天器末端追逃博弈的Matlab实现

不完全信息Epsilon纳什均衡在航天器末端追逃博弈的Matlab实现

作为研究航天器近距离任务的开发者,我们组在复现不完全信息Epsilon纳什均衡的航天器末端追逃博弈策略(Matlab代码实现)这篇论文时,最大的感受是:以前觉得博弈论在航天里就是理论推推公式,真正要把“对方参数…

2026/10/17 9:27:38 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →