You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击按钮后如何将FastAPI模型预测结果展示到网页?

解决方案

1. 调整FastAPI后端代码

核心修改点

解决原代码中上传图片无法在前端展示的问题,改用Base64编码传递图片内容,同时计算全部分类的预测分数用于展示:

from fastapi import FastAPI, File, UploadFile, Request
import uvicorn
import numpy as np
import tensorflow as tf
from keras.applications.vgg16 import preprocess_input
import os
from fastapi.templating import Jinja2Templates
import shutil
from pathlib import Path
from tempfile import NamedTemporaryFile
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
import base64  # 新增:用于图片Base64编码

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")

origins = [
    "http://127.0.0.1",
    "http://127.0.0.1:80",
]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


templates = Jinja2Templates(directory="templates/")

MODEL = tf.keras.models.load_model("./Skin Cancer.h5")

classes = {4: ('nv', ' melanocytic nevi'),
           6: ('mel', 'melanoma'),
           2 :('bkl', 'benign keratosis-like lesions'), 
           1:('bcc' , ' basal cell carcinoma'),
           5: ('vasc', ' pyogenic granulomas and hemorrhage'),
           0: ('akiec', 'Actinic keratoses and intraepithelial carcinomae'),
           3: ('df', 'dermatofibroma')}

def model_predict(img_path, MODEL):
    img = tf.keras.utils.load_img(img_path, target_size = (28,28))
    img = tf.keras.utils.img_to_array(img)
    img = np.expand_dims(img, axis=0)
    img_data = preprocess_input(img)
    preds = MODEL.predict(img_data)
    return preds

def save_upload_file_tmp(upload_file: UploadFile) -> Path:
    try:
        suffix = Path(upload_file.filename).suffix
        with NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
            shutil.copyfileobj(upload_file.file, tmp)
            tmp_path = Path(tmp.name)
    finally:
        upload_file.file.close()
    return tmp_path

@app.get("/ping")
def ping():
    return "pinging!"

@app.get("/")
def home(request: Request):
    return templates.TemplateResponse("index.html", {'request':request})

@app.post("/predict")
async def predict(
    request: Request,
    file: UploadFile = File(...)
):
    # 读取图片并转为Base64格式,用于前端展示
    img_data = await file.read()
    img_base64 = base64.b64encode(img_data).decode("utf-8")
    img_uri = f"data:image/{file.content_type.split('/')[1]};base64,{img_base64}"

    # 保存临时文件用于模型预测
    filepath = save_upload_file_tmp(file)
    predictions = model_predict(filepath, MODEL)
    filepath.unlink()

    # 计算预测结果
    class_lab = [0,1,2,3,4,5,6]
    class_score = tf.nn.softmax(predictions[0])
    conf = float(np.max(class_score)) * 100  # 用softmax后的分数计算置信度,结果更合理
    cl = class_lab[tf.argmax(class_score)]
    cancer_name = classes[cl][1]

    # 整理所有类别的预测分数,用于前端展示
    all_predictions = []
    for idx, score in enumerate(class_score):
        all_predictions.append({
            "name": classes[idx][1],
            "score": float(score) * 100
        })

    return templates.TemplateResponse("pred.html", {
        "request": request,
        "img_uri": img_uri,
        "predicted_class": cancer_name,
        "confidence": round(conf, 2),
        "all_predictions": all_predictions
    })

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=80)

2. 编写上传页面模板(templates/index.html)

创建带文件上传表单的页面,提交后触发预测接口:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>皮肤癌图像检测</title>
    <style>
        .container {
            max-width: 600px;
            margin: 2rem auto;
            padding: 0 1rem;
        }
        .upload-form {
            display: flex;
            flex-direction: column;
            gap: 1rem;
            margin-top: 2rem;
        }
        input[type="file"] {
            padding: 0.5rem;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        button {
            padding: 0.75rem;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>皮肤癌图像检测</h1>
        <form class="upload-form" action="/predict" method="post" enctype="multipart/form-data">
            <input type="file" name="file" accept="image/*" required>
            <button type="submit">开始预测</button>
        </form>
    </div>
</body>
</html>

3. 编写结果展示模板(templates/pred.html)

用网格布局展示上传图片和所有预测分数:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>预测结果</title>
    <style>
        .result-container {
            max-width: 850px;
            margin: 2rem auto;
            padding: 0 1rem;
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 2rem;
        }
        .card {
            border: 1px solid #eee;
            border-radius: 8px;
            padding: 1.5rem;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .img-card img {
            max-width: 100%;
            border-radius: 4px;
            margin-bottom: 1rem;
        }
        .top-pred {
            font-size: 1.5rem;
            font-weight: bold;
            color: #dc3545;
            margin-bottom: 2rem;
        }
        .score-list {
            display: flex;
            flex-direction: column;
            gap: 0.8rem;
        }
        .score-item {
            display: flex;
            justify-content: space-between;
            padding-bottom: 0.5rem;
            border-bottom: 1px solid #eee;
        }
        .back-btn {
            margin-top: 2rem;
            padding: 0.75rem 1.5rem;
            background-color: #6c757d;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="result-container">
        <div class="card img-card">
            <h3>上传的图像</h3>
            <img src="{{ img_uri }}" alt="皮肤检测图像">
        </div>
        <div class="card pred-card">
            <h2>预测结果</h2>
            <div class="top-pred">
                {{ predicted_class }}<br>
                <span style="font-size: 1rem; font-weight: normal; color: #666;">置信度: {{ confidence }}%</span>
            </div>
            <h3>全部分类分数</h3>
            <div class="score-list">
                {% for pred in all_predictions %}
                <div class="score-item">
                    <span>{{ pred.name }}</span>
                    <span>{{ round(pred.score, 2) }}%</span>
                </div>
                {% endfor %}
            </div>
            <button class="back-btn" onclick="window.location.href='/'">返回上传页面</button>
        </div>
    </div>
</body>
</html>

4. 运行验证

  1. 确保项目目录下存在templates文件夹,放入上述两个HTML文件
  2. 确保static文件夹存在(即使为空)
  3. 执行python main.py启动服务
  4. 访问http://127.0.0.1,上传图片后点击预测,将自动跳转到结果页面,以网格形式展示图片和所有预测分数

内容的提问来源于stack exchange,提问作者Jinen Rathore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:00:59