yetrun's picture
ver3: 将源码迁入 src/deep_learning 包,重塑训练流水线,规范 data/model 契约,并补齐文档与测试
2c90129
Raw
History Blame Contribute Delete
7.66 kB
"""
Oxford Pets 图像分割 Gradio 交互界面。
这个文件把分割 Pipeline 包装成可体验的页面,方便上传图片或选择内置示例后查看模型预测的
灰度 mask。
"""
# ASK: 和 Yolo 的 gradio 还有测试文件做比较
from typing import cast
import gradio as gr
import numpy as np
from PIL import Image
from deep_learning.env.resolve import display_path, resolve_path, resolve_saved
from deep_learning.pipeline.services.checkpoint import describe_checkpoint_lookup, resolve_checkpoint
from deep_learning.pipeline.services.model_loader import load_inference_artifact
from tasks.segmentation.runner import pipeline
IMAGE_SIZE = (200, 200)
class SegmentationTool:
def __init__(self, pipeline, inference_artifact, resource, image_size: tuple[int, int]):
self.pipeline = pipeline
self.inference_artifact = inference_artifact
self.resource = resource
self.image_size = image_size
def segment(self, image) -> Image.Image:
input_image = Image.fromarray(np.asarray(image, dtype="uint8")).convert("RGB")
resized_image = input_image.resize(
(self.image_size[1], self.image_size[0])
)
image_array = np.asarray(resized_image, dtype="float32")
model_input = np.expand_dims(image_array, axis=0)
prediction = self.inference_artifact.model(model_input, training=False)
mask = np.argmax(np.asarray(prediction)[0], axis=-1).astype("uint8") * 127
return Image.fromarray(mask)
class SegmentationPageAdapter:
def __init__(self, pipeline):
self.pipeline = pipeline
self.sample_dir = resolve_path("data/dev/oxford_pets/images")
def list_sample_images(self) -> list[str]:
return sorted(
path.name
for path in self.sample_dir.iterdir()
if path.is_file() and path.suffix.lower() in [".jpg", ".jpeg", ".png"]
)
def load_sample_image(self, sample_name: str):
image_path = self.sample_dir / sample_name
return np.asarray(Image.open(image_path).convert("RGB"))
def extra_model_info(self) -> str:
return f"**示例图片目录**: {display_path(self.sample_dir)}"
def create_tool(self, inference_artifact, resource) -> SegmentationTool:
return SegmentationTool(self.pipeline, inference_artifact, resource, IMAGE_SIZE)
class SegmentationAppBuilder:
def __init__(
self,
pipeline,
tool_factory,
sample_images: list[str],
load_sample_image,
title: str = "Oxford Pets 图像分割",
input_label: str = "输入图片",
sample_label: str = "示例图片",
output_image_label: str = "分割结果",
extra_model_info=None
):
self.pipeline = pipeline
self.tool_factory = tool_factory
self.sample_images = sample_images
self.load_sample_image = load_sample_image
self.title = title
self.input_label = input_label
self.sample_label = sample_label
self.output_image_label = output_image_label
self.extra_model_info = extra_model_info
self._tool = None
def _resolve_checkpoint_rule(self) -> dict:
checkpoint_rule = self.pipeline.checkpoint_load_rules.resolve_test_rule(
default_dirs=[resolve_saved(f"models/{self.pipeline.name}")]
)
if checkpoint_rule["suffix"] is None:
checkpoint_rule["suffix"] = ".keras"
return checkpoint_rule
def _load_inference_artifact(self) -> tuple:
checkpoint_rule = self._resolve_checkpoint_rule()
return load_inference_artifact(
checkpoint_rule,
self.pipeline.model_builder.load_inference_artifact
)
def get_model_info(self) -> str:
parts = []
checkpoint_rule = self._resolve_checkpoint_rule()
checkpoint_path, _ = resolve_checkpoint(**checkpoint_rule)
if checkpoint_path is None:
lookup_info = describe_checkpoint_lookup(
dirs=checkpoint_rule.get("dirs"),
path=checkpoint_rule.get("path"),
suffix=checkpoint_rule.get("suffix")
)
raise FileNotFoundError(f"未找到模型检查点文件。查找信息: {lookup_info}")
file_name = checkpoint_path.name
file_size = checkpoint_path.stat().st_size
parts.append(f"**模型文件**: {file_name}{file_size / (1024 * 1024):.2f} MB)")
if self.extra_model_info is not None:
extra_info = self.extra_model_info()
if extra_info:
parts.append(extra_info)
return ",".join(parts)
def _init_tool(self):
print("正在加载分割模型...")
inference_artifact, resource = self._load_inference_artifact()
print("模型加载完成!")
return self.tool_factory(inference_artifact, resource)
def _ensure_tool_initialized(self) -> None:
if self._tool is None:
self._tool = self._init_tool()
def select_sample_image(self, sample_name: str):
return self.load_sample_image(sample_name)
def segment_image(self, image):
self._ensure_tool_initialized()
return self._tool.segment(image)
def create_ui(self):
with gr.Blocks(title=self.title) as demo:
gr.Markdown(f"# {self.title}")
gr.Markdown("请选择示例图片或直接上传图片,分割时只会读取当前输入框中的图片。")
try:
gr.Markdown(self.get_model_info())
except Exception as e:
gr.Markdown(f"**模型信息加载失败**: {str(e)}")
with gr.Row():
with gr.Column():
input_image = gr.Image(
label=self.input_label,
type="numpy"
)
sample_name = gr.Dropdown(
choices=self.sample_images,
value=cast(str | None, None),
label=self.sample_label
)
segment_btn = gr.Button("开始分割", variant="primary")
with gr.Column():
output_image = gr.Image(
label=self.output_image_label,
type="pil",
interactive=False
)
sample_name.change(
fn=self.select_sample_image,
inputs=[sample_name],
outputs=[input_image]
)
segment_btn.click(
fn=self.segment_image,
inputs=[input_image],
outputs=[output_image]
)
original_get_config_file = demo.get_config_file
def get_config_file():
config = original_get_config_file()
for component in config["components"]:
if component["type"] == "dropdown" and component["props"].get("label") == self.sample_label:
component["props"]["value"] = None
return config
demo.get_config_file = get_config_file
return demo
adapter = SegmentationPageAdapter(pipeline)
app = SegmentationAppBuilder(
pipeline=pipeline,
tool_factory=adapter.create_tool,
sample_images=adapter.list_sample_images(),
load_sample_image=adapter.load_sample_image,
extra_model_info=adapter.extra_model_info
)
demo = app.create_ui()
if __name__ == "__main__":
from deep_learning.env.keras import enable_mixed_precision
enable_mixed_precision()
demo.launch()