diff --git a/app.py b/app.py index 82eb89c..1d80157 100644 --- a/app.py +++ b/app.py @@ -25,187 +25,11 @@ st.set_page_config( initial_sidebar_state="expanded", ) -# ─── Custom CSS ────────────────────────────────────────────────────────────── +# ─── Minimal CSS ───────────────────────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) @@ -423,11 +247,11 @@ def build_index() -> tuple[list[dict], list[str], list[str]]: # ─── Sidebar ───────────────────────────────────────────────────────────────── with st.sidebar: - st.markdown('', unsafe_allow_html=True) - st.markdown('', unsafe_allow_html=True) - st.markdown("
", unsafe_allow_html=True) + st.header("🌿 病虫害以图搜图") + st.caption("上传图片,智能识别相似病虫害") + st.divider() - st.markdown('
🖼️ 输入方式
', unsafe_allow_html=True) + st.subheader("🖼️ 输入方式") input_mode = st.radio("", ["上传本地图片", "输入图片 URL", "选择示例图片"], label_visibility="collapsed") # 初始化 session_state @@ -453,7 +277,7 @@ with st.sidebar: if query_url.strip(): query_source = query_url.strip() else: - st.markdown('
点击选择示例
', unsafe_allow_html=True) + st.caption("点击选择示例") cols = st.columns(2) for idx, (name, url) in enumerate(EXAMPLE_IMAGES): with cols[idx % 2]: @@ -466,42 +290,32 @@ with st.sidebar: query_source = query_url st.image(query_url, use_container_width=True) - st.markdown('
⚙️ 搜索设置
', unsafe_allow_html=True) + st.subheader("⚙️ 搜索设置") top_k = st.slider("返回条数", 1, min(12, len(PEST_KNOWLEDGE)), 5) - st.markdown("
", unsafe_allow_html=True) search_clicked = st.button("开始搜索", type="primary", use_container_width=True) - st.markdown("
", unsafe_allow_html=True) - st.markdown(""" -
- 使用说明
- 1. 上传病虫害患处图片
- 2. 系统自动提取图像特征
- 3. 与知识库比对返回相似结果
- 4. 参考症状与防治建议 -
- """, unsafe_allow_html=True) + st.divider() + st.info( + "**使用说明**\n\n" + "1. 上传病虫害患处图片\n" + "2. 系统自动提取图像特征\n" + "3. 与知识库比对返回相似结果\n" + "4. 参考症状与防治建议" + ) # ─── Build Index ───────────────────────────────────────────────────────────── index_items, succeeded, failed = build_index() # ─── Main Layout ───────────────────────────────────────────────────────────── -st.markdown(""" -
-
病虫害以图搜图
-
-
基于 CLIP 视觉模型的病虫害相似度检索与防治建议
-""", unsafe_allow_html=True) +st.title("🌿 病虫害以图搜图") +st.caption("基于 CLIP 视觉模型的病虫害相似度检索与防治建议") # Status badges -badges = [] if succeeded: - badges.append(f'📚 知识库 {len(succeeded)} 种') + st.badge(f"📚 知识库 {len(succeeded)} 种", color="blue") if failed: - badges.append(f'⚠️ 索引失败 {len(failed)} 种') -if badges: - st.markdown(f"
{''.join(badges)}
", unsafe_allow_html=True) + st.badge(f"⚠️ 索引失败 {len(failed)} 种", color="red") st.markdown("
", unsafe_allow_html=True) @@ -516,11 +330,11 @@ if search_clicked: if query_img is not None: col_query, col_preview = st.columns([1, 3]) with col_query: - st.markdown('
🔍 查询图片
', unsafe_allow_html=True) + st.subheader("🔍 查询图片") st.image(query_img, use_container_width=True) with col_preview: - st.markdown('
⏳ 正在分析...
', unsafe_allow_html=True) + st.subheader("⏳ 正在分析...") progress = st.progress(0, text="提取图像特征...") embedder = get_embedder() @@ -536,7 +350,7 @@ if search_clicked: progress.progress(100, text="搜索完成") progress.empty() - st.markdown(f'
🏆 搜索结果(Top-{len(results)})
', unsafe_allow_html=True) + st.subheader(f"🏆 搜索结果(Top-{len(results)})") # Similarity bar chart names = [f"{r[1]['name']}" for r in results] @@ -566,49 +380,34 @@ if search_clicked: st.plotly_chart(fig_bar, use_container_width=True) # Result cards below - st.markdown('
📋 详细结果
', unsafe_allow_html=True) + st.subheader("📋 详细结果") for rank, (sim, item) in enumerate(results, 1): - with st.container(): - st.markdown(f""" -
-
-
- -
-
-
- {rank} - {item['name']} - 相似度 {sim*100:.1f}% -
-
- {item['crop']} - {item['category']} -
-
- 症状:{item['symptoms']}
- 防治:{item['treatment']} -
-
-
-
- """, unsafe_allow_html=True) + with st.container(border=True): + c1, c2 = st.columns([1, 4]) + with c1: + st.image(item["url"], use_container_width=True) + with c2: + header_col, score_col = st.columns([3, 1]) + header_col.markdown(f"**#{rank} {item['name']}**") + score_col.markdown(f"
相似度 {sim*100:.1f}%
", unsafe_allow_html=True) + + badge_cols = st.columns([1, 1, 4]) + badge_cols[0].caption(f"🌾 {item['crop']}") + badge_cols[1].caption(f"🐛 {item['category']}" if item["category"] == "虫害" else f"🍃 {item['category']}") + + st.markdown(f"**症状:** {item['symptoms']}") + st.markdown(f"**防治:** {item['treatment']}") # Advisory summary if results: best = results[0][1] - st.markdown('
💡 初步建议
', unsafe_allow_html=True) - st.markdown(f""" -
- 系统判断该图片与 {best['name']}({best['crop']}{best['category']})最为相似,相似度 {results[0][0]*100:.1f}%
- 建议结合田间实际情况进一步确认,参考防治方案:{best['treatment']} -
- """, unsafe_allow_html=True) + st.subheader("💡 初步建议") + st.info( + f"系统判断该图片与 **{best['name']}**({best['crop']}{best['category']})最为相似," + f"相似度 **{results[0][0]*100:.1f}%**。\n\n" + f"建议结合田间实际情况进一步确认,参考防治方案:**{best['treatment']}**" + ) # ─── Footer ─────────────────────────────────────────────────────────────────── -st.markdown("
", unsafe_allow_html=True) -st.markdown(""" -
- 病虫害以图搜图 · 基于 CLIP 视觉模型 · 结果仅供参考,请结合田间实际情况判断 -
-""", unsafe_allow_html=True) +st.divider() +st.caption("病虫害以图搜图 · 基于 CLIP 视觉模型 · 结果仅供参考,请结合田间实际情况判断")