修复演示恢复项目帧数据
- 恢复演示出厂设置后直接解析演视LC视频序列并生成可打开帧序列 - 保持演视DICOM序列按文件名自然顺序恢复并生成帧 - 增加 MinIO 浏览器访问端点配置,修复 Docker 部署中封面和帧图预签名地址使用容器内主机名的问题 - 更新管理员恢复测试覆盖视频和 DICOM 帧数量 - 更新 README 和前后端契约/设计/测试文档中的演示恢复说明
This commit is contained in:
@@ -12,12 +12,20 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from minio_client import upload_file
|
||||
from models import Frame, Project, User
|
||||
from services.frame_parser import natural_filename_key, parse_dicom, upload_frames_to_minio
|
||||
from services.frame_parser import (
|
||||
extract_thumbnail,
|
||||
natural_filename_key,
|
||||
parse_dicom,
|
||||
parse_video,
|
||||
upload_frames_to_minio,
|
||||
)
|
||||
from statuses import PROJECT_STATUS_PENDING, PROJECT_STATUS_READY
|
||||
|
||||
DEMO_DICOM_PROJECT_NAME = "演视DICOM序列"
|
||||
DEMO_DICOM_PARSE_FPS = 30.0
|
||||
DEMO_VIDEO_PROJECT_NAME = "演视LC视频序列"
|
||||
DEMO_VIDEO_PARSE_FPS = 30.0
|
||||
DEMO_VIDEO_TARGET_WIDTH = 640
|
||||
LEGACY_DEMO_VIDEO_PROJECT_NAMES = {"Data_MyVideo_1"}
|
||||
LEGACY_DEMO_DICOM_PROJECT_NAMES = {"演示DICOM序列"}
|
||||
|
||||
@@ -67,6 +75,85 @@ def create_unparsed_video_demo_project(
|
||||
return project
|
||||
|
||||
|
||||
def create_parsed_video_demo_project(
|
||||
db: Session,
|
||||
*,
|
||||
owner: User,
|
||||
video_path: str,
|
||||
project_name: str = DEMO_VIDEO_PROJECT_NAME,
|
||||
) -> Project:
|
||||
"""Create the bundled demo video project and register its extracted frame sequence."""
|
||||
source = Path(video_path)
|
||||
if not source.exists() or not source.is_file():
|
||||
raise FileNotFoundError(f"Demo video not found: {video_path}")
|
||||
|
||||
project = Project(
|
||||
name=project_name,
|
||||
description="默认演示视频,已生成帧",
|
||||
status=PROJECT_STATUS_PENDING,
|
||||
source_type="video",
|
||||
parse_fps=DEMO_VIDEO_PARSE_FPS,
|
||||
original_fps=None,
|
||||
owner_user_id=owner.id,
|
||||
)
|
||||
db.add(project)
|
||||
db.flush()
|
||||
|
||||
data = source.read_bytes()
|
||||
object_name = f"uploads/{project.id}/{source.name}"
|
||||
upload_file(object_name, data, content_type="video/mp4", length=len(data))
|
||||
project.video_path = object_name
|
||||
|
||||
tmp_dir = tempfile.mkdtemp(prefix=f"seg_demo_video_{project.id}_")
|
||||
try:
|
||||
output_dir = os.path.join(tmp_dir, "frames")
|
||||
frame_files, original_fps = parse_video(
|
||||
str(source),
|
||||
output_dir,
|
||||
fps=int(DEMO_VIDEO_PARSE_FPS),
|
||||
target_width=DEMO_VIDEO_TARGET_WIDTH,
|
||||
)
|
||||
project.original_fps = original_fps
|
||||
object_names = upload_frames_to_minio(frame_files, project.id)
|
||||
|
||||
for idx, obj_name in enumerate(object_names):
|
||||
image = cv2.imread(frame_files[idx])
|
||||
height, width = image.shape[:2] if image is not None else (None, None)
|
||||
db.add(Frame(
|
||||
project_id=project.id,
|
||||
frame_index=idx,
|
||||
image_url=obj_name,
|
||||
width=width,
|
||||
height=height,
|
||||
timestamp_ms=idx * 1000.0 / DEMO_VIDEO_PARSE_FPS,
|
||||
source_frame_number=idx,
|
||||
))
|
||||
|
||||
thumbnail_path = os.path.join(tmp_dir, "thumbnail.jpg")
|
||||
try:
|
||||
extract_thumbnail(str(source), thumbnail_path)
|
||||
with open(thumbnail_path, "rb") as thumbnail_file:
|
||||
thumbnail_data = thumbnail_file.read()
|
||||
thumbnail_object = f"projects/{project.id}/thumbnail.jpg"
|
||||
upload_file(
|
||||
thumbnail_object,
|
||||
thumbnail_data,
|
||||
content_type="image/jpeg",
|
||||
length=len(thumbnail_data),
|
||||
)
|
||||
project.thumbnail_url = thumbnail_object
|
||||
except Exception: # noqa: BLE001
|
||||
if object_names:
|
||||
project.thumbnail_url = object_names[0]
|
||||
|
||||
project.status = PROJECT_STATUS_READY
|
||||
db.commit()
|
||||
db.refresh(project)
|
||||
return project
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def create_parsed_dicom_demo_project(
|
||||
db: Session,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user