puruan/app.py
2026-02-04 09:54:24 +08:00

193 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import streamlit as st
import subprocess
import os
from PIL import Image
# === 页面基础配置 ===
st.set_page_config(layout="wide", page_title="多核违建检测平台 v3.0")
st.title("🚁 多核无人机违建检测平台 v3.0")
st.caption("集成内核: DINOv2 Giant | DiffSim-Pro v2.1 | DreamSim Ensemble")
# ==========================================
# 1. 侧边栏:算法核心选择
# ==========================================
st.sidebar.header("🧠 算法内核 (Core)")
algo_type = st.sidebar.radio(
"选择检测模式",
(
"DreamSim(语义/感知) 🔥",
#"DINOv2 Giant (切片/精度)",
#"DiffSim (结构/抗视差)",
#"DINOv2 Giant (全图/感知)"
),
index=0, # 默认选中 DINOv2 切片版,因为它是目前效果最好的
help="DINOv2: 几何结构最敏感,抗光照干扰。\nDiffSim: 可调参数多,适合微调。\nDreamSim: 关注整体风格差异。"
)
st.sidebar.markdown("---")
st.sidebar.header("🛠️ 参数配置")
# 初始化默认参数变量
script_name = ""
cmd_extra_args = []
show_slice_params = True # 默认显示切片参数
# ==========================================
# 2. 根据选择配置参数
# ==========================================
if "DINOv2" in algo_type:
# === DINOv2 通用配置 ===
st.sidebar.info(f"当前内核: {algo_type.split(' ')[0]}")
# 阈值控制 (DINO 的差异值通常较小,默认给 0.3)
thresh = st.sidebar.slider("敏感度阈值 (Thresh)", 0.0, 1.0, 0.30, 0.01, help="值越小越敏感,值越大越只看显著变化")
# 区分全图 vs 切片
if "切片" in algo_type:
script_name = "main_dinov2_sliced.py"
show_slice_params = True
st.sidebar.caption("✅ 适用场景4K/8K 大图,寻找细微违建。")
else:
script_name = "main_dinov2.py" # 对应你之前的 main_dinov2_giant.py (全图版)
show_slice_params = False # 全图模式不需要调切片
st.sidebar.caption("⚡ 适用场景:快速扫描,显存充足,寻找大面积变化。")
elif "DiffSim" in algo_type:
# === DiffSim 配置 ===
script_name = "main_finally.py"
show_slice_params = True # DiffSim 需要切片
st.sidebar.subheader("1. 感知权重")
w_struct = st.sidebar.slider("结构权重 (Struct)", 0.0, 1.0, 0.3, 0.05)
w_sem = st.sidebar.slider("语义权重 (Sem)", 0.0, 1.0, 0.7, 0.05)
w_tex = st.sidebar.slider("纹理权重 (Texture)", 0.0, 1.0, 0.0, 0.05)
st.sidebar.subheader("2. 信号处理")
kernel = st.sidebar.number_input("抗视差窗口 (Kernel)", value=5, step=2)
gamma = st.sidebar.slider("Gamma 压制", 0.5, 4.0, 1.0, 0.1)
thresh = st.sidebar.slider("可视化阈值", 0.0, 1.0, 0.15, 0.01)
# 封装 DiffSim 独有的参数
cmd_extra_args = [
"--model", "Manojb/stable-diffusion-2-1-base",
"--w_struct", str(w_struct),
"--w_sem", str(w_sem),
"--w_tex", str(w_tex),
"--gamma", str(gamma),
"--kernel", str(kernel)
]
else: # DreamSim
# === DreamSim 配置 ===
script_name = "main_dreamsim.py"
show_slice_params = True
st.sidebar.subheader("阈值控制")
thresh = st.sidebar.slider("可视化阈值 (Thresh)", 0.0, 1.0, 0.3, 0.01)
# ==========================================
# 3. 公共参数 (切片策略)
# ==========================================
if show_slice_params:
st.sidebar.subheader("🚀 扫描策略")
# DINOv2 切片版建议 Batch 小一点
default_batch = 8 if "DINOv2" in algo_type else 16
crop_size = st.sidebar.number_input("切片大小 (Crop)", value=224)
step_size = st.sidebar.number_input("步长 (Step, 0=自动)", value=0)
batch_size = st.sidebar.number_input("批次大小 (Batch)", value=default_batch)
else:
# 全图模式,隐藏参数但保留变量防报错
st.sidebar.success("全图模式:无需切片设置 (One-Shot)")
crop_size, step_size, batch_size = 224, 0, 1
# ==========================================
# 4. 主界面:图片上传与执行
# ==========================================
col1, col2 = st.columns(2)
with col1:
file_t1 = st.file_uploader("上传基准图 (Base / Old)", type=["jpg","png","jpeg"], key="t1")
if file_t1: st.image(file_t1, use_column_width=True)
with col2:
file_t2 = st.file_uploader("上传现状图 (Current / New)", type=["jpg","png","jpeg"], key="t2")
if file_t2: st.image(file_t2, use_column_width=True)
st.markdown("---")
# 启动按钮
if st.button("🚀 启动检测内核", type="primary", use_container_width=True):
if not file_t1 or not file_t2:
st.error("请先上传两张图片!")
else:
# 1. 保存临时文件
os.makedirs("temp_uploads", exist_ok=True)
t1_path = os.path.join("temp_uploads", "t1.jpg")
t2_path = os.path.join("temp_uploads", "t2.jpg")
# 结果文件名根据算法区分,防止缓存混淆
result_name = f"result_{script_name.replace('.py', '')}.jpg"
out_path = os.path.join("temp_uploads", result_name)
with open(t1_path, "wb") as f: f.write(file_t1.getbuffer())
with open(t2_path, "wb") as f: f.write(file_t2.getbuffer())
# 2. 构建命令
# 基础命令: python3 script.py t1 t2 out
cmd = ["python3", script_name, t1_path, t2_path, out_path]
# 添加通用参数 (所有脚本都兼容 -c -s -b --thresh 格式)
# 注意:即使全图模式脚本忽略 -c -s传进去也不会报错保持逻辑简单
cmd.extend([
"--crop", str(crop_size),
"--step", str(step_size),
"--batch", str(batch_size),
"--thresh", str(thresh)
])
# 添加特定算法参数 (DiffSim)
if cmd_extra_args:
cmd.extend(cmd_extra_args)
# 3. 显示状态与运行
st.info(f"⏳ 正在调用内核: `{script_name}` ...")
st.text(f"执行命令: {' '.join(cmd)}") # 方便调试
try:
# 实时显示进度条可能比较难,这里用 spinner
with st.spinner('AI 正在进行特征提取与比对... (DINO Giant 可能需要几秒钟)'):
result = subprocess.run(cmd, capture_output=True, text=True)
# 4. 结果处理
# 展开日志供查看
with st.expander("📄 查看内核运行日志", expanded=(result.returncode != 0)):
if result.stdout: st.code(result.stdout, language="bash")
if result.stderr: st.error(result.stderr)
if result.returncode == 0:
st.success(f"✅ 检测完成!耗时逻辑已结束。")
# 结果展示区
r_col1, r_col2 = st.columns(2)
with r_col1:
# 尝试读取调试热力图 (如果有的话)
if os.path.exists("debug_raw_heatmap.png"):
st.image("debug_raw_heatmap.png", caption="🔍 原始差异热力图 (Debug)", use_column_width=True)
else:
st.warning("无调试热力图生成")
with r_col2:
if os.path.exists(out_path):
# 使用 PIL 打开以强制刷新缓存 (Streamlit 有时会缓存同名图片)
res_img = Image.open(out_path)
st.image(res_img, caption=f"🎯 最终检测结果 ({algo_type})", use_column_width=True)
else:
st.error(f"❌ 未找到输出文件: {out_path}")
else:
st.error("❌ 内核运行出错,请检查上方日志。")
except Exception as e:
st.error(f"系统错误: {e}")