ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Django搭配YOLO26m的医疗废弃物识别系统实现

Django搭配YOLO26m的医疗废弃物识别系统实现

计算机毕设指导师

⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。
⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!也可以在主页上或文末下与博主交流~~
⚡⚡Java、Python、小程序、大数据实战项目集](https://blog.csdn.net/2301_80395604/category_12487856.html)

⚡⚡文末获取源码

温馨提示:文末有CSDN平台官方免费提供的博客联系方式!
温馨提示:文末有CSDN平台官方免费提供的博客联系方式!
温馨提示:文末有CSDN平台官方免费提供的博客联系方式!

医疗废弃物识别系统-简介

本系统以基于深度学习的复杂场景医疗废弃物目标检测识别研究为题,后端采用Python与Django搭建,前端用Vue组织页面,检测环节选用YOLO26m单阶段目标检测模型,经ONNX Runtime部署到服务端,对纱布、试管、医用帽、护目镜、鞋套、尿袋以及单只与成对的乳胶、丁腈、手术手套共13类医疗废弃物做框选与分类。训练数据取自公开的Medical Waste 4.0标注集,1415张彩色图像按拍摄时间戳分组划分为1060张训练、213张验证与142张测试,避免同一场景的图像跨集出现导致指标虚高。系统提供图片上传、视频抽帧与摄像头实时三种识别入口,识别结果与置信度同步写入历史记录,识别大屏用类别分布、数量趋势、置信度分布、时段热力与任务类型五张图呈现整体情况。为了减少无效输出,系统在推理前后各设一道判断,纯色画面直接拦截,与训练场景差异过大的画面按能量分数判为域外样本,尽量让结果可靠一些。

医疗废弃物识别系统-背景

选题背景
医疗废弃物在处置环节需要按类别分开收集,纱布、手套、鞋套、试管、尿袋这些物品在临床处置场景里常常混在一起,透明材质、随手堆叠和光照变化又让辨认变得吃力。人工分拣依赖经验,长时间作业容易漏看,接触风险也摆在那里。视觉检测这几年在通用目标上已经比较成熟,但公开的医疗废弃物标注数据不多,能把检测模型真正接进一个可操作页面的实现更少。很多同学的毕设停在后端增删改查,缺少一条从数据整理、模型训练到页面识别的完整链路,这个题目正好落在这个空档上。
选题意义
从学习角度看,这个课题把数据划分、指标口径、阈值设定这些平时容易被跳过的环节摊开在眼前,比如按时间戳分组划分、用独立测试集评估、用能量分数控制误拒,做过一遍比看十篇文章更清楚。从应用角度看,系统给出的框选结果与类别提示,可以为分拣环节提供一层辅助参考,识别记录也让处置过程留下可回看的痕迹。从工程角度看,训练与部署通过导出产物解耦,模型更新不必改动页面代码,这种组织方式对同类课题有参考价值。受样本规模与采集设备限制,系统目前只是一个可复现的实现思路,谈不上替代人工。

医疗废弃物识别系统-核心功能

功能一:账号登录与角色区分
账号密码登录后下发令牌,后续请求携带令牌识别身份,菜单按角色返回。
功能二:单张图片识别
校验格式与体积,判断画面是否过于单一,送入模型得到框、类别与置信度,框选结果叠加在原图上展示。
功能三:视频抽帧识别
按帧率折算采样间隔,约每秒两帧,跨帧对同一类别去重,保留置信度最高的一次并记录出现时间点。
功能四:识别历史管理
按时间倒序分页展示,可按任务类型与类别筛选,详情含原图、类别、平均置信度、推理耗时与模型版本,删除为标记删除。
功能五:识别大屏统计
类别分布柱图、数量趋势折线、置信度分布柱图、星期与小时热力图、任务类型环形图,五张图并行加载。
功能六:用户与权限维护
账号信息维护,管理员查看全部记录,普通用户只看自己的数据。

医疗废弃物识别系统-视频

Django搭配YOLO26m的医疗废弃物识别系统实现

医疗废弃物识别系统-图片展示








医疗废弃物识别系统-代码展示

# -*- coding: utf-8 -*-importos,io,json,time,uuid,base64,datetimeimportnumpyasnpimportcv2fromPILimportImagefromdjango.httpimportJsonResponsefromdjango.views.decorators.csrfimportcsrf_exemptfrompyspark.sqlimportSparkSession,functionsasFfrommain.modelsimportrecognitionevent,detectionrecord spark=SparkSession.builder.appName("MedicalWasteDetect").master("local[2]").config("spark.sql.shuffle.partitions","4").getOrCreate()LABELS_ZH={"gauze":"纱布","test_tube":"试管","medical_cap":"医用帽","medical_glasses":"医用护目镜","shoe_cover_single":"鞋套(单只)","shoe_cover_pair":"鞋套(成对)","urine_bag":"尿袋","glove_single_latex":"乳胶手套(单只)","glove_single_nitrile":"丁腈手套(单只)","glove_single_surgery":"手术手套(单只)","glove_pair_latex":"乳胶手套(成对)","glove_pair_nitrile":"丁腈手套(成对)","glove_pair_surgery":"手术手套(成对)"}CONF_THR,IOU_THR,IMG_SIZE,OOD_THR,MAX_MB=0.4,0.5,512,0.32,8# ============ 核心功能一:单张图像识别与双重拒识 ============@csrf_exemptdefai_predict(request):t0=time.time()ifrequest.method!='POST':returnJsonResponse({'code':1,'msg':'请使用POST上传文件'})task_type=request.POST.get('task_type','image')iftask_typenotin('image','video','camera'):returnJsonResponse({'code':1,'msg':'任务类型不合法'})upload=request.FILES.get('file')ifnotupload:returnJsonResponse({'code':1,'msg':'未接收到文件'})ifupload.size>MAX_MB*1024*1024:returnJsonResponse({'code':2,'msg':'文件超过%dMB,请压缩后重试'%MAX_MB})raw=upload.read()arr=np.asarray(Image.open(io.BytesIO(raw)).convert('RGB'))iffloat(arr.std())<3.0orlen(np.unique(arr[::8,::8]))<8:_write_event(request,task_type,'纯色图',0.0,False)returnJsonResponse({'code':3,'reason':'pure_color','msg':'画面内容过于单一,未发现可识别目标'})boxes=_detect(arr)top_conf=max([b['conf']forbinboxes],default=0.0)if_energy(top_conf)>OOD_THR:_write_event(request,task_type,'域外样本',top_conf,False)returnJsonResponse({'code':3,'reason':'out_of_distribution','msg':'该画面与训练场景差异较大,结果仅供参考'})ifnotboxes:_write_event(request,task_type,'无目标',0.0,False)returnJsonResponse({'code':3,'reason':'empty','msg':'未检出医疗废弃物目标'})top1=max(boxes,key=lambdab:b['conf'])latency=int((time.time()-t0)*1000)rec=_save_record(request,task_type,raw,boxes,arr.shape[1],arr.shape[0],latency)_write_event(request,task_type,top1['class'],top1['conf'],True)returnJsonResponse({'code':0,'data':boxes,'record_id':rec.id,'latency_ms':latency})# ============ 核心功能二:视频抽帧识别与跨帧去重 ============@csrf_exemptdefai_predict_video(request):t0=time.time()upload=request.FILES.get('file')ifnotupload:returnJsonResponse({'code':1,'msg':'未接收到视频文件'})ifupload.size>100*1024*1024:returnJsonResponse({'code':2,'msg':'视频超过100MB,请截取片段后重试'})tmp=os.path.join('/tmp',uuid.uuid4().hex+'.mp4')withopen(tmp,'wb')asfh:forchunkinupload.chunks():fh.write(chunk)cap=cv2.VideoCapture(tmp)ifnotcap.isOpened():os.remove(tmp)returnJsonResponse({'code':2,'msg':'视频解码失败,请更换格式后重试'})fps=cap.get(cv2.CAP_PROP_FPS)or25.0step=max(int(round(fps/2)),1)hits,idx,sampled,skipped={},0,0,0whileTrue:ok,frame=cap.read()ifnotok:breakifidx%step==0:sampled+=1rgb=cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)iffloat(rgb.std())<3.0:skipped+=1else:forbin_detect(rgb):old=hits.get(b['class'])ifoldisNoneorb['conf']>old['conf']:hits[b['class']]=dict(b,frame=idx,second=round(idx/fps,2))idx+=1cap.release()os.remove(tmp)merged=sorted(hits.values(),key=lambdax:-x['conf'])top_conf=merged[0]['conf']ifmergedelse0.0ifmergedand_energy(top_conf)<=OOD_THR:_write_event(request,'video',merged[0]['class'],top_conf,True)returnJsonResponse({'code':0,'data':merged,'sampled':sampled,'frames':idx,'skipped':skipped,'latency_ms':int((time.time()-t0)*1000)})_write_event(request,'video','无目标',top_conf,False)returnJsonResponse({'code':3,'reason':'empty','msg':'整段视频未发现可确认的废弃物目标','sampled':sampled,'frames':idx,'skipped':skipped})# ============ 核心功能三:识别大屏批量聚合统计 ============@csrf_exemptdefdashboard_summary(request):days=int(request.GET.get('days',14))since=datetime.datetime.now()-datetime.timedelta(days=days)qs=recognitionevent.objects.filter(is_success=True,created_at__gte=since).values('user','task_type','top1_class','confidence','created_at')rows=[(int(r['user']),r['task_type']or'image',r['top1_class']or'未知',float(r['confidence']),r['created_at'].strftime('%Y-%m-%d'),r['created_at'].hour)forrinqs]ifnot_is_admin(request):uid=int(request.session.get('user_id',0))rows=[rforrinrowsifr[0]==uid]ifnotrows:returnJsonResponse({'code':0,'data':{'class_dist':[],'trend':[],'conf_hist':[],'task_type':[],'heat':[],'days':days}})sdf=spark.createDataFrame(rows,schema=['user','task_type','top1_class','confidence','day','hour']).cache()class_dist=[{'class':r['top1_class'],'count':r['count']}forrinsdf.groupBy('top1_class').count().orderBy(F.desc('count')).limit(20).collect()]trend=[{'date':r['day'],'count':r['count']}forrinsdf.groupBy('day').count().orderBy('day').collect()]conf_hist=[{'bin':round(float(r['bin']),1),'count':r['count']}forrinsdf.withColumn('bin',F.floor(F.col('confidence')*10)/10).groupBy('bin').count().orderBy('bin').collect()]task_type=[{'name':r['task_type'],'value':r['count']}forrinsdf.groupBy('task_type').count().collect()]heat=[{'h':int(r['hour']),'v':r['count']}forrinsdf.groupBy('hour').count().orderBy('hour').collect()]sdf.unpersist()returnJsonResponse({'code':0,'data':{'class_dist':class_dist,'trend':trend,'conf_hist':conf_hist,'task_type':task_type,'heat':heat,'days':days}})# ============ 内部处理函数 ============def_detect(rgb):result=_YOLO.predict(source=rgb,imgsz=IMG_SIZE,conf=CONF_THR,iou=IOU_THR,verbose=False)[0]boxes=[]forxyxy,cls,confinzip(result.boxes.xyxy.tolist(),result.boxes.cls.tolist(),result.boxes.conf.tolist()):name=result.names[int(cls)]boxes.append({'class':LABELS_ZH.get(name,name),'conf':round(float(conf),4),'box':[round(float(v),1)forvinxyxy]})returnsorted(boxes,key=lambdab:-b['conf'])def_energy(top_conf):returnfloat(-np.log(max(float(top_conf),1e-6)))def_write_event(request,task_type,top1,conf,ok):recognitionevent.objects.create(user=int(request.session.get('user_id',0)),task_type=task_type,model='yolo26m',top1_class=top1,confidence=round(float(conf),4),is_success=bool(ok))def_save_record(request,task_type,raw,boxes,width,height,latency):thumb=Image.open(io.BytesIO(raw)).convert('RGB')thumb.thumbnail((320,320))buf=io.BytesIO()thumb.save(buf,format='JPEG',quality=80)returndetectionrecord.objects.create(user=int(request.session.get('user_id',0)),task_type=task_type,detected_classes=','.join(sorted({b['class']forbinboxes})),confidence_avg=round(float(np.mean([b['conf']forbinboxes])),4),result_json=json.dumps(boxes,ensure_ascii=False),inference_latency_ms=latency,original_w=width,original_h=height,model_version='yolo26m-onnx',thumbnail_base64=base64.b64encode(buf.getvalue()).decode())def_is_admin(request):returnrequest.session.get('role')=='admin'

医疗废弃物识别系统-结语

如果这期分享对你准备计算机毕设有帮助,别忘了顺手点赞、收藏、关注支持一下,你的支持就是我持续更新的动力。
也欢迎大家在评论区多交流技术和选题想法,你踩过的坑、卡住的地方,都可以拿出来说说,互相参考,争取计算机毕设顺顺利利通过、答辩稳稳过关!

实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流!
如果遇到具体的技术问题或其他需求,你也可以问我,我会尽力帮你分析和解决问题所在,支持我记得点赞、收藏,再点个关注,学习不迷路!~~

返回列表