feat(annotation): 添加分段标注功能支持

- 定义分段标注相关常量(segmented、segments、result等键名)
- 实现分段标注提取方法_extract_segment_annotations处理字典和列表格式
- 添加分段标注判断方法_is_segmented_annotation检测标注状态
- 修改_has_annotation_result方法使用新的分段标注处理逻辑
- 在任务创建过程中集成分段标注数据处理
- 更新导出服务中的分段标注结果扁平化处理
- 实现标注归一化方法支持分段标注格式转换
- 调整JSON和CSV导出格式适配分段标注结构
This commit is contained in:
2026-01-31 14:36:16 +08:00
parent 8fdc7d99b8
commit c5c8e6c69e
2 changed files with 145 additions and 29 deletions

View File

@@ -63,6 +63,12 @@ from ..schema.export import (
logger = get_logger(__name__)
SEGMENTED_KEY = "segmented"
SEGMENTS_KEY = "segments"
SEGMENT_RESULT_KEY = "result"
SEGMENT_INDEX_KEY = "segmentIndex"
SEGMENT_INDEX_FALLBACK_KEY = "segment_index"
class AnnotationExportService:
"""标注数据导出服务"""
@@ -239,6 +245,61 @@ class AnnotationExportService:
return items
@staticmethod
def _flatten_annotation_results(annotation: Dict[str, Any]) -> List[Dict[str, Any]]:
if not annotation or not isinstance(annotation, dict):
return []
segments = annotation.get(SEGMENTS_KEY)
if annotation.get(SEGMENTED_KEY) or isinstance(segments, (dict, list)):
results: List[Dict[str, Any]] = []
if isinstance(segments, dict):
for key, segment in segments.items():
if not isinstance(segment, dict):
continue
segment_results = segment.get(SEGMENT_RESULT_KEY)
if not isinstance(segment_results, list):
continue
for item in segment_results:
if isinstance(item, dict):
normalized = dict(item)
if SEGMENT_INDEX_KEY not in normalized and SEGMENT_INDEX_FALLBACK_KEY not in normalized:
normalized[SEGMENT_INDEX_KEY] = int(key) if str(key).isdigit() else key
results.append(normalized)
else:
results.append({"value": item, SEGMENT_INDEX_KEY: key})
elif isinstance(segments, list):
for idx, segment in enumerate(segments):
if not isinstance(segment, dict):
continue
segment_results = segment.get(SEGMENT_RESULT_KEY)
if not isinstance(segment_results, list):
continue
segment_index = segment.get(SEGMENT_INDEX_KEY, segment.get(SEGMENT_INDEX_FALLBACK_KEY, idx))
for item in segment_results:
if isinstance(item, dict):
normalized = dict(item)
if SEGMENT_INDEX_KEY not in normalized and SEGMENT_INDEX_FALLBACK_KEY not in normalized:
normalized[SEGMENT_INDEX_KEY] = segment_index
results.append(normalized)
else:
results.append({"value": item, SEGMENT_INDEX_KEY: segment_index})
return results
result = annotation.get(SEGMENT_RESULT_KEY)
return result if isinstance(result, list) else []
@classmethod
def _normalize_annotation_for_export(cls, annotation: Dict[str, Any]) -> Dict[str, Any]:
if not annotation or not isinstance(annotation, dict):
return {}
segments = annotation.get(SEGMENTS_KEY)
if annotation.get(SEGMENTED_KEY) or isinstance(segments, (dict, list)):
normalized = dict(annotation)
normalized_result = cls._flatten_annotation_results(annotation)
if SEGMENT_RESULT_KEY not in normalized or not isinstance(normalized.get(SEGMENT_RESULT_KEY), list):
normalized[SEGMENT_RESULT_KEY] = normalized_result
return normalized
return annotation
def _export_json(
self, items: List[AnnotationExportItem], project_name: str
) -> Tuple[bytes, str, str]:
@@ -252,7 +313,7 @@ class AnnotationExportService:
"file_id": item.file_id,
"file_name": item.file_name,
"data": item.data,
"annotations": item.annotations,
"annotations": [self._normalize_annotation_for_export(ann) for ann in item.annotations],
"created_at": item.created_at.isoformat() if item.created_at else None,
"updated_at": item.updated_at.isoformat() if item.updated_at else None,
}
@@ -274,7 +335,7 @@ class AnnotationExportService:
"file_id": item.file_id,
"file_name": item.file_name,
"data": item.data,
"annotations": item.annotations,
"annotations": [self._normalize_annotation_for_export(ann) for ann in item.annotations],
"created_at": item.created_at.isoformat() if item.created_at else None,
"updated_at": item.updated_at.isoformat() if item.updated_at else None,
}
@@ -307,7 +368,7 @@ class AnnotationExportService:
# 提取标签信息(支持多种标注类型)
labels = []
for ann in item.annotations:
results = ann.get("result", [])
results = self._flatten_annotation_results(ann)
for r in results:
value = r.get("value", {})
label_type = r.get("type", "")
@@ -382,7 +443,7 @@ class AnnotationExportService:
# 处理标注
for ann in item.annotations:
results = ann.get("result", [])
results = self._flatten_annotation_results(ann)
for r in results:
# 处理矩形框标注 (rectanglelabels)
if r.get("type") == "rectanglelabels":
@@ -434,7 +495,7 @@ class AnnotationExportService:
lines = []
for ann in item.annotations:
results = ann.get("result", [])
results = self._flatten_annotation_results(ann)
for r in results:
# 处理矩形框标注
if r.get("type") == "rectanglelabels":