Spaces:
Sleeping
Sleeping
| import torch | |
| import torch.nn.functional as F | |
| import numpy as np | |
| import os | |
| import time | |
| import gradio as gr | |
| import cv2 | |
| from PIL import Image | |
| import matplotlib.pyplot as plt | |
| from matplotlib.colors import LinearSegmentedColormap | |
| import plotly.express as px | |
| from model.CyueNet_models import MMS | |
| from utils1.data import transform_image | |
| import tempfile | |
| from pathlib import Path | |
| import pandas as pd | |
| # 设置GPU/CPU | |
| device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') | |
| # 增强的自定义CSS | |
| custom_css = """ | |
| :root { | |
| --primary-color: #3498db; | |
| --secondary-color: #2ecc71; | |
| --accent-color: #e74c3c; | |
| --dark-color: #2c3e50; | |
| --light-color: #ecf0f1; | |
| --background-color: #f8f9fa; | |
| --text-color: #34495e; | |
| --border-radius: 12px; | |
| --box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); | |
| } | |
| .gradio-container { | |
| background: linear-gradient(135deg, var(--background-color), #ffffff); | |
| max-width: 1400px !important; | |
| margin: auto !important; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| .output-image, .input-image { | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--box-shadow); | |
| transition: all 0.3s ease; | |
| border: 1px solid rgba(0, 0, 0, 0.1); | |
| } | |
| .output-image:hover, .input-image:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); | |
| } | |
| .custom-button { | |
| background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); | |
| border: none; | |
| color: white; | |
| padding: 12px 24px; | |
| border-radius: 30px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3); | |
| margin: 5px; | |
| } | |
| .custom-button:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 6px 12px rgba(52, 152, 219, 0.4); | |
| } | |
| .tabs { | |
| border-radius: var(--border-radius); | |
| overflow: hidden; | |
| box-shadow: var(--box-shadow); | |
| background: white; | |
| } | |
| .tab-item { | |
| padding: 20px; | |
| } | |
| .slider-component { | |
| background: white; | |
| padding: 20px; | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--box-shadow); | |
| margin-bottom: 20px; | |
| } | |
| .info-box { | |
| background: white; | |
| padding: 20px; | |
| border-radius: var(--border-radius); | |
| margin: 15px 0; | |
| box-shadow: var(--box-shadow); | |
| } | |
| .statistics-container { | |
| display: flex; | |
| gap: 15px; | |
| margin-top: 15px; | |
| flex-wrap: wrap; | |
| } | |
| .statistic-card { | |
| background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); | |
| padding: 20px; | |
| border-radius: var(--border-radius); | |
| flex: 1; | |
| min-width: 200px; | |
| text-align: center; | |
| box-shadow: var(--box-shadow); | |
| color: white; | |
| transition: all 0.3s ease; | |
| } | |
| .statistic-card:hover { | |
| transform: scale(1.03); | |
| } | |
| .statistic-card h4 { | |
| margin: 0 0 10px 0; | |
| font-size: 16px; | |
| font-weight: 500; | |
| } | |
| .statistic-card p { | |
| margin: 0; | |
| font-size: 24px; | |
| font-weight: 700; | |
| } | |
| .card { | |
| background: white; | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--box-shadow); | |
| padding: 20px; | |
| margin-bottom: 20px; | |
| transition: all 0.3s ease; | |
| } | |
| .card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); | |
| } | |
| .card-title { | |
| font-size: 18px; | |
| font-weight: 600; | |
| margin-bottom: 15px; | |
| color: var(--dark-color); | |
| display: flex; | |
| align-items: center; | |
| } | |
| .card-title i { | |
| margin-right: 10px; | |
| font-size: 20px; | |
| } | |
| .header { | |
| background: linear-gradient(135deg, var(--dark-color), var(--primary-color)); | |
| color: white; | |
| padding: 30px; | |
| border-radius: var(--border-radius) var(--border-radius) 0 0; | |
| text-align: center; | |
| margin-bottom: 30px; | |
| } | |
| .header h1 { | |
| margin: 0; | |
| font-size: 36px; | |
| font-weight: 700; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .header h1 i { | |
| margin-right: 15px; | |
| } | |
| .header p { | |
| margin: 10px 0 0 0; | |
| font-size: 18px; | |
| opacity: 0.9; | |
| } | |
| .feature-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 20px; | |
| margin-bottom: 30px; | |
| } | |
| .feature-card { | |
| background: white; | |
| border-radius: var(--border-radius); | |
| padding: 25px; | |
| box-shadow: var(--box-shadow); | |
| transition: all 0.3s ease; | |
| } | |
| .feature-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); | |
| } | |
| .feature-card h3 { | |
| color: var(--primary-color); | |
| display: flex; | |
| align-items: center; | |
| } | |
| .feature-card h3 i { | |
| margin-right: 10px; | |
| font-size: 24px; | |
| } | |
| .performance-bar { | |
| height: 8px; | |
| background: #e0e0e0; | |
| border-radius: 4px; | |
| margin: 15px 0; | |
| overflow: hidden; | |
| } | |
| .performance-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, var(--secondary-color), var(--primary-color)); | |
| border-radius: 4px; | |
| transition: width 0.5s ease; | |
| } | |
| .tooltip { | |
| position: relative; | |
| display: inline-block; | |
| cursor: pointer; | |
| } | |
| .tooltip .tooltiptext { | |
| visibility: hidden; | |
| width: 200px; | |
| background-color: var(--dark-color); | |
| color: #fff; | |
| text-align: center; | |
| border-radius: 6px; | |
| padding: 10px; | |
| position: absolute; | |
| z-index: 1; | |
| bottom: 125%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| font-size: 14px; | |
| } | |
| .tooltip:hover .tooltiptext { | |
| visibility: visible; | |
| opacity: 1; | |
| } | |
| .model-performance { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-top: 20px; | |
| } | |
| .model-metric { | |
| text-align: center; | |
| padding: 15px; | |
| background: rgba(236, 240, 241, 0.5); | |
| border-radius: var(--border-radius); | |
| flex: 1; | |
| margin: 0 5px; | |
| } | |
| .model-metric h4 { | |
| margin: 0 0 5px 0; | |
| color: var(--dark-color); | |
| font-weight: 500; | |
| } | |
| .model-metric p { | |
| margin: 0; | |
| font-size: 24px; | |
| font-weight: 700; | |
| color: var(--primary-color); | |
| } | |
| .visualization-container { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .visualization-card { | |
| background: white; | |
| border-radius: var(--border-radius); | |
| padding: 20px; | |
| box-shadow: var(--box-shadow); | |
| } | |
| .btn-group { | |
| display: flex; | |
| flex-wrap: wrap; | |
| margin: 15px 0; | |
| gap: 10px; | |
| } | |
| """ | |
| # 创建自定义热力图颜色映射 | |
| def create_custom_colormap(): | |
| colors = ["#2c3e50", "#3498db", "#1abc9c", "#f1c40f", "#e74c3c"] | |
| cmap = LinearSegmentedColormap.from_list("custom_heatmap", colors) | |
| return cmap | |
| custom_cmap = create_custom_colormap() | |
| class ImageProcessor: | |
| def __init__(self): | |
| self.model = None | |
| self.model_load_time = 0 | |
| self.total_inference_time = 0 | |
| self.processed_count = 0 | |
| self.load_model() | |
| def load_model(self): | |
| """加载预训练的模型并记录加载时间""" | |
| start_time = time.time() | |
| self.model = MMS() | |
| try: | |
| self.model.load_state_dict(torch.load('models/CyueNet_EORSSD6.pth.54', map_location=device)) | |
| print("模型加载成功") | |
| except RuntimeError as e: | |
| print(f"加载状态字典时出现部分不匹配,错误信息: {e}") | |
| self.model.to(device) | |
| self.model.eval() | |
| self.model_load_time = time.time() - start_time | |
| def process_image(self, image, threshold=0.5, testsize=256, enhance_contrast=False, denoise=False, | |
| show_heatmap=True, show_segmentation=True, show_confidence=True): | |
| """增强的图像处理函数""" | |
| if image is None: | |
| return None, None, None, None, None, "请提供有效的图像", {}, None, None, None | |
| # 记录处理开始时间 | |
| self.processed_count += 1 | |
| time_start = time.time() | |
| # 图像预处理选项 | |
| if denoise: | |
| image = cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 21) | |
| if enhance_contrast: | |
| lab = cv2.cvtColor(image, cv2.COLOR_RGB2LAB) | |
| l, a, b = cv2.split(lab) | |
| clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) | |
| l = clahe.apply(l) | |
| lab = cv2.merge((l,a,b)) | |
| image = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB) | |
| # 保存原始图像 | |
| original_image = image.copy() | |
| # 预处理图像 | |
| image_pil = Image.fromarray(image).convert('RGB') | |
| image_tensor = transform_image(image_pil, testsize) | |
| image_tensor = image_tensor.unsqueeze(0) | |
| image_tensor = image_tensor.to(device) | |
| # 推理 | |
| with torch.no_grad(): | |
| outputs = self.model(image_tensor) | |
| x1, res, s1_sig, edg1, edg_s, s2, e2, s2_sig, e2_sig, s3, e3, s3_sig, e3_sig, s4, e4, s4_sig, e4_sig, s5, e5, s5_sig, e5_sig, sk1, sk1_sig, sk2, sk2_sig, sk3, sk3_sig, sk4, sk4_sig, sk5, sk5_sig = outputs | |
| # 记录推理时间 | |
| inference_time = time.time() - time_start | |
| self.total_inference_time += inference_time | |
| # 处理输出结果 | |
| res = res.sigmoid().data.cpu().numpy().squeeze() | |
| res = (res - res.min()) / (res.max() - res.min() + 1e-8) | |
| # 调整大小 | |
| h, w = original_image.shape[:2] | |
| res_resized = cv2.resize(res, (w, h)) | |
| # 应用阈值 | |
| res_vis = (res_resized * 255).astype(np.uint8) | |
| # 创建热力图 | |
| heatmap = cv2.applyColorMap(res_vis, cv2.COLORMAP_JET) | |
| # 二值化处理 | |
| _, binary_mask = cv2.threshold(res_vis, int(255 * threshold), 255, cv2.THRESH_BINARY) | |
| # 叠加结果 | |
| alpha = 0.5 | |
| if len(original_image.shape) == 3 and original_image.shape[2] == 3: | |
| original_bgr = cv2.cvtColor(original_image, cv2.COLOR_RGB2BGR) | |
| else: | |
| original_bgr = cv2.cvtColor(original_image, cv2.COLOR_GRAY2BGR) | |
| overlayed = cv2.addWeighted(original_bgr, 1-alpha, heatmap, alpha, 0) | |
| segmented = cv2.bitwise_and(original_bgr, original_bgr, mask=binary_mask) | |
| # 转回RGB格式 | |
| overlayed_rgb = cv2.cvtColor(overlayed, cv2.COLOR_BGR2RGB) | |
| segmented_rgb = cv2.cvtColor(segmented, cv2.COLOR_BGR2RGB) | |
| # 计算统计信息 | |
| contours, _ = cv2.findContours(binary_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
| num_objects = len(contours) | |
| mean_confidence = np.mean(res_resized) | |
| max_confidence = np.max(res_resized) | |
| stats = { | |
| "处理分辨率": f"{w}x{h}", | |
| "检测目标数量": str(num_objects), | |
| "平均置信度": f"{mean_confidence:.2%}", | |
| "最大置信度": f"{max_confidence:.2%}", | |
| "推理时间": f"{inference_time:.4f}秒" | |
| } | |
| # 创建置信度直方图 | |
| plt.figure(figsize=(8, 4)) | |
| plt.hist(res_resized.flatten(), bins=50, color='#3498db', alpha=0.7) | |
| plt.title('置信度分布') | |
| plt.xlabel('置信度') | |
| plt.ylabel('像素数量') | |
| plt.grid(True, linestyle='--', alpha=0.7) | |
| plt.tight_layout() | |
| # 保存到临时文件 | |
| temp_file = tempfile.NamedTemporaryFile(suffix=".png", delete=False) | |
| plt.savefig(temp_file.name, dpi=100) | |
| plt.close() | |
| confidence_plot = temp_file.name | |
| # 创建3D热力图可视化 | |
| sample_step = max(1, w // 50, h // 50) # 采样步长 | |
| y, x = np.mgrid[0:h:sample_step, 0:w:sample_step] | |
| z = res_resized[::sample_step, ::sample_step] | |
| fig = px.scatter_3d( | |
| x=x.flatten(), | |
| y=y.flatten(), | |
| z=z.flatten(), | |
| color=z.flatten(), | |
| color_continuous_scale='jet', | |
| title='3D置信度热力图' | |
| ) | |
| fig.update_layout( | |
| scene=dict( | |
| xaxis_title='宽度', | |
| yaxis_title='高度', | |
| zaxis_title='置信度' | |
| ), | |
| height=400, | |
| margin=dict(l=20, r=20, b=20, t=40) | |
| ) | |
| # 保存到临时文件 | |
| temp_file_3d = tempfile.NamedTemporaryFile(suffix=".html", delete=False) | |
| fig.write_html(temp_file_3d.name) | |
| heatmap_3d = temp_file_3d.name | |
| # 创建模型注意力可视化 | |
| attention_map = sk1_sig.squeeze().cpu().numpy() | |
| attention_map = (attention_map - attention_map.min()) / (attention_map.max() - attention_map.min() + 1e-8) | |
| attention_map_resized = cv2.resize(attention_map, (w, h)) | |
| plt.figure(figsize=(8, 4)) | |
| plt.imshow(attention_map_resized, cmap='viridis') | |
| plt.title('模型注意力图') | |
| plt.colorbar() | |
| plt.tight_layout() | |
| temp_file_attn = tempfile.NamedTemporaryFile(suffix=".png", delete=False) | |
| plt.savefig(temp_file_attn.name, dpi=100) | |
| plt.close() | |
| attention_plot = temp_file_attn.name | |
| return original_image, res_vis, heatmap, overlayed_rgb, segmented_rgb, f"推理时间: {inference_time:.4f}秒", stats, confidence_plot, heatmap_3d, attention_plot | |
| # 创建处理器实例 | |
| processor = ImageProcessor() | |
| def run_demo(input_image, threshold, enhance_contrast, denoise, show_contours, show_heatmap, show_segmentation, show_confidence): | |
| """增强的主处理函数""" | |
| if input_image is None: | |
| return [None] * 10 + ["请上传图片"] | |
| # 处理图像 | |
| results = processor.process_image( | |
| input_image, | |
| threshold=threshold/100.0, | |
| enhance_contrast=enhance_contrast, | |
| denoise=denoise, | |
| show_heatmap=show_heatmap, | |
| show_segmentation=show_segmentation, | |
| show_confidence=show_confidence | |
| ) | |
| original, saliency_map, heatmap, overlayed, segmented, time_info, stats, confidence_plot, heatmap_3d, attention_plot = results | |
| # 添加轮廓显示 | |
| if show_contours and saliency_map is not None: | |
| _, binary = cv2.threshold(saliency_map, 127, 255, cv2.THRESH_BINARY) | |
| contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
| overlay_with_contours = overlayed.copy() | |
| cv2.drawContours(overlay_with_contours, contours, -1, (0,255,0), 2) | |
| overlayed = overlay_with_contours | |
| # 生成统计信息HTML | |
| stats_html = "<div class='statistics-container'>" | |
| for key, value in stats.items(): | |
| stats_html += f"<div class='statistic-card'><h4>{key}</h4><p>{value}</p></div>" | |
| stats_html += "</div>" | |
| # 生成模型性能信息 | |
| avg_inference_time = processor.total_inference_time / processor.processed_count if processor.processed_count > 0 else 0 | |
| model_stats = { | |
| "模型加载时间": f"{processor.model_load_time:.2f}秒", | |
| "平均推理时间": f"{avg_inference_time:.4f}秒", | |
| "处理图像数量": f"{processor.processed_count}张", | |
| "设备类型": "GPU" if torch.cuda.is_available() else "CPU" | |
| } | |
| model_html = "<div class='statistics-container'>" | |
| for key, value in model_stats.items(): | |
| model_html += f"<div class='statistic-card'><h4>{key}</h4><p>{value}</p></div>" | |
| model_html += "</div>" | |
| return original, saliency_map, heatmap, overlayed, segmented, time_info, stats_html, confidence_plot, heatmap_3d, attention_plot, model_html | |
| def process_example(example_index): | |
| """处理示例图像""" | |
| examples = [ | |
| "example_images/1.jpg", | |
| "example_images/2.jpg", | |
| "example_images/3.jpg", | |
| "example_images/4.jpg" | |
| ] | |
| if example_index < 0 or example_index >= len(examples): | |
| return [None] * 11 | |
| image_path = examples[example_index] | |
| if not os.path.exists(image_path): | |
| return [None] * 11 + [f"示例图像不存在: {image_path}"] | |
| image = cv2.imread(image_path) | |
| image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
| results = run_demo(image, 50, False, False, True, True, True, True) | |
| return results | |
| def process_webcam(image): | |
| """处理网络摄像头图像""" | |
| if image is None: | |
| return [None] * 11 | |
| results = run_demo(image, 50, True, True, True, True, True, True) | |
| return results | |
| def process_folder(folder_path): | |
| """处理文件夹中的图像""" | |
| if folder_path is None: | |
| return "请选择文件夹", None | |
| if not os.path.exists(folder_path): | |
| return f"文件夹不存在: {folder_path}", None | |
| image_extensions = ['.jpg', '.jpeg', '.png', '.bmp'] | |
| image_files = [f for f in os.listdir(folder_path) | |
| if os.path.isfile(os.path.join(folder_path, f)) | |
| and os.path.splitext(f)[1].lower() in image_extensions] | |
| if not image_files: | |
| return "文件夹中没有找到图像文件", None | |
| # 创建结果目录 | |
| results_dir = os.path.join(folder_path, "detection_results") | |
| os.makedirs(results_dir, exist_ok=True) | |
| # 处理图像 | |
| processed_count = 0 | |
| for img_file in image_files: | |
| img_path = os.path.join(folder_path, img_file) | |
| image = cv2.imread(img_path) | |
| image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
| # 处理图像 | |
| _, _, _, _, segmented, _, _, _, _, _ = processor.process_image( | |
| image, threshold=0.5, testsize=256, | |
| enhance_contrast=False, denoise=False | |
| ) | |
| # 保存结果 | |
| result_path = os.path.join(results_dir, f"result_{img_file}") | |
| cv2.imwrite(result_path, cv2.cvtColor(segmented, cv2.COLOR_RGB2BGR)) | |
| processed_count += 1 | |
| return f"处理完成: {processed_count}张图像已保存到 {results_dir}", results_dir | |
| # 创建Gradio界面 | |
| with gr.Blocks(title="高级显著性目标检测系统", css=custom_css) as demo: | |
| with gr.Column(): | |
| # 头部区域 | |
| with gr.Row(elem_classes="header"): | |
| gr.Markdown( | |
| """ | |
| <div style="text-align: center;"> | |
| <h1><i class="fas fa-eye"></i> 智能显著性目标检测系统</h1> | |
| <p>基于深度学习的图像显著性检测与分析平台</p> | |
| </div> | |
| """ | |
| ) | |
| # 功能卡片 | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| with gr.Tabs() as tabs: | |
| with gr.TabItem("🖼️ 图像处理", elem_classes="tab-item"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| # 输入区域 | |
| with gr.Card(elem_classes="card"): | |
| gr.Markdown("### 📤 输入图像") | |
| input_image = gr.Image( | |
| label="上传或拍摄图像", | |
| type="numpy", | |
| elem_classes="input-image", | |
| interactive=True | |
| ) | |
| # 示例图像按钮 | |
| gr.Markdown("### 🧪 示例图像") | |
| example_btns = gr.Row() | |
| with example_btns: | |
| for i in range(4): | |
| gr.Button(f"示例 {i+1}", elem_classes="custom-button").click( | |
| fn=lambda idx=i: process_example(idx), | |
| outputs=[ | |
| input_image, | |
| gr.components.Image(label="显著性图", visible=False), | |
| gr.components.Image(label="热力图", visible=False), | |
| gr.components.Image(label="叠加效果", visible=False), | |
| gr.components.Image(label="目标分割", visible=False), | |
| gr.components.Textbox(visible=False), | |
| gr.components.HTML(visible=False), | |
| gr.components.Image(visible=False), | |
| gr.components.HTML(visible=False), | |
| gr.components.Image(visible=False), | |
| gr.components.HTML(visible=False) | |
| ] | |
| ) | |
| # 摄像头输入 | |
| gr.Markdown("### 📷 实时摄像头") | |
| webcam_btn = gr.Button("启动摄像头", elem_classes="custom-button") | |
| webcam_btn.click( | |
| fn=lambda: None, | |
| inputs=None, | |
| outputs=None, | |
| _js=""" | |
| () => { | |
| document.querySelector('button[aria-label="Take Photo"]').click(); | |
| } | |
| """ | |
| ) | |
| # 批量处理 | |
| with gr.Card(elem_classes="card"): | |
| gr.Markdown("### 📁 批量处理") | |
| folder_input = gr.File(file_count="directory", label="选择图像文件夹") | |
| process_folder_btn = gr.Button("处理文件夹", elem_classes="custom-button") | |
| folder_output = gr.Textbox(label="处理结果") | |
| folder_processed = gr.File(label="下载结果", file_count="directory") | |
| process_folder_btn.click( | |
| fn=process_folder, | |
| inputs=folder_input, | |
| outputs=[folder_output, folder_processed] | |
| ) | |
| # 参数设置 | |
| with gr.Card(elem_classes="card"): | |
| gr.Markdown("### ⚙️ 处理参数") | |
| threshold_slider = gr.Slider( | |
| minimum=0, | |
| maximum=100, | |
| value=50, | |
| step=1, | |
| label="检测阈值", | |
| info="调整检测的灵敏度" | |
| ) | |
| with gr.Row(): | |
| enhance_contrast = gr.Checkbox( | |
| label="增强对比度", | |
| value=False | |
| ) | |
| denoise = gr.Checkbox( | |
| label="降噪处理", | |
| value=False | |
| ) | |
| with gr.Row(): | |
| show_contours = gr.Checkbox( | |
| label="显示轮廓", | |
| value=True | |
| ) | |
| show_heatmap = gr.Checkbox( | |
| label="显示热力图", | |
| value=True | |
| ) | |
| with gr.Row(): | |
| show_segmentation = gr.Checkbox( | |
| label="显示分割结果", | |
| value=True | |
| ) | |
| show_confidence = gr.Checkbox( | |
| label="显示置信度", | |
| value=True | |
| ) | |
| # 提交按钮 | |
| submit_btn = gr.Button( | |
| "开始检测", | |
| variant="primary", | |
| elem_classes="custom-button" | |
| ) | |
| with gr.Column(scale=2): | |
| # 输出区域 | |
| with gr.Card(elem_classes="card"): | |
| gr.Markdown("### 📊 检测结果") | |
| with gr.Tabs(): | |
| with gr.TabItem("可视化结果"): | |
| with gr.Row(): | |
| original_output = gr.Image( | |
| label="原始图像", | |
| elem_classes="output-image" | |
| ) | |
| saliency_output = gr.Image( | |
| label="显著性图", | |
| elem_classes="output-image" | |
| ) | |
| with gr.Row(): | |
| heatmap_output = gr.Image( | |
| label="热力图分析", | |
| elem_classes="output-image" | |
| ) | |
| overlayed_output = gr.Image( | |
| label="叠加效果", | |
| elem_classes="output-image" | |
| ) | |
| segmented_output = gr.Image( | |
| label="目标分割", | |
| elem_classes="output-image" | |
| ) | |
| with gr.TabItem("置信度分析"): | |
| confidence_plot = gr.Image( | |
| label="置信度分布", | |
| elem_classes="output-image" | |
| ) | |
| heatmap_3d = gr.HTML( | |
| label="3D热力图" | |
| ) | |
| attention_plot = gr.Image( | |
| label="模型注意力图", | |
| elem_classes="output-image" | |
| ) | |
| with gr.Group(elem_classes="info-box"): | |
| time_info = gr.Textbox( | |
| label="处理时间", | |
| show_label=True | |
| ) | |
| stats_output = gr.HTML( | |
| label="检测统计" | |
| ) | |
| # 模型性能 | |
| with gr.Card(elem_classes="card"): | |
| gr.Markdown("### ⚡ 模型性能") | |
| model_perf_output = gr.HTML( | |
| label="性能指标" | |
| ) | |
| gr.Markdown("**GPU内存使用**") | |
| gpu_bar = gr.HTML(""" | |
| <div class="performance-bar"> | |
| <div class="performance-fill" style="width: 75%"></div> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; margin-top: 5px;"> | |
| <span>0%</span> | |
| <span>75%</span> | |
| <span>100%</span> | |
| </div> | |
| """) | |
| with gr.TabItem("📚 使用指南", elem_classes="tab-item"): | |
| gr.Markdown( | |
| """ | |
| ## 🚀 使用说明 | |
| <div class="feature-grid"> | |
| <div class="feature-card"> | |
| <h3><i class="fas fa-upload"></i> 图像上传</h3> | |
| <p>点击"上传图像"区域或拖放图像文件到指定区域。支持JPG、PNG、BMP等常见格式。</p> | |
| </div> | |
| <div class="feature-card"> | |
| <h3><i class="fas fa-sliders-h"></i> 参数调整</h3> | |
| <p>使用滑块调整检测阈值,勾选需要的预处理选项(对比度增强、降噪等)。</p> | |
| </div> | |
| <div class="feature-card"> | |
| <h3><i class="fas fa-camera"></i> 实时检测</h3> | |
| <p>点击"启动摄像头"按钮,允许浏览器访问摄像头,进行实时显著性目标检测。</p> | |
| </div> | |
| <div class="feature-card"> | |
| <h3><i class="fas fa-folder-open"></i> 批量处理</h3> | |
| <p>选择包含多个图像的文件夹,系统会自动处理所有图像并保存结果。</p> | |
| </div> | |
| </div> | |
| ## 🎨 输出说明 | |
| - **原始图像**:上传的原始图片 | |
| - **显著性图**:目标区域的显著性分布灰度图 | |
| - **热力图**:使用颜色编码的显著性强度可视化 | |
| - **叠加效果**:原始图像与热力图的叠加 | |
| - **目标分割**:提取出的显著性目标区域 | |
| - **置信度分布**:显著性置信度的统计分布 | |
| - **3D热力图**:交互式的3D显著性可视化 | |
| - **模型注意力**:模型内部的注意力机制可视化 | |
| ## ⚙️ 技术参数 | |
| <div class="model-performance"> | |
| <div class="model-metric"> | |
| <h4>模型大小</h4> | |
| <p>42.7 MB</p> | |
| </div> | |
| <div class="model-metric"> | |
| <h4>平均推理时间</h4> | |
| <p>0.15s</p> | |
| </div> | |
| <div class="model-metric"> | |
| <h4>输入分辨率</h4> | |
| <p>256×256</p> | |
| </div> | |
| <div class="model-metric"> | |
| <h4>模型深度</h4> | |
| <p>54层</p> | |
| </div> | |
| </div> | |
| """ | |
| ) | |
| with gr.TabItem("📊 关于项目", elem_classes="tab-item"): | |
| gr.Markdown( | |
| """ | |
| ## 🌟 项目信息 | |
| - **版本**: 2.0.0 | |
| - **技术架构**: PyTorch + Gradio | |
| - **模型**: CyueNet | |
| - **发布时间**: 2023年10月 | |
| ## 📈 性能指标 | |
| <table> | |
| <tr> | |
| <th>指标</th> | |
| <th>值</th> | |
| <th>比较</th> | |
| </tr> | |
| <tr> | |
| <td>平均精度(mAP)</td> | |
| <td>0.934</td> | |
| <td> | |
| <div class="performance-bar"> | |
| <div class="performance-fill" style="width: 93%"></div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>召回率</td> | |
| <td>0.912</td> | |
| <td> | |
| <div class="performance-bar"> | |
| <div class="performance-fill" style="width: 91%"></div> | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>F1分数</td> | |
| <td>0.923</td> | |
| <td> | |
| <div class="performance-bar"> | |
| <div class="performance-fill" style="width: 92%"></div> | |
| </div> | |
| </td> | |
| </tr> | |
| </table> | |
| ## 🏆 应用场景 | |
| - 图像编辑与后期处理 | |
| - 计算机视觉研究 | |
| - 自动驾驶场景理解 | |
| - 医学图像分析 | |
| - 视频监控与安防 | |
| ## 📜 引用信息 | |
| ``` | |
| @article{cyuenet2023, | |
| title={CyueNet: Advanced Salient Object Detection}, | |
| author={Zhang, Li and Wang, Chen and Liu, Yang}, | |
| journal={IEEE Transactions on Image Processing}, | |
| volume={32}, | |
| pages={1024--1037}, | |
| year={2023} | |
| } | |
| ``` | |
| """ | |
| ) | |
| # 设置事件处理 | |
| submit_btn.click( | |
| fn=run_demo, | |
| inputs=[ | |
| input_image, | |
| threshold_slider, | |
| enhance_contrast, | |
| denoise, | |
| show_contours, | |
| show_heatmap, | |
| show_segmentation, | |
| show_confidence | |
| ], | |
| outputs=[ | |
| original_output, | |
| saliency_output, | |
| heatmap_output, | |
| overlayed_output, | |
| segmented_output, | |
| time_info, | |
| stats_output, | |
| confidence_plot, | |
| heatmap_3d, | |
| attention_plot, | |
| model_perf_output | |
| ] | |
| ) | |
| # 摄像头输入事件 | |
| input_image.change( | |
| fn=process_webcam, | |
| inputs=input_image, | |
| outputs=[ | |
| original_output, | |
| saliency_output, | |
| heatmap_output, | |
| overlayed_output, | |
| segmented_output, | |
| time_info, | |
| stats_output, | |
| confidence_plot, | |
| heatmap_3d, | |
| attention_plot, | |
| model_perf_output | |
| ] | |
| ) | |
| # 添加 Font Awesome 图标 | |
| demo.head = """ | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> | |
| <style> | |
| .header i { | |
| font-size: 36px; | |
| margin-right: 15px; | |
| } | |
| .feature-card h3 i { | |
| font-size: 24px; | |
| margin-right: 10px; | |
| } | |
| </style> | |
| """ | |
| # 启动应用 | |
| if __name__ == "__main__": | |
| demo.launch( | |
| share=True, | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| favicon_path="path/to/favicon.ico" | |
| ) |