关于前端页面上传图片检测

  1. 依赖于前文,linux系统上部署yolo识别图片,远程宿主机访问docker全流程(https://blog.csdn.net/yanzhuang521967/article/details/148777650?spm=1001.2014.3001.5501)
    fastapi把端口暴露出来

  2. 后端代码

from fastapi import FastAPI, UploadFile, File, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, StreamingResponse, Response
from starlette.responses import RedirectResponse
from urllib.parse import urlparse
from ultralytics import YOLO
import os
import json
from pathlib import Path
from fastapi.staticfiles import StaticFiles
import logging
import io
from PIL import Image# 初始化应用
app = FastAPI()# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)# 静态文件服务
app.mount("/output-predict", StaticFiles(directory="/usr/src/output/predict"), name="output-predict")
app.mount("/output-labels", StaticFiles(directory="/usr/src/output/predict/labels"), name="output-labels")# CORS配置
app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"],expose_headers=["*"]
)# 修复后的协议转换中间件
@app.middleware("http")
async def protocol_converter(request: Request, call_next):try:# 处理HTTPS转HTTP(如果需要)if request.url.scheme == "https":url = str(request.url).replace("https://", "http://", 1)logger.info(f"Converting HTTPS to HTTP: {url}")scope = request.scopescope["scheme"] = "http"headers = []for k, v in scope["headers"]:if k == b"referer":headers.append((k, v.replace(b"https://", b"http://")))else:headers.append((k, v))scope["headers"] = headersresponse = await call_next(request)# 不处理流式响应和重定向if isinstance(response, (StreamingResponse, RedirectResponse)):return response# 获取响应体(兼容新旧版本)if hasattr(response, "body_iterator"):# 处理StreamingResponsebody = b"".join([chunk async for chunk in response.body_iterator])else:# 普通响应body = await response.body()# 替换内容中的HTTPS链接(如果需要)if body and b"https://" in body:body = body.replace(b"https://", b"http://")logger.debug("Replaced HTTPS links in response body")return Response(content=body,status_code=response.status_code,media_type=response.media_type,headers=dict(response.headers))except Exception as e:logger.error(f"Protocol converter error: {str(e)}", exc_info=True)raise HTTPException(status_code=500, detail="Internal server error")# 安全头中间件
@app.middleware("http")
async def security_headers(request: Request, call_next):response = await call_next(request)response.headers.update({"Access-Control-Allow-Private-Network": "true","Cross-Origin-Resource-Policy": "cross-origin","X-Content-Type-Options": "nosniff"})return response# 初始化模型和目录
model = YOLO("/ultralytics/yolo11n.pt")
output_base = Path("/usr/src/output")
predict_dir = output_base / "predict"
(predict_dir / "labels").mkdir(parents=True, exist_ok=True)# 辅助函数
async def save_upload_file(file: UploadFile) -> str:"""保存上传文件到临时位置"""temp_path = f"/tmp/{file.filename}"try:with open(temp_path, "wb") as f:content = await file.read()f.write(content)return temp_pathexcept Exception as e:logger.error(f"File save failed: {str(e)}")raise HTTPException(500, "File upload failed")# API端点
@app.post("/predict")
async def predict(request: Request, file: UploadFile = File(...)):temp_path = Nonetry:# 1. 保存文件temp_path = await save_upload_file(file)# 2. 运行预测results = model.predict(source=temp_path,project=str(predict_dir),name="",save=True,save_txt=True,save_conf=True,exist_ok=True)# 3. 准备结果(使用新的to_json()方法)file_stem = Path(file.filename).stembase_url = str(request.base_url).replace("https://", "http://")json_result = {"filename": file.filename,"detections": json.loads(results[0].to_json()),  # 使用to_json()替代tojson()"image_path": f"{base_url}output-predict/{file.filename}","label_path": f"{base_url}output-labels/{file_stem}.txt","speed": {"preprocess": results[0].speed["preprocess"],"inference": results[0].speed["inference"],"postprocess": results[0].speed["postprocess"]}}# 4. 保存JSONjson_path = predict_dir / "labels" / f"{file_stem}.json"with open(json_path, "w") as f:json.dump(json_result, f, indent=2)return JSONResponse({"status": "success","data": json_result,"debug": {"original_protocol": request.url.scheme,"processed_protocol": "http"}})except Exception as e:logger.error(f"Prediction failed: {str(e)}", exc_info=True)raise HTTPException(status_code=500, detail=str(e))finally:if temp_path and os.path.exists(temp_path):os.remove(temp_path)# 健康检查端点
@app.get("/health")
async def health_check():return {"status": "healthy", "protocol": "http"}# 协议检查端点
@app.get("/check-protocol")
async def check_protocol(request: Request):return {"client_protocol": request.url.scheme,"server_protocol": "http","headers": dict(request.headers)}
  1. 前端代码
   <!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 在HTML的<head>中添加 --><title>YOLOv8 图像检测系统</title><style>body {font-family: Arial, sans-serif;max-width: 1200px;margin: 0 auto;padding: 20px;line-height: 1.6;}.upload-container {text-align: center;margin-bottom: 30px;}.upload-area {border: 2px dashed #ccc;border-radius: 8px;padding: 40px;margin: 20px 0;cursor: pointer;transition: all 0.3s;}.upload-area:hover {border-color: #4CAF50;background-color: #f9f9f9;}#fileInput {display: none;}button {background-color: #4CAF50;color: white;border: none;padding: 10px 20px;border-radius: 4px;cursor: pointer;font-size: 16px;transition: background 0.3s;}button:hover {background-color: #45a049;}button:disabled {background-color: #cccccc;cursor: not-allowed;}.status {margin: 20px 0;padding: 15px;border-radius: 4px;display: none;}.loading {background-color: #e8f5e9;color: #2e7d32;}.error {background-color: #ffebee;color: #f44336;}.image-container {display: flex;flex-wrap: wrap;gap: 20px;margin-top: 30px;}.image-box {flex: 1;min-width: 300px;margin-bottom: 20px;}.image-box img {max-width: 100%;border-radius: 4px;box-shadow: 0 2px 10px rgba(0,0,0,0.1);}.results-panel {margin-top: 30px;padding: 20px;background-color: #f5f5f5;border-radius: 8px;}pre {background-color: #eee;padding: 15px;border-radius: 4px;overflow-x: auto;}.spinner {border: 4px solid rgba(0,0,0,0.1);border-radius: 50%;border-top: 4px solid #4CAF50;width: 40px;height: 40px;animation: spin 1s linear infinite;margin: 20px auto;}@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }}</style>
</head>
<body><div class="upload-container"><h1>YOLOv8 图像检测系统</h1><div class="upload-area" id="dropZone"><p>点击或拖拽图片到此处上传</p><input type="file" id="fileInput" accept="image/*"></div><button id="detectBtn" disabled>开始检测</button><div id="loadingStatus" class="status loading"><div class="spinner"></div><p>正在处理图像,请稍候...</p></div><div id="errorStatus" class="status error"><h3>检测失败</h3><p id="errorMessage"></p></div></div><div class="image-container"><div class="image-box"><h3>原始图片</h3><img id="preview" style="display: none;"></div><div class="image-box"><h3>检测结果</h3><img id="result" style="display: none;"></div></div><div class="results-panel" id="resultsPanel" style="display: none;"><h3>检测结果数据</h3><pre id="jsonData"></pre></div><script>// DOM元素const fileInput = document.getElementById('fileInput');const dropZone = document.getElementById('dropZone');const detectBtn = document.getElementById('detectBtn');const preview = document.getElementById('preview');const result = document.getElementById('result');const loadingStatus = document.getElementById('loadingStatus');const errorStatus = document.getElementById('errorStatus');const errorMessage = document.getElementById('errorMessage');const resultsPanel = document.getElementById('resultsPanel');const jsonData = document.getElementById('jsonData');// 当前处理的文件let currentFile = null;// 文件选择处理fileInput.addEventListener('change', handleFileSelect);dropZone.addEventListener('click', () => fileInput.click());// 拖放功能dropZone.addEventListener('dragover', (e) => {e.preventDefault();dropZone.style.borderColor = '#4CAF50';dropZone.style.backgroundColor = '#f0fff0';});dropZone.addEventListener('dragleave', () => {dropZone.style.borderColor = '#ccc';dropZone.style.backgroundColor = '';});dropZone.addEventListener('drop', (e) => {e.preventDefault();dropZone.style.borderColor = '#ccc';dropZone.style.backgroundColor = '';if (e.dataTransfer.files.length) {fileInput.files = e.dataTransfer.files;handleFileSelect({ target: fileInput });}});// 检测按钮点击detectBtn.addEventListener('click', startDetection);function handleFileSelect(event) {const file = event.target.files[0];if (file && file.type.match('image.*')) {currentFile = file;const reader = new FileReader();reader.onload = (e) => {preview.src = e.target.result;preview.style.display = 'block';detectBtn.disabled = false;// 重置状态result.style.display = 'none';resultsPanel.style.display = 'none';errorStatus.style.display = 'none';};reader.readAsDataURL(file);}}async function startDetection() {if (!currentFile) return;// 显示加载状态loadingStatus.style.display = 'block';errorStatus.style.display = 'none';detectBtn.disabled = true;try {// 1. 上传图片进行预测const formData = new FormData();formData.append('file', currentFile);const response = await fetch('http://192.168.0.100:34567/predict', {method: 'POST',mode: 'cors', // 确保这是cors而不是no-corsbody: formData});if (!response.ok) {const error = await response.text();throw new Error(error || `服务器错误: ${response.status}`);}const resultData = await response.json();// 2. 显示处理后的图片const filename = currentFile.name.split('.')[0];result.src = `http://192.168.0.100:34567/output-predict/predict/${filename}.jpg?t=${Date.now()}`;result.style.display = 'block';// 3. 显示JSON数据jsonData.textContent = JSON.stringify(resultData, null, 2);resultsPanel.style.display = 'block';} catch (error) {console.error('检测失败:', error);errorMessage.textContent = error.message;errorStatus.style.display = 'block';} finally {loadingStatus.style.display = 'none';detectBtn.disabled = false;}}</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/88969.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/88969.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十三章---软件工程过程管理

仅供参考 文章目录 一、Gantt图是做什么的。二、软件配置的概念 一、Gantt图是做什么的。 Gantt 图&#xff08;甘特图&#xff09;是软件项目管理中用于进度安排和可视化管理的重要工具&#xff0c;主要用于展示任务的时间安排、进度状态及任务之间的依赖关系 Gantt 图是一种…

多模态大语言模型arxiv论文略读(140)

SemiHVision: Enhancing Medical Multimodal Models with a Semi-Human Annotated Dataset and Fine-Tuned Instruction Generation ➡️ 论文标题&#xff1a;SemiHVision: Enhancing Medical Multimodal Models with a Semi-Human Annotated Dataset and Fine-Tuned Instruc…

模型预测控制专题:无差拍预测电流控制

前言&#xff1a; 为了进一步深入探索电机控制这个领域&#xff0c;找到了一些志同道合的同学一起来进行知识的分享。最近群里投票后续更新内容&#xff0c;票数最多的方向就是模型预测控制&#xff1b;无论这个方向目前是否还是很火&#xff0c;至少应大家需求&#xff0c;工…

Youtube双塔模型

1. 引言 在大规模推荐系统中&#xff0c;如何从海量候选物品中高效检索出用户可能感兴趣的物品是一个关键问题。传统的矩阵分解方法在处理稀疏数据和长尾分布时面临挑战。本文介绍了一种基于双塔神经网络的建模框架&#xff0c;通过采样偏差校正技术提升推荐质量&#xff0c;并…

.net8创建tcp服务接收数据通过websocket广播

注册TCP服务器 注册WebSocket中间件 using System.Net; using System.Net.Sockets; using System.Text; using System.Text.Json; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.AspNet…

阅读服务使用示例(HarmonyOS Reader Kit)

阅读服务使用示例&#xff08;HarmonyOS Reader Kit&#xff09; Reader Kit到底能干啥&#xff1f; 第一次搞电子书阅读器&#xff0c;真以为就是“读txt显示出来”这么简单&#xff0c;结果各种格式、排版、翻页动效、目录跳转……全是坑。还好有Reader Kit&#xff0c;救了…

ASP.NET Core Web API 实现 JWT 身份验证

在ASP.NET Core WebApi中使用标识框架&#xff08;Identity)-CSDN博客 因为一般需要和标识框架一起使用,建议先查看标识框架用法 一.为什么需要JWT 我们的系统需要实现认证,即服务端需要知道登录进来的客户端的身份,管理员有管理员的权限,普通用户有普通用户的权限. 但服务…

优化Cereal宏 一行声明序列化函数

Cereal序列化库中宏递归展开的优化方案及技术解析 未优化&#xff1a;参考nlohmann json设计Cereal宏 一行声明序列化函数 宏实现 #include <cereal/cereal.hpp>// 强制二次展开 #define CEREAL_EXPAND( x ) x// 获取宏参数的数量&#xff0c;对应的CEREAL_PASTEn宏NAME…

14-C#的弹出的窗口输入与输出

C#的弹出的窗口输入与输出 1.文件名输入 string fileName Interaction.InputBox("输入保存的文件名", "保存");2.弹窗信息输出 MessageBox.Show("请选择轮询!", "Error", MessageBoxButtons.OK);catch (Exception ex){MessageBox.S…

多模态大语言模型arxiv论文略读(141)

Mini-InternVL: A Flexible-Transfer Pocket Multimodal Model with 5% Parameters and 90% Performance ➡️ 论文标题&#xff1a;Mini-InternVL: A Flexible-Transfer Pocket Multimodal Model with 5% Parameters and 90% Performance ➡️ 论文作者&#xff1a;Zhangwei …

VScode使用usb转网口远程开发rk3588

我使用的是鲁班猫的板&#xff0c;只有一个网口&#xff0c;需要接雷达&#xff0c;因此另外弄了一个usb转网口来连接电脑开发。 在使用vscode或MobaXterm连接板子时&#xff0c;使用主机名与用户名来连接&#xff1a; ssh catlubancat rk那边就直接插入usb转网口以及网线&a…

AUTOSAR图解==>AUTOSAR_AP_EXP_SOVD

AUTOSAR服务导向车辆诊断详解 面向现代化车辆架构的诊断方案 目录 1. 引言 1.1 ASAM SOVD简介1.2 SOVD产生的动机 2. SOVD参考架构 2.1 SOVD网关2.2 诊断管理器2.3 SOVD到UDS转换2.4 后端连接 3. SOVD用例 3.1 SOVD和UDS的共同用例3.2 SOVD特定用例 3.2.1 访问权限3.2.2 软件更…

第八讲:STL简介

1. 什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复的 组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2. STL的版本 a. 原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本…

高弹性、高可靠!腾讯云 TDMQ RabbitMQ Serverless 版全新发布

导语 2025年6月起&#xff0c;腾讯云 TDMQ RabbitMQ 版正式推出 Serverless 版本&#xff0c;该版本基于自研的存算分离架构&#xff0c;兼容 AMQP 0-9-1 协议和开源 RabbitMQ 的各个组件与概念&#xff0c;且能够规避开源版本固有的不抗消息堆积、脑裂等稳定性缺陷&#xff0…

Linux 内存调优之 BPF 分析用户态小内存分配

写在前面 博文内容为 使用 BPF 工具跟踪 Linux 用户态小内存分配(brk,sbrk)理解不足小伙伴帮忙指正 😃,生活加油我看远山,远山悲悯 持续分享技术干货,感兴趣小伙伴可以关注下 _ brk 内存分配简单概述 一般来说,应用程序的数据存放于堆内存中,堆内存通过brk(2)系统调用进…

心理测评app心理测试系统框架设计

一、逻辑分析 用户管理逻辑 新用户注册&#xff1a;需要收集用户的基本信息&#xff0c;如用户名、密码、邮箱等&#xff0c;并且要对输入信息进行合法性校验&#xff0c;确保信息完整且符合格式要求。同时&#xff0c;为每个新用户生成唯一的标识符&#xff0c;方便后续数据管…

配置有nvlink的H20A800使用pytorch报错

背景 装有nvlink的h20机器上配置好驱动和cuda之后使用pytorch报错 A800机器同样 (pytorch2.4) rootxx-dev-H20:~# python Python 3.12.0 | packaged by Anaconda, Inc. | (main, Oct 2 2023, 17:29:18) [GCC 11.2.0] on linux Type “help”, “copyright”, “credits” or …

sql的语句执行过程

第一步&#xff1a;客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时&#xff0c;客户端会把这条SQL语句发送给服务器端&#xff0c;让服务器端的进程来处理这语句。也就是说&#xff0c;Oracle 客户端是不会做任何的操作&#xff0c;他的主要任务就是把客户端产生的…

深度学习-分类

深度学习-分类方式 &#xff08;重点&#xff09;一、按数据类型与处理逻辑分类1. 序列数据&#xff08;时序/顺序相关&#xff09;2. 网格状数据&#xff08;空间相关&#xff09;3. 图结构数据&#xff08;非欧几里得结构&#xff09;4. 其他特殊类型数据 &#xff08;重点&a…

C语言---常见的字符函数和字符串函数介绍

目录 前言 1 字符分类函数 2 字符转换函数 3 strlen的使用和模拟实现 3.1 strlen的模拟实现 4 strcpy的使用和模拟实现 4.1 strcpy的模拟实现 5 strcat的使用和模拟实现 5.1 strcat的模拟实现 6 strcmp的使用和模拟实现 6.1 strcmp的模拟实现 7 strncpy函数的使用…