保持传播多区域结果为单个遮罩
- 后端传播落库时将同一 seed 在同一目标帧的多个不连通 polygon 保存到同一 annotation - 同步任务传播和兼容同步传播接口的多 polygon 保存逻辑 - 传播结果 bbox 改为覆盖全部不连通 polygon,并保留多 polygon scores 与 holes - 前端回显单条多 polygon annotation 时使用组合 bbox 和真实 polygon 面积 - 补充后端传播 worker 回归测试,验证不连通结果只生成一个 annotation - 补充前端 API 回归测试,验证多 polygon annotation 回显为一个 mask - 更新项目指南和设计冻结文档
This commit is contained in:
@@ -582,6 +582,79 @@ def test_propagation_task_runner_saves_annotations_and_progress(client, db_sessi
|
||||
assert len(stored_polygon) > 3
|
||||
|
||||
|
||||
def test_propagation_task_runner_keeps_disconnected_result_polygons_in_one_annotation(client, db_session, monkeypatch):
|
||||
project = client.post("/api/projects", json={"name": "Propagation Disconnected Mask"}).json()
|
||||
frames = [
|
||||
client.post(f"/api/projects/{project['id']}/frames", json={
|
||||
"project_id": project["id"],
|
||||
"frame_index": idx,
|
||||
"image_url": f"frames/{idx}.jpg",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
}).json()
|
||||
for idx in range(2)
|
||||
]
|
||||
first_piece = [[0.15, 0.15], [0.25, 0.15], [0.25, 0.25], [0.15, 0.25]]
|
||||
second_piece = [[0.70, 0.70], [0.90, 0.70], [0.90, 0.90], [0.70, 0.90]]
|
||||
second_hole = [[[0.76, 0.76], [0.82, 0.76], [0.82, 0.82], [0.76, 0.82]]]
|
||||
task = ProcessingTask(
|
||||
task_type="propagate_masks",
|
||||
status="queued",
|
||||
progress=0,
|
||||
project_id=project["id"],
|
||||
payload={
|
||||
"project_id": project["id"],
|
||||
"frame_id": frames[0]["id"],
|
||||
"model": "sam2.1_hiera_tiny",
|
||||
"include_source": False,
|
||||
"save_annotations": True,
|
||||
"steps": [{
|
||||
"direction": "forward",
|
||||
"max_frames": 2,
|
||||
"seed": {
|
||||
"polygons": [
|
||||
[[0.1, 0.1], [0.2, 0.1], [0.2, 0.2]],
|
||||
[[0.6, 0.6], [0.8, 0.6], [0.8, 0.8]],
|
||||
],
|
||||
"label": "多区域",
|
||||
"color": "#ff0000",
|
||||
"source_annotation_id": 7,
|
||||
"source_mask_id": "annotation-7",
|
||||
},
|
||||
}],
|
||||
},
|
||||
)
|
||||
db_session.add(task)
|
||||
db_session.commit()
|
||||
db_session.refresh(task)
|
||||
|
||||
monkeypatch.setattr("services.propagation_task_runner.download_file", lambda object_name: b"jpeg")
|
||||
monkeypatch.setattr("services.propagation_task_runner.publish_task_progress_event", lambda event_task: None)
|
||||
monkeypatch.setattr("services.propagation_task_runner.sam_registry.propagate_video", lambda *args, **kwargs: [
|
||||
{"frame_index": 0, "polygons": [], "scores": []},
|
||||
{
|
||||
"frame_index": 1,
|
||||
"polygons": [first_piece, second_piece],
|
||||
"holes": [[], second_hole],
|
||||
"scores": [0.72, 0.93],
|
||||
},
|
||||
])
|
||||
|
||||
result = run_propagate_project_task(db_session, task.id)
|
||||
|
||||
assert result["created_annotation_count"] == 1
|
||||
annotations = db_session.query(Annotation).filter(Annotation.project_id == project["id"]).all()
|
||||
assert len(annotations) == 1
|
||||
annotation = annotations[0]
|
||||
assert annotation.frame_id == frames[1]["id"]
|
||||
assert annotation.bbox == [0.15, 0.15, 0.75, 0.75]
|
||||
assert annotation.mask_data["polygons"] == [first_piece, second_piece]
|
||||
assert annotation.mask_data["holes"] == [[], second_hole]
|
||||
assert annotation.mask_data["hasHoles"] is True
|
||||
assert annotation.mask_data["score"] == 0.93
|
||||
assert annotation.mask_data["scores"] == [0.72, 0.93]
|
||||
|
||||
|
||||
def test_propagation_task_runner_skips_unchanged_seed_and_replaces_changed_seed(client, db_session, monkeypatch):
|
||||
project = client.post("/api/projects", json={"name": "Propagation Dedupe"}).json()
|
||||
frames = [
|
||||
|
||||
Reference in New Issue
Block a user