Add UPP STL asset indexing workflow

This commit is contained in:
Codex
2026-05-25 22:55:58 +08:00
parent 70215ce611
commit 7eebab455d
8 changed files with 1960 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
CREATE TABLE IF NOT EXISTS upp_exam_assets (
ct_number text NOT NULL PRIMARY KEY,
list_present boolean NOT NULL DEFAULT false,
stl_present boolean NOT NULL DEFAULT false,
patient_name text,
patient_sex text,
patient_age text,
patient_id_masked text,
exam_date timestamptz,
task_created_at timestamptz,
exam_description text,
exam_device text,
algorithm_model text,
upp_status text,
list_record_count integer NOT NULL DEFAULT 0,
selected_list_record jsonb,
list_records jsonb NOT NULL DEFAULT '[]'::jsonb,
selected_source_case_dir text,
selected_source_stl_dir text,
processed_stl_dir text,
stl_case_name text,
stl_sequence_no integer,
stl_file_count integer NOT NULL DEFAULT 0,
stl_total_bytes bigint NOT NULL DEFAULT 0,
stl_files jsonb NOT NULL DEFAULT '[]'::jsonb,
stl_candidates jsonb NOT NULL DEFAULT '[]'::jsonb,
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT ck_upp_exam_assets_ct_number_present CHECK (btrim(ct_number) <> ''),
CONSTRAINT ck_upp_exam_assets_ct_number_format CHECK (ct_number ~ '^D?CT[0-9]{8,}$')
);
DO $$
BEGIN
IF to_regclass('public.upp_stl_files') IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'upp_stl_files'
AND column_name = 'file_names'
)
THEN
CREATE TABLE IF NOT EXISTS upp_stl_files_row_detail_backup AS TABLE upp_stl_files;
DROP TABLE upp_stl_files;
END IF;
END $$;
CREATE TABLE IF NOT EXISTS upp_stl_files (
ct_number text NOT NULL PRIMARY KEY REFERENCES upp_exam_assets(ct_number) ON DELETE CASCADE,
file_count integer NOT NULL DEFAULT 0,
total_bytes bigint NOT NULL DEFAULT 0,
segment_names jsonb NOT NULL DEFAULT '[]'::jsonb,
segment_families jsonb NOT NULL DEFAULT '[]'::jsonb,
segment_categories jsonb NOT NULL DEFAULT '[]'::jsonb,
file_names jsonb NOT NULL DEFAULT '[]'::jsonb,
source_file_paths jsonb NOT NULL DEFAULT '[]'::jsonb,
processed_file_paths jsonb NOT NULL DEFAULT '[]'::jsonb,
files jsonb NOT NULL DEFAULT '[]'::jsonb,
updated_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE upp_exam_assets ALTER COLUMN ct_number SET NOT NULL;
ALTER TABLE upp_stl_files ALTER COLUMN ct_number SET NOT NULL;
ALTER TABLE upp_stl_files ADD COLUMN IF NOT EXISTS segment_families jsonb NOT NULL DEFAULT '[]'::jsonb;
ALTER TABLE upp_stl_files ADD COLUMN IF NOT EXISTS segment_categories jsonb NOT NULL DEFAULT '[]'::jsonb;
CREATE INDEX IF NOT EXISTS idx_upp_exam_assets_list_present ON upp_exam_assets(list_present);
CREATE INDEX IF NOT EXISTS idx_upp_exam_assets_stl_present ON upp_exam_assets(stl_present);
CREATE INDEX IF NOT EXISTS idx_upp_exam_assets_patient_name ON upp_exam_assets(patient_name);
CREATE INDEX IF NOT EXISTS idx_upp_exam_assets_exam_date ON upp_exam_assets(exam_date);
CREATE INDEX IF NOT EXISTS idx_upp_exam_assets_list_records_gin ON upp_exam_assets USING gin (list_records);
CREATE INDEX IF NOT EXISTS idx_upp_exam_assets_stl_files_gin ON upp_exam_assets USING gin (stl_files);
CREATE INDEX IF NOT EXISTS idx_upp_stl_files_segment_names_gin ON upp_stl_files USING gin (segment_names);
CREATE INDEX IF NOT EXISTS idx_upp_stl_files_segment_families_gin ON upp_stl_files USING gin (segment_families);
CREATE INDEX IF NOT EXISTS idx_upp_stl_files_segment_categories_gin ON upp_stl_files USING gin (segment_categories);
CREATE INDEX IF NOT EXISTS idx_upp_stl_files_file_names_gin ON upp_stl_files USING gin (file_names);
CREATE INDEX IF NOT EXISTS idx_upp_stl_files_files_gin ON upp_stl_files USING gin (files);
COMMENT ON TABLE upp_exam_assets IS 'UPP列表、STL重建结果、未来CT数据的CT号唯一资产索引';
COMMENT ON COLUMN upp_exam_assets.ct_number IS 'CT检查号唯一索引键';
COMMENT ON COLUMN upp_exam_assets.list_records IS '来自UPP列表OCR的同CT号全部记录';
COMMENT ON COLUMN upp_exam_assets.selected_list_record IS '同CT号列表记录中按任务创建时间/检查时间选择的新记录';
COMMENT ON COLUMN upp_exam_assets.stl_candidates IS '同CT号全部候选STL目录保留去重选择依据';
COMMENT ON COLUMN upp_exam_assets.stl_files IS '最终选中STL目录的文件清单';
COMMENT ON COLUMN upp_exam_assets.processed_stl_dir IS '规范化后的STL目录目录名为CT号';
COMMENT ON TABLE upp_stl_files IS 'UPP最终选中STL文件聚合表每个CT号唯一一行';
COMMENT ON COLUMN upp_stl_files.ct_number IS 'CT检查号主键并关联upp_exam_assets.ct_number';
COMMENT ON COLUMN upp_stl_files.segment_names IS '最终选中STL文件的分割名称JSON数组';
COMMENT ON COLUMN upp_stl_files.segment_families IS '与segment_names同顺序的训练family JSON数组';
COMMENT ON COLUMN upp_stl_files.segment_categories IS '与segment_names同顺序的粗分类JSON数组';
COMMENT ON COLUMN upp_stl_files.file_names IS '最终选中STL文件名JSON数组';
COMMENT ON COLUMN upp_stl_files.files IS '最终选中STL文件完整明细JSON数组';

View File

@@ -0,0 +1,672 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""整理UPP STL目录并把CT号唯一资产索引同步到PostgreSQL。"""
from __future__ import annotations
import argparse
import csv
import json
import os
import re
import shutil
import subprocess
import tempfile
import unicodedata
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any
BASE_DIR = Path(__file__).resolve().parents[1]
DEFAULT_STL_ROOT = BASE_DIR / "UPP_STL处理" / "待处理STL数据"
DEFAULT_PROCESSED_ROOT = BASE_DIR / "UPP_STL处理" / "已处理STL数据"
DEFAULT_LIST_JSON = BASE_DIR / "UPP列表处理" / "数据处理结果区" / "全量分片结果" / "合并_图片表格_结构化.json"
DEFAULT_SCHEMA = BASE_DIR / "UPP_数据库构建" / "01_UPP资产索引建表.sql"
DEFAULT_REPORT = BASE_DIR / "UPP_数据库构建" / "UPP_STL资产同步报告.json"
CT_PATTERN = re.compile(r"(D?CT\d{8,})", re.IGNORECASE)
VALID_CT_PATTERN = re.compile(r"^D?CT\d{8,}$")
ASSET_FIELDS = [
"ct_number",
"list_present",
"stl_present",
"patient_name",
"patient_sex",
"patient_age",
"patient_id_masked",
"exam_date",
"task_created_at",
"exam_description",
"exam_device",
"algorithm_model",
"upp_status",
"list_record_count",
"selected_list_record",
"list_records",
"selected_source_case_dir",
"selected_source_stl_dir",
"processed_stl_dir",
"stl_case_name",
"stl_sequence_no",
"stl_file_count",
"stl_total_bytes",
"stl_files",
"stl_candidates",
]
STL_FIELDS = [
"ct_number",
"file_count",
"total_bytes",
"segment_names",
"segment_families",
"segment_categories",
"file_names",
"source_file_paths",
"processed_file_paths",
"files",
]
MODEL_VALUES = {"肝胆模型", "泌尿模型", "胸外模型"}
DEVICE_VALUES = {"CT", "MR", "DR", "CR", "DX", "US", "XA", "NM", "PT"}
def normalize_text(value: Any) -> str:
if value is None:
return ""
text = unicodedata.normalize("NFKC", str(value)).replace("\u3000", " ")
return re.sub(r"\s+", " ", text).strip()
def normalize_ct(value: Any) -> str:
return re.sub(r"\s+", "", normalize_text(value)).upper()
def valid_ct(value: str) -> bool:
return bool(VALID_CT_PATTERN.fullmatch(value))
def extract_ct(text: str) -> str:
match = CT_PATTERN.search(text)
if not match:
return ""
ct_number = normalize_ct(match.group(1))
return ct_number if valid_ct(ct_number) else ""
def json_dump(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
def path_text(path: Path) -> str:
return str(path.expanduser().absolute())
def parse_time(value: Any) -> datetime | None:
text = normalize_text(value)
if not text:
return None
for fmt in ("%Y-%m-%d %H:%M:%S", "%Y/%m/%d %H:%M:%S", "%Y-%m-%d", "%Y/%m/%d"):
try:
return datetime.strptime(text, fmt)
except ValueError:
pass
return None
def normalize_status(value: Any) -> str:
text = normalize_text(value).replace("\ufe0f", "")
compact = re.sub(r"\s+", "", text)
if not compact:
return ""
if "部分重建成功" in compact:
return "√ 部分重建成功"
if "重建失败" in compact or compact.startswith(("×", "")):
return "× 重建失败"
if "重建成功" in compact:
return "√ 重建成功"
if "已报告" in compact:
return "√ 已报告"
return text
def status_like(value: Any) -> bool:
text = normalize_text(value)
return bool(re.search(r"(重建成功|重建失败|部分重建成功|已报告)", text))
def classify_segment(segment_name: str) -> tuple[str, str]:
if segment_name in {"liver", "liver_left", "liver_right"}:
return "肝脏主体", segment_name
if re.fullmatch(r"liver_segment_S[1-8]", segment_name):
return "肝段", segment_name
if segment_name in {"liver_artery", "liver_vein", "portal_vein", "bile_duct"}:
return "血管胆管", segment_name
if segment_name in {"pancreas", "spleen", "cholecyst"}:
return "腹部脏器", segment_name
if segment_name in {"skin", "rib", "vertebrae", "sternum", "hipbone", "sacrum"}:
return "体表骨骼", segment_name
if re.fullmatch(r"liver_tumor_\d+", segment_name):
return "肝脏肿瘤", "liver_tumor_*"
if re.fullmatch(r"liver_cyst_\d+", segment_name):
return "肝囊肿", "liver_cyst_*"
if re.fullmatch(r"liver_hemangioma_\d+", segment_name):
return "肝血管瘤", "liver_hemangioma_*"
if re.fullmatch(r"pancreas_tumor_\d+", segment_name):
return "胰腺肿瘤", "pancreas_tumor_*"
if re.fullmatch(r"Segment_\d+", segment_name):
return "未命名分割", "Segment_*"
return "其他", segment_name
def clean_record_info(info: dict[str, Any]) -> dict[str, Any]:
cleaned = dict(info)
exam_description = normalize_text(cleaned.get("检查描述"))
exam_device = normalize_text(cleaned.get("检查设备"))
algorithm_model = normalize_text(cleaned.get("算法模型"))
if exam_description in DEVICE_VALUES and exam_device in MODEL_VALUES and status_like(algorithm_model):
cleaned["检查描述"] = ""
cleaned["检查设备"] = exam_description
cleaned["算法模型"] = exam_device
cleaned["状态"] = normalize_status(algorithm_model)
elif exam_device in MODEL_VALUES and status_like(algorithm_model) and normalize_text(cleaned.get("状态")) in {"", "", ""}:
cleaned["检查设备"] = "CT"
cleaned["算法模型"] = exam_device
cleaned["状态"] = normalize_status(algorithm_model)
else:
cleaned["状态"] = normalize_status(cleaned.get("状态"))
return cleaned
def clean_record(record: dict[str, Any]) -> dict[str, Any]:
cleaned = dict(record)
info = cleaned.get("记录信息")
if isinstance(info, dict):
cleaned["记录信息"] = clean_record_info(info)
return cleaned
def sql_quote_path(path: Path) -> str:
return "'" + str(path.expanduser().absolute()).replace("'", "''") + "'"
@dataclass(frozen=True)
class StlCandidate:
ct_number: str
source_case_dir: Path
source_stl_dir: Path
sequence_no: int | None
files: tuple[Path, ...]
@property
def file_count(self) -> int:
return len(self.files)
@property
def total_bytes(self) -> int:
return sum(file.stat().st_size for file in self.files)
def score(self) -> tuple[int, int, str, str]:
return (
self.file_count,
self.sequence_no if self.sequence_no is not None else -1,
self.source_case_dir.name,
self.source_stl_dir.name,
)
def summary(self) -> dict[str, Any]:
return {
"source_case_dir": path_text(self.source_case_dir),
"source_stl_dir": path_text(self.source_stl_dir),
"case_name": self.source_case_dir.name,
"sequence_no": self.sequence_no,
"file_count": self.file_count,
"total_bytes": self.total_bytes,
}
def sequence_no(case_dir: Path, stl_dir: Path) -> int | None:
values: list[int] = []
for pattern in (r"-(\d{3,6})_D?CT", r"^(\d{3,6})-STL$"):
for text in (case_dir.name, stl_dir.name):
values.extend(int(item) for item in re.findall(pattern, text, re.IGNORECASE))
return max(values) if values else None
def scan_stl_candidates(stl_root: Path) -> tuple[dict[str, list[StlCandidate]], list[str]]:
candidates: dict[str, list[StlCandidate]] = {}
no_ct_dirs: list[str] = []
for directory in sorted(stl_root.rglob("*")):
if not directory.is_dir():
continue
files = tuple(sorted([item for item in directory.iterdir() if item.is_file() and item.suffix.lower() == ".stl"]))
if not files:
continue
ct_number = extract_ct("/".join(directory.relative_to(stl_root).parts))
if not ct_number:
no_ct_dirs.append(path_text(directory))
continue
candidate = StlCandidate(
ct_number=ct_number,
source_case_dir=directory.parent,
source_stl_dir=directory,
sequence_no=sequence_no(directory.parent, directory),
files=files,
)
candidates.setdefault(ct_number, []).append(candidate)
return candidates, no_ct_dirs
def choose_candidates(candidates: dict[str, list[StlCandidate]]) -> dict[str, StlCandidate]:
return {ct_number: max(items, key=lambda item: item.score()) for ct_number, items in candidates.items()}
def clear_directory(directory: Path) -> None:
directory.mkdir(parents=True, exist_ok=True)
for child in directory.iterdir():
if child.is_dir() and not child.is_symlink():
shutil.rmtree(child)
else:
child.unlink()
def place_file(source: Path, destination: Path, mode: str) -> str:
destination.parent.mkdir(parents=True, exist_ok=True)
if mode == "symlink":
destination.symlink_to(source.expanduser().absolute())
return "symlink"
if mode == "copy":
shutil.copy2(source, destination)
return "copy"
try:
os.link(source, destination)
return "hardlink"
except OSError:
shutil.copy2(source, destination)
return "copy"
def materialize_selected_stl(
selected: dict[str, StlCandidate],
processed_root: Path,
link_mode: str,
refresh_files: bool,
) -> tuple[dict[str, list[dict[str, Any]]], dict[str, str]]:
processed_files: dict[str, list[dict[str, Any]]] = {}
link_methods: dict[str, str] = {}
processed_root.mkdir(parents=True, exist_ok=True)
for ct_number, candidate in sorted(selected.items()):
ct_dir = processed_root / ct_number
if refresh_files:
clear_directory(ct_dir)
else:
ct_dir.mkdir(parents=True, exist_ok=True)
file_rows: list[dict[str, Any]] = []
used_methods: set[str] = set()
for source in candidate.files:
destination = ct_dir / source.name
if destination.exists() or destination.is_symlink():
destination.unlink()
method = place_file(source, destination, link_mode)
used_methods.add(method)
category, family = classify_segment(source.stem)
file_rows.append(
{
"segment_name": source.stem,
"family": family,
"category": category,
"file_name": source.name,
"source_file_path": path_text(source),
"processed_file_path": path_text(destination),
"size_bytes": source.stat().st_size,
}
)
manifest = {
"ct_number": ct_number,
"selected_source_case_dir": path_text(candidate.source_case_dir),
"selected_source_stl_dir": path_text(candidate.source_stl_dir),
"processed_stl_dir": path_text(ct_dir),
"selection_rule": "file_count_desc_then_sequence_no_desc",
"stl_file_count": candidate.file_count,
"stl_total_bytes": candidate.total_bytes,
"files": file_rows,
}
(ct_dir / "manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
processed_files[ct_number] = file_rows
link_methods[ct_number] = "+".join(sorted(used_methods))
return processed_files, link_methods
def load_list_records(list_json: Path) -> tuple[dict[str, list[dict[str, Any]]], list[dict[str, Any]]]:
data = json.loads(list_json.read_text(encoding="utf-8"))
records_by_ct: dict[str, list[dict[str, Any]]] = {}
invalid: list[dict[str, Any]] = []
for index, record in enumerate(data.get("图片表格记录", []), start=1):
record = clean_record(record)
info = record.get("记录信息") or {}
ct_number = normalize_ct(info.get("检查号", ""))
if not valid_ct(ct_number):
invalid.append({"row_index": index, "ct_number": ct_number, "record": record})
continue
records_by_ct.setdefault(ct_number, []).append(record)
return records_by_ct, invalid
def select_list_record(records: list[dict[str, Any]]) -> dict[str, Any]:
def key(record: dict[str, Any]) -> tuple[datetime, datetime, int, int]:
info = record.get("记录信息") or {}
image = record.get("图片信息") or {}
task_time = parse_time(info.get("任务创建时间")) or datetime.min
exam_time = parse_time(info.get("检查日期")) or datetime.min
page_no = -1
seq = image.get("图片序号")
if isinstance(seq, list):
numbers = [item for item in seq if isinstance(item, int)]
if numbers:
page_no = numbers[0]
row_no = int(image.get("图片内行号") or -1)
return (task_time, exam_time, page_no, row_no)
return max(records, key=key)
def build_rows(
selected: dict[str, StlCandidate],
all_candidates: dict[str, list[StlCandidate]],
processed_root: Path,
processed_files: dict[str, list[dict[str, Any]]],
list_records: dict[str, list[dict[str, Any]]],
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], int]:
asset_rows: list[dict[str, Any]] = []
stl_rows: list[dict[str, Any]] = []
stl_file_count = 0
for ct_number in sorted(set(selected) | set(list_records)):
candidate = selected.get(ct_number)
records = list_records.get(ct_number, [])
chosen_record = select_list_record(records) if records else None
info = (chosen_record or {}).get("记录信息") or {}
files = processed_files.get(ct_number, [])
processed_dir = processed_root / ct_number
asset_rows.append(
{
"ct_number": ct_number,
"list_present": bool(records),
"stl_present": candidate is not None,
"patient_name": normalize_text(info.get("姓名")),
"patient_sex": normalize_text(info.get("性别")),
"patient_age": normalize_text(info.get("年龄")),
"patient_id_masked": normalize_text(info.get("患者号")),
"exam_date": normalize_text(info.get("检查日期")),
"task_created_at": normalize_text(info.get("任务创建时间")),
"exam_description": normalize_text(info.get("检查描述")),
"exam_device": normalize_text(info.get("检查设备")),
"algorithm_model": normalize_text(info.get("算法模型")),
"upp_status": normalize_text(info.get("状态")),
"list_record_count": len(records),
"selected_list_record": json_dump(chosen_record) if chosen_record else "",
"list_records": json_dump(records),
"selected_source_case_dir": path_text(candidate.source_case_dir) if candidate else "",
"selected_source_stl_dir": path_text(candidate.source_stl_dir) if candidate else "",
"processed_stl_dir": path_text(processed_dir) if candidate else "",
"stl_case_name": candidate.source_case_dir.name if candidate else "",
"stl_sequence_no": candidate.sequence_no if candidate and candidate.sequence_no is not None else "",
"stl_file_count": candidate.file_count if candidate else 0,
"stl_total_bytes": candidate.total_bytes if candidate else 0,
"stl_files": json_dump(files),
"stl_candidates": json_dump([item.summary() for item in sorted(all_candidates.get(ct_number, []), key=lambda item: item.score(), reverse=True)]),
}
)
if files:
stl_file_count += len(files)
stl_rows.append(
{
"ct_number": ct_number,
"file_count": len(files),
"total_bytes": sum(int(file_info.get("size_bytes") or 0) for file_info in files),
"segment_names": json_dump([file_info["segment_name"] for file_info in files]),
"segment_families": json_dump([file_info["family"] for file_info in files]),
"segment_categories": json_dump([file_info["category"] for file_info in files]),
"file_names": json_dump([file_info["file_name"] for file_info in files]),
"source_file_paths": json_dump([file_info["source_file_path"] for file_info in files]),
"processed_file_paths": json_dump([file_info["processed_file_path"] for file_info in files]),
"files": json_dump(files),
}
)
return asset_rows, stl_rows, stl_file_count
def write_csv(rows: list[dict[str, Any]], fields: list[str], suffix: str) -> Path:
temp_file = tempfile.NamedTemporaryFile("w", encoding="utf-8", newline="", suffix=suffix, delete=False)
with temp_file:
writer = csv.DictWriter(temp_file, fieldnames=fields)
writer.writeheader()
for row in rows:
writer.writerow({field: row.get(field, "") for field in fields})
return Path(temp_file.name)
def run_psql(args: argparse.Namespace, asset_csv: Path, stl_csv: Path) -> None:
sql = f"""
\\set ON_ERROR_STOP on
\\i {sql_quote_path(Path(args.schema))}
CREATE TEMP TABLE stg_upp_exam_assets (
ct_number text,
list_present boolean,
stl_present boolean,
patient_name text,
patient_sex text,
patient_age text,
patient_id_masked text,
exam_date timestamptz,
task_created_at timestamptz,
exam_description text,
exam_device text,
algorithm_model text,
upp_status text,
list_record_count integer,
selected_list_record text,
list_records text,
selected_source_case_dir text,
selected_source_stl_dir text,
processed_stl_dir text,
stl_case_name text,
stl_sequence_no integer,
stl_file_count integer,
stl_total_bytes bigint,
stl_files text,
stl_candidates text
);
CREATE TEMP TABLE stg_upp_stl_files (
ct_number text,
file_count integer,
total_bytes bigint,
segment_names text,
segment_families text,
segment_categories text,
file_names text,
source_file_paths text,
processed_file_paths text,
files text
);
\\copy stg_upp_exam_assets({",".join(ASSET_FIELDS)}) FROM {sql_quote_path(asset_csv)} WITH (FORMAT csv, HEADER true, NULL '')
\\copy stg_upp_stl_files({",".join(STL_FIELDS)}) FROM {sql_quote_path(stl_csv)} WITH (FORMAT csv, HEADER true, NULL '')
INSERT INTO upp_exam_assets (
ct_number, list_present, stl_present, patient_name, patient_sex, patient_age, patient_id_masked,
exam_date, task_created_at, exam_description, exam_device, algorithm_model, upp_status,
list_record_count, selected_list_record, list_records, selected_source_case_dir, selected_source_stl_dir,
processed_stl_dir, stl_case_name, stl_sequence_no, stl_file_count, stl_total_bytes, stl_files, stl_candidates, updated_at
)
SELECT
ct_number, list_present, stl_present, patient_name, patient_sex, patient_age, patient_id_masked,
exam_date, task_created_at, exam_description, exam_device, algorithm_model, upp_status,
COALESCE(list_record_count, 0),
NULLIF(selected_list_record, '')::jsonb,
COALESCE(NULLIF(list_records, ''), '[]')::jsonb,
selected_source_case_dir, selected_source_stl_dir, processed_stl_dir, stl_case_name, stl_sequence_no,
COALESCE(stl_file_count, 0), COALESCE(stl_total_bytes, 0),
COALESCE(NULLIF(stl_files, ''), '[]')::jsonb,
COALESCE(NULLIF(stl_candidates, ''), '[]')::jsonb,
now()
FROM stg_upp_exam_assets
ON CONFLICT (ct_number) DO UPDATE SET
list_present = EXCLUDED.list_present,
stl_present = EXCLUDED.stl_present,
patient_name = EXCLUDED.patient_name,
patient_sex = EXCLUDED.patient_sex,
patient_age = EXCLUDED.patient_age,
patient_id_masked = EXCLUDED.patient_id_masked,
exam_date = EXCLUDED.exam_date,
task_created_at = EXCLUDED.task_created_at,
exam_description = EXCLUDED.exam_description,
exam_device = EXCLUDED.exam_device,
algorithm_model = EXCLUDED.algorithm_model,
upp_status = EXCLUDED.upp_status,
list_record_count = EXCLUDED.list_record_count,
selected_list_record = EXCLUDED.selected_list_record,
list_records = EXCLUDED.list_records,
selected_source_case_dir = EXCLUDED.selected_source_case_dir,
selected_source_stl_dir = EXCLUDED.selected_source_stl_dir,
processed_stl_dir = EXCLUDED.processed_stl_dir,
stl_case_name = EXCLUDED.stl_case_name,
stl_sequence_no = EXCLUDED.stl_sequence_no,
stl_file_count = EXCLUDED.stl_file_count,
stl_total_bytes = EXCLUDED.stl_total_bytes,
stl_files = EXCLUDED.stl_files,
stl_candidates = EXCLUDED.stl_candidates,
updated_at = now();
DELETE FROM upp_stl_files WHERE ct_number IN (SELECT ct_number FROM stg_upp_exam_assets);
INSERT INTO upp_stl_files (
ct_number, file_count, total_bytes, segment_names, segment_families, segment_categories, file_names, source_file_paths, processed_file_paths, files, updated_at
)
SELECT
ct_number,
COALESCE(file_count, 0),
COALESCE(total_bytes, 0),
COALESCE(NULLIF(segment_names, ''), '[]')::jsonb,
COALESCE(NULLIF(segment_families, ''), '[]')::jsonb,
COALESCE(NULLIF(segment_categories, ''), '[]')::jsonb,
COALESCE(NULLIF(file_names, ''), '[]')::jsonb,
COALESCE(NULLIF(source_file_paths, ''), '[]')::jsonb,
COALESCE(NULLIF(processed_file_paths, ''), '[]')::jsonb,
COALESCE(NULLIF(files, ''), '[]')::jsonb,
now()
FROM stg_upp_stl_files;
"""
env = os.environ.copy()
password = args.password or env.get("PGPASSWORD")
if password:
env["PGPASSWORD"] = password
command = ["psql"]
if args.host:
command.extend(["-h", args.host])
if args.port:
command.extend(["-p", str(args.port)])
if args.user:
command.extend(["-U", args.user])
if args.dbname:
command.extend(["-d", args.dbname])
command.extend(["-v", "ON_ERROR_STOP=1"])
completed = subprocess.run(command, input=sql, text=True, env=env, capture_output=True)
print(completed.stdout, end="")
if completed.returncode != 0:
print(completed.stderr, end="")
completed.check_returncode()
def write_report(path: Path, report: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--stl-root", default=str(DEFAULT_STL_ROOT), help="待处理STL数据根目录")
parser.add_argument("--processed-root", default=str(DEFAULT_PROCESSED_ROOT), help="规范化后的STL输出目录")
parser.add_argument("--list-json", default=str(DEFAULT_LIST_JSON), help="UPP列表合并结构化JSON")
parser.add_argument("--schema", default=str(DEFAULT_SCHEMA), help="PostgreSQL建表SQL")
parser.add_argument("--report", default=str(DEFAULT_REPORT), help="同步报告JSON")
parser.add_argument("--link-mode", choices=["hardlink", "copy", "symlink"], default="hardlink", help="输出STL文件方式")
parser.add_argument("--no-refresh-files", action="store_true", help="不清空已处理CT目录中的旧文件")
parser.add_argument("--dry-run", action="store_true", help="只整理文件和生成报告,不写数据库")
parser.add_argument("--host", default=os.getenv("PGHOST", ""))
parser.add_argument("--port", default=os.getenv("PGPORT", ""))
parser.add_argument("--dbname", default=os.getenv("PGDATABASE", ""))
parser.add_argument("--user", default=os.getenv("PGUSER", ""))
parser.add_argument("--password", default=os.getenv("PGPASSWORD", ""), help="数据库密码也可用PGPASSWORD环境变量")
return parser.parse_args()
def main() -> None:
args = parse_args()
stl_root = Path(args.stl_root)
processed_root = Path(args.processed_root)
all_candidates, no_ct_dirs = scan_stl_candidates(stl_root)
selected = choose_candidates(all_candidates)
processed_files, link_methods = materialize_selected_stl(
selected=selected,
processed_root=processed_root,
link_mode=args.link_mode,
refresh_files=not args.no_refresh_files,
)
list_records, invalid_list_records = load_list_records(Path(args.list_json))
asset_rows, stl_rows, stl_file_count = build_rows(
selected=selected,
all_candidates=all_candidates,
processed_root=processed_root,
processed_files=processed_files,
list_records=list_records,
)
duplicate_ct_numbers = sorted(ct_number for ct_number, items in all_candidates.items() if len(items) > 1)
report = {
"stl_candidate_dirs": sum(len(items) for items in all_candidates.values()),
"stl_unique_ct": len(selected),
"stl_no_ct_dirs": len(no_ct_dirs),
"stl_duplicate_ct": len(duplicate_ct_numbers),
"list_unique_valid_ct": len(list_records),
"list_invalid_ct_records": len(invalid_list_records),
"asset_rows": len(asset_rows),
"stl_table_rows": len(stl_rows),
"stl_file_rows": stl_file_count,
"matched_list_and_stl_ct": len(set(selected) & set(list_records)),
"list_without_stl_ct": len(set(list_records) - set(selected)),
"stl_without_list_ct": len(set(selected) - set(list_records)),
"duplicate_ct_examples": {
ct_number: [item.summary() for item in sorted(all_candidates[ct_number], key=lambda item: item.score(), reverse=True)]
for ct_number in duplicate_ct_numbers[:20]
},
"no_ct_dir_examples": no_ct_dirs[:20],
"invalid_list_ct_examples": invalid_list_records[:20],
"link_methods": dict(sorted(link_methods.items())[:20]),
"processed_root": path_text(processed_root),
"selection_rule": "同一CT号优先选择STL文件数更多文件数相同选择目录/病例名中的较大序号",
"dry_run": args.dry_run,
}
write_report(Path(args.report), report)
asset_csv = write_csv(asset_rows, ASSET_FIELDS, "_upp_exam_assets.csv")
stl_csv = write_csv(stl_rows, STL_FIELDS, "_upp_stl_files.csv")
try:
if not args.dry_run:
run_psql(args, asset_csv, stl_csv)
finally:
asset_csv.unlink(missing_ok=True)
stl_csv.unlink(missing_ok=True)
print(json.dumps({k: report[k] for k in [
"stl_candidate_dirs",
"stl_unique_ct",
"stl_no_ct_dirs",
"stl_duplicate_ct",
"list_unique_valid_ct",
"asset_rows",
"stl_table_rows",
"stl_file_rows",
"matched_list_and_stl_ct",
"list_without_stl_ct",
"stl_without_list_ct",
]}, ensure_ascii=False))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""统计已处理STL名称并按可解释的医学/结构类别归类。"""
from __future__ import annotations
import csv
import json
import re
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any
BASE_DIR = Path(__file__).resolve().parents[1]
DEFAULT_PROCESSED_ROOT = BASE_DIR / "UPP_STL处理" / "已处理STL数据"
DEFAULT_JSON = BASE_DIR / "UPP_数据库构建" / "UPP_STL名称统计.json"
DEFAULT_CSV = BASE_DIR / "UPP_数据库构建" / "UPP_STL名称统计.csv"
DEFAULT_FAMILY_CSV = BASE_DIR / "UPP_数据库构建" / "UPP_STL_family统计.csv"
DEFAULT_ORDERED_CSV = BASE_DIR / "UPP_数据库构建" / "UPP_STL文件family顺序明细.csv"
def classify(segment_name: str) -> tuple[str, str]:
if segment_name in {"liver", "liver_left", "liver_right"}:
return "肝脏主体", segment_name
if re.fullmatch(r"liver_segment_S[1-8]", segment_name):
return "肝段", segment_name
if segment_name in {"liver_artery", "liver_vein", "portal_vein", "bile_duct"}:
return "血管胆管", segment_name
if segment_name in {"pancreas", "spleen", "cholecyst"}:
return "腹部脏器", segment_name
if segment_name in {"skin", "rib", "vertebrae", "sternum", "hipbone", "sacrum"}:
return "体表骨骼", segment_name
if re.fullmatch(r"liver_tumor_\d+", segment_name):
return "肝脏肿瘤", "liver_tumor_*"
if re.fullmatch(r"liver_cyst_\d+", segment_name):
return "肝囊肿", "liver_cyst_*"
if re.fullmatch(r"liver_hemangioma_\d+", segment_name):
return "肝血管瘤", "liver_hemangioma_*"
if re.fullmatch(r"pancreas_tumor_\d+", segment_name):
return "胰腺肿瘤", "pancreas_tumor_*"
if re.fullmatch(r"Segment_\d+", segment_name):
return "未命名分割", "Segment_*"
return "其他", segment_name
def collect(processed_root: Path) -> dict[str, Any]:
segment_counter: Counter[str] = Counter()
category_counter: Counter[str] = Counter()
family_counter: Counter[str] = Counter()
family_ct: dict[str, set[str]] = defaultdict(set)
category_ct: dict[str, set[str]] = defaultdict(set)
name_ct: dict[str, set[str]] = defaultdict(set)
family_members: dict[str, set[str]] = defaultdict(set)
ordered_files: list[dict[str, Any]] = []
ct_dirs = [item for item in processed_root.iterdir() if item.is_dir()]
for ct_dir in sorted(ct_dirs):
ct_number = ct_dir.name
for order_no, stl_file in enumerate(sorted(ct_dir.glob("*.stl")), start=1):
segment_name = stl_file.stem
category, family = classify(segment_name)
segment_counter[segment_name] += 1
category_counter[category] += 1
family_counter[family] += 1
name_ct[segment_name].add(ct_number)
family_ct[family].add(ct_number)
category_ct[category].add(ct_number)
family_members[family].add(segment_name)
ordered_files.append(
{
"ct_number": ct_number,
"order_no": order_no,
"segment_name": segment_name,
"family": family,
"category": category,
"file_name": stl_file.name,
"file_path": str(stl_file),
}
)
by_name = [
{
"segment_name": name,
"category": classify(name)[0],
"family": classify(name)[1],
"file_count": count,
"ct_count": len(name_ct[name]),
}
for name, count in segment_counter.most_common()
]
by_category = [
{
"category": category,
"file_count": count,
"ct_count": len(category_ct[category]),
}
for category, count in category_counter.most_common()
]
by_family = [
{
"family": family,
"category": classify(family.replace("*", "1"))[0] if "*" in family else classify(family)[0],
"file_count": count,
"ct_count": len(family_ct[family]),
"segment_name_count": len(family_members[family]),
"segment_names": "|".join(sorted(family_members[family])),
}
for family, count in family_counter.most_common()
]
return {
"processed_root": str(processed_root.relative_to(BASE_DIR)),
"ct_count": len(ct_dirs),
"stl_file_count": sum(segment_counter.values()),
"unique_segment_name_count": len(segment_counter),
"by_category": by_category,
"by_family": by_family,
"by_name": by_name,
"ordered_files": ordered_files,
}
def write_csv(path: Path, rows: list[dict[str, Any]], fieldnames: list[str]) -> None:
with path.open("w", encoding="utf-8", newline="") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
def main() -> None:
result = collect(DEFAULT_PROCESSED_ROOT)
ordered_files = result.pop("ordered_files")
DEFAULT_JSON.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
write_csv(DEFAULT_CSV, result["by_name"], ["segment_name", "category", "family", "file_count", "ct_count"])
write_csv(
DEFAULT_FAMILY_CSV,
result["by_family"],
["family", "category", "file_count", "ct_count", "segment_name_count", "segment_names"],
)
write_csv(
DEFAULT_ORDERED_CSV,
ordered_files,
["ct_number", "order_no", "segment_name", "family", "category", "file_name", "file_path"],
)
print(json.dumps({
"ct_count": result["ct_count"],
"stl_file_count": result["stl_file_count"],
"unique_segment_name_count": result["unique_segment_name_count"],
"json": str(DEFAULT_JSON),
"csv": str(DEFAULT_CSV),
"family_csv": str(DEFAULT_FAMILY_CSV),
"ordered_csv": str(DEFAULT_ORDERED_CSV),
}, ensure_ascii=False))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,45 @@
# UPP STL资产索引
本目录用于把 `UPP_STL处理/待处理STL数据``UPP列表处理` 的合并 list 建成 CT 号唯一索引。
处理规则:
- STL 候选目录必须能从路径中提取 `CT号`,格式为 `CT...``DCT...`
- 同一 CT 号有多个 STL 目录时,优先选择 STL 文件数更多的目录。
- 文件数相同,则选择病例名或 `*-STL` 目录中序号更大的目录。
- 规范化输出到 `UPP_STL处理/已处理STL数据/<CT号>/`
- 数据库中 `upp_exam_assets.ct_number` 是主键,可用 list 的检查号查询对应 STL 路径;未来 CT 数据也可以继续挂在同一行。
运行:
```bash
cd /home/wkmgc/Desktop/PACS数据处理
PGHOST='<host>' PGPORT='<port>' PGDATABASE='<database>' PGUSER='<user>' PGPASSWORD='<password>' \
python3 UPP_数据库构建/02_同步UPP_STL资产.py
```
主要表:
- `upp_exam_assets`CT 号唯一资产索引,含 list 元数据、最终选中 STL 目录、候选目录、STL 文件清单。
- `upp_stl_files`:最终选中 STL 的 CT 级聚合表,每个 CT 号唯一一行文件名、分割名、family、分类、路径和完整文件清单用 JSONB 存储。
STL 名称统计:
```bash
python3 UPP_数据库构建/03_统计STL名称.py
```
输出:
- `UPP_数据库构建/UPP_STL名称统计.json`
- `UPP_数据库构建/UPP_STL名称统计.csv`
- `UPP_数据库构建/UPP_STL_family统计.csv`
- `UPP_数据库构建/UPP_STL文件family顺序明细.csv`,含逐 CT 文件路径,默认不提交
常用查询:
```sql
SELECT ct_number, patient_name, processed_stl_dir, stl_file_count
FROM upp_exam_assets
WHERE ct_number = '<CT号>';
```

View File

@@ -0,0 +1,30 @@
family,category,file_count,ct_count,segment_name_count,segment_names
liver_cyst_*,肝囊肿,945,272,37,liver_cyst_1|liver_cyst_10|liver_cyst_11|liver_cyst_12|liver_cyst_13|liver_cyst_14|liver_cyst_15|liver_cyst_16|liver_cyst_17|liver_cyst_18|liver_cyst_19|liver_cyst_2|liver_cyst_20|liver_cyst_21|liver_cyst_22|liver_cyst_23|liver_cyst_24|liver_cyst_25|liver_cyst_26|liver_cyst_27|liver_cyst_28|liver_cyst_29|liver_cyst_3|liver_cyst_30|liver_cyst_31|liver_cyst_32|liver_cyst_33|liver_cyst_34|liver_cyst_35|liver_cyst_36|liver_cyst_37|liver_cyst_4|liver_cyst_5|liver_cyst_6|liver_cyst_7|liver_cyst_8|liver_cyst_9
liver_tumor_*,肝脏肿瘤,839,576,12,liver_tumor_1|liver_tumor_10|liver_tumor_11|liver_tumor_12|liver_tumor_2|liver_tumor_3|liver_tumor_4|liver_tumor_5|liver_tumor_6|liver_tumor_7|liver_tumor_8|liver_tumor_9
liver,肝脏主体,725,725,1,liver
liver_left,肝脏主体,725,725,1,liver_left
liver_right,肝脏主体,725,725,1,liver_right
liver_segment_S1,肝段,725,725,1,liver_segment_S1
liver_segment_S2,肝段,725,725,1,liver_segment_S2
liver_segment_S3,肝段,725,725,1,liver_segment_S3
liver_segment_S4,肝段,725,725,1,liver_segment_S4
liver_segment_S5,肝段,725,725,1,liver_segment_S5
liver_segment_S6,肝段,725,725,1,liver_segment_S6
liver_segment_S7,肝段,725,725,1,liver_segment_S7
liver_segment_S8,肝段,725,725,1,liver_segment_S8
skin,体表骨骼,725,725,1,skin
liver_artery,血管胆管,724,724,1,liver_artery
portal_vein,血管胆管,724,724,1,portal_vein
rib,体表骨骼,724,724,1,rib
vertebrae,体表骨骼,724,724,1,vertebrae
liver_vein,血管胆管,723,723,1,liver_vein
pancreas,腹部脏器,723,723,1,pancreas
bile_duct,血管胆管,720,720,1,bile_duct
spleen,腹部脏器,717,717,1,spleen
sternum,体表骨骼,684,684,1,sternum
cholecyst,腹部脏器,565,565,1,cholecyst
hipbone,体表骨骼,455,455,1,hipbone
sacrum,体表骨骼,445,445,1,sacrum
liver_hemangioma_*,肝血管瘤,74,43,5,liver_hemangioma_1|liver_hemangioma_2|liver_hemangioma_3|liver_hemangioma_4|liver_hemangioma_5
pancreas_tumor_*,胰腺肿瘤,12,12,1,pancreas_tumor_1
Segment_*,未命名分割,9,5,4,Segment_0|Segment_1|Segment_2|Segment_3
1 family category file_count ct_count segment_name_count segment_names
2 liver_cyst_* 肝囊肿 945 272 37 liver_cyst_1|liver_cyst_10|liver_cyst_11|liver_cyst_12|liver_cyst_13|liver_cyst_14|liver_cyst_15|liver_cyst_16|liver_cyst_17|liver_cyst_18|liver_cyst_19|liver_cyst_2|liver_cyst_20|liver_cyst_21|liver_cyst_22|liver_cyst_23|liver_cyst_24|liver_cyst_25|liver_cyst_26|liver_cyst_27|liver_cyst_28|liver_cyst_29|liver_cyst_3|liver_cyst_30|liver_cyst_31|liver_cyst_32|liver_cyst_33|liver_cyst_34|liver_cyst_35|liver_cyst_36|liver_cyst_37|liver_cyst_4|liver_cyst_5|liver_cyst_6|liver_cyst_7|liver_cyst_8|liver_cyst_9
3 liver_tumor_* 肝脏肿瘤 839 576 12 liver_tumor_1|liver_tumor_10|liver_tumor_11|liver_tumor_12|liver_tumor_2|liver_tumor_3|liver_tumor_4|liver_tumor_5|liver_tumor_6|liver_tumor_7|liver_tumor_8|liver_tumor_9
4 liver 肝脏主体 725 725 1 liver
5 liver_left 肝脏主体 725 725 1 liver_left
6 liver_right 肝脏主体 725 725 1 liver_right
7 liver_segment_S1 肝段 725 725 1 liver_segment_S1
8 liver_segment_S2 肝段 725 725 1 liver_segment_S2
9 liver_segment_S3 肝段 725 725 1 liver_segment_S3
10 liver_segment_S4 肝段 725 725 1 liver_segment_S4
11 liver_segment_S5 肝段 725 725 1 liver_segment_S5
12 liver_segment_S6 肝段 725 725 1 liver_segment_S6
13 liver_segment_S7 肝段 725 725 1 liver_segment_S7
14 liver_segment_S8 肝段 725 725 1 liver_segment_S8
15 skin 体表骨骼 725 725 1 skin
16 liver_artery 血管胆管 724 724 1 liver_artery
17 portal_vein 血管胆管 724 724 1 portal_vein
18 rib 体表骨骼 724 724 1 rib
19 vertebrae 体表骨骼 724 724 1 vertebrae
20 liver_vein 血管胆管 723 723 1 liver_vein
21 pancreas 腹部脏器 723 723 1 pancreas
22 bile_duct 血管胆管 720 720 1 bile_duct
23 spleen 腹部脏器 717 717 1 spleen
24 sternum 体表骨骼 684 684 1 sternum
25 cholecyst 腹部脏器 565 565 1 cholecyst
26 hipbone 体表骨骼 455 455 1 hipbone
27 sacrum 体表骨骼 445 445 1 sacrum
28 liver_hemangioma_* 肝血管瘤 74 43 5 liver_hemangioma_1|liver_hemangioma_2|liver_hemangioma_3|liver_hemangioma_4|liver_hemangioma_5
29 pancreas_tumor_* 胰腺肿瘤 12 12 1 pancreas_tumor_1
30 Segment_* 未命名分割 9 5 4 Segment_0|Segment_1|Segment_2|Segment_3

View File

@@ -0,0 +1,84 @@
segment_name,category,family,file_count,ct_count
liver,肝脏主体,liver,725,725
liver_left,肝脏主体,liver_left,725,725
liver_right,肝脏主体,liver_right,725,725
liver_segment_S1,肝段,liver_segment_S1,725,725
liver_segment_S2,肝段,liver_segment_S2,725,725
liver_segment_S3,肝段,liver_segment_S3,725,725
liver_segment_S4,肝段,liver_segment_S4,725,725
liver_segment_S5,肝段,liver_segment_S5,725,725
liver_segment_S6,肝段,liver_segment_S6,725,725
liver_segment_S7,肝段,liver_segment_S7,725,725
liver_segment_S8,肝段,liver_segment_S8,725,725
skin,体表骨骼,skin,725,725
liver_artery,血管胆管,liver_artery,724,724
portal_vein,血管胆管,portal_vein,724,724
rib,体表骨骼,rib,724,724
vertebrae,体表骨骼,vertebrae,724,724
liver_vein,血管胆管,liver_vein,723,723
pancreas,腹部脏器,pancreas,723,723
bile_duct,血管胆管,bile_duct,720,720
spleen,腹部脏器,spleen,717,717
sternum,体表骨骼,sternum,684,684
liver_tumor_1,肝脏肿瘤,liver_tumor_*,576,576
cholecyst,腹部脏器,cholecyst,565,565
hipbone,体表骨骼,hipbone,455,455
sacrum,体表骨骼,sacrum,445,445
liver_cyst_1,肝囊肿,liver_cyst_*,272,272
liver_cyst_2,肝囊肿,liver_cyst_*,154,154
liver_tumor_2,肝脏肿瘤,liver_tumor_*,143,143
liver_cyst_3,肝囊肿,liver_cyst_*,102,102
liver_cyst_4,肝囊肿,liver_cyst_*,70,70
liver_cyst_5,肝囊肿,liver_cyst_*,59,59
liver_tumor_3,肝脏肿瘤,liver_tumor_*,50,50
liver_cyst_6,肝囊肿,liver_cyst_*,49,49
liver_hemangioma_1,肝血管瘤,liver_hemangioma_*,43,43
liver_cyst_7,肝囊肿,liver_cyst_*,36,36
liver_cyst_8,肝囊肿,liver_cyst_*,28,28
liver_tumor_4,肝脏肿瘤,liver_tumor_*,25,25
liver_cyst_9,肝囊肿,liver_cyst_*,23,23
liver_cyst_10,肝囊肿,liver_cyst_*,20,20
liver_hemangioma_2,肝血管瘤,liver_hemangioma_*,16,16
liver_cyst_11,肝囊肿,liver_cyst_*,15,15
liver_cyst_12,肝囊肿,liver_cyst_*,12,12
liver_cyst_13,肝囊肿,liver_cyst_*,12,12
pancreas_tumor_1,胰腺肿瘤,pancreas_tumor_*,12,12
liver_tumor_5,肝脏肿瘤,liver_tumor_*,12,12
liver_cyst_14,肝囊肿,liver_cyst_*,11,11
liver_tumor_6,肝脏肿瘤,liver_tumor_*,10,10
liver_cyst_15,肝囊肿,liver_cyst_*,9,9
liver_hemangioma_3,肝血管瘤,liver_hemangioma_*,9,9
liver_tumor_7,肝脏肿瘤,liver_tumor_*,9,9
liver_cyst_16,肝囊肿,liver_cyst_*,8,8
liver_cyst_17,肝囊肿,liver_cyst_*,7,7
liver_cyst_18,肝囊肿,liver_cyst_*,6,6
liver_cyst_19,肝囊肿,liver_cyst_*,6,6
liver_cyst_20,肝囊肿,liver_cyst_*,6,6
liver_cyst_21,肝囊肿,liver_cyst_*,5,5
liver_tumor_8,肝脏肿瘤,liver_tumor_*,5,5
liver_hemangioma_4,肝血管瘤,liver_hemangioma_*,4,4
liver_cyst_22,肝囊肿,liver_cyst_*,4,4
liver_cyst_23,肝囊肿,liver_cyst_*,4,4
liver_cyst_24,肝囊肿,liver_cyst_*,4,4
liver_cyst_25,肝囊肿,liver_cyst_*,4,4
Segment_0,未命名分割,Segment_*,4,4
liver_tumor_9,肝脏肿瘤,liver_tumor_*,4,4
liver_cyst_26,肝囊肿,liver_cyst_*,3,3
liver_cyst_27,肝囊肿,liver_cyst_*,3,3
liver_cyst_28,肝囊肿,liver_cyst_*,3,3
liver_tumor_10,肝脏肿瘤,liver_tumor_*,3,3
liver_hemangioma_5,肝血管瘤,liver_hemangioma_*,2,2
liver_cyst_29,肝囊肿,liver_cyst_*,2,2
Segment_1,未命名分割,Segment_*,2,2
Segment_2,未命名分割,Segment_*,2,2
liver_cyst_30,肝囊肿,liver_cyst_*,1,1
liver_cyst_31,肝囊肿,liver_cyst_*,1,1
liver_cyst_32,肝囊肿,liver_cyst_*,1,1
liver_cyst_33,肝囊肿,liver_cyst_*,1,1
liver_cyst_34,肝囊肿,liver_cyst_*,1,1
liver_cyst_35,肝囊肿,liver_cyst_*,1,1
liver_cyst_36,肝囊肿,liver_cyst_*,1,1
liver_cyst_37,肝囊肿,liver_cyst_*,1,1
Segment_3,未命名分割,Segment_*,1,1
liver_tumor_11,肝脏肿瘤,liver_tumor_*,1,1
liver_tumor_12,肝脏肿瘤,liver_tumor_*,1,1
1 segment_name category family file_count ct_count
2 liver 肝脏主体 liver 725 725
3 liver_left 肝脏主体 liver_left 725 725
4 liver_right 肝脏主体 liver_right 725 725
5 liver_segment_S1 肝段 liver_segment_S1 725 725
6 liver_segment_S2 肝段 liver_segment_S2 725 725
7 liver_segment_S3 肝段 liver_segment_S3 725 725
8 liver_segment_S4 肝段 liver_segment_S4 725 725
9 liver_segment_S5 肝段 liver_segment_S5 725 725
10 liver_segment_S6 肝段 liver_segment_S6 725 725
11 liver_segment_S7 肝段 liver_segment_S7 725 725
12 liver_segment_S8 肝段 liver_segment_S8 725 725
13 skin 体表骨骼 skin 725 725
14 liver_artery 血管胆管 liver_artery 724 724
15 portal_vein 血管胆管 portal_vein 724 724
16 rib 体表骨骼 rib 724 724
17 vertebrae 体表骨骼 vertebrae 724 724
18 liver_vein 血管胆管 liver_vein 723 723
19 pancreas 腹部脏器 pancreas 723 723
20 bile_duct 血管胆管 bile_duct 720 720
21 spleen 腹部脏器 spleen 717 717
22 sternum 体表骨骼 sternum 684 684
23 liver_tumor_1 肝脏肿瘤 liver_tumor_* 576 576
24 cholecyst 腹部脏器 cholecyst 565 565
25 hipbone 体表骨骼 hipbone 455 455
26 sacrum 体表骨骼 sacrum 445 445
27 liver_cyst_1 肝囊肿 liver_cyst_* 272 272
28 liver_cyst_2 肝囊肿 liver_cyst_* 154 154
29 liver_tumor_2 肝脏肿瘤 liver_tumor_* 143 143
30 liver_cyst_3 肝囊肿 liver_cyst_* 102 102
31 liver_cyst_4 肝囊肿 liver_cyst_* 70 70
32 liver_cyst_5 肝囊肿 liver_cyst_* 59 59
33 liver_tumor_3 肝脏肿瘤 liver_tumor_* 50 50
34 liver_cyst_6 肝囊肿 liver_cyst_* 49 49
35 liver_hemangioma_1 肝血管瘤 liver_hemangioma_* 43 43
36 liver_cyst_7 肝囊肿 liver_cyst_* 36 36
37 liver_cyst_8 肝囊肿 liver_cyst_* 28 28
38 liver_tumor_4 肝脏肿瘤 liver_tumor_* 25 25
39 liver_cyst_9 肝囊肿 liver_cyst_* 23 23
40 liver_cyst_10 肝囊肿 liver_cyst_* 20 20
41 liver_hemangioma_2 肝血管瘤 liver_hemangioma_* 16 16
42 liver_cyst_11 肝囊肿 liver_cyst_* 15 15
43 liver_cyst_12 肝囊肿 liver_cyst_* 12 12
44 liver_cyst_13 肝囊肿 liver_cyst_* 12 12
45 pancreas_tumor_1 胰腺肿瘤 pancreas_tumor_* 12 12
46 liver_tumor_5 肝脏肿瘤 liver_tumor_* 12 12
47 liver_cyst_14 肝囊肿 liver_cyst_* 11 11
48 liver_tumor_6 肝脏肿瘤 liver_tumor_* 10 10
49 liver_cyst_15 肝囊肿 liver_cyst_* 9 9
50 liver_hemangioma_3 肝血管瘤 liver_hemangioma_* 9 9
51 liver_tumor_7 肝脏肿瘤 liver_tumor_* 9 9
52 liver_cyst_16 肝囊肿 liver_cyst_* 8 8
53 liver_cyst_17 肝囊肿 liver_cyst_* 7 7
54 liver_cyst_18 肝囊肿 liver_cyst_* 6 6
55 liver_cyst_19 肝囊肿 liver_cyst_* 6 6
56 liver_cyst_20 肝囊肿 liver_cyst_* 6 6
57 liver_cyst_21 肝囊肿 liver_cyst_* 5 5
58 liver_tumor_8 肝脏肿瘤 liver_tumor_* 5 5
59 liver_hemangioma_4 肝血管瘤 liver_hemangioma_* 4 4
60 liver_cyst_22 肝囊肿 liver_cyst_* 4 4
61 liver_cyst_23 肝囊肿 liver_cyst_* 4 4
62 liver_cyst_24 肝囊肿 liver_cyst_* 4 4
63 liver_cyst_25 肝囊肿 liver_cyst_* 4 4
64 Segment_0 未命名分割 Segment_* 4 4
65 liver_tumor_9 肝脏肿瘤 liver_tumor_* 4 4
66 liver_cyst_26 肝囊肿 liver_cyst_* 3 3
67 liver_cyst_27 肝囊肿 liver_cyst_* 3 3
68 liver_cyst_28 肝囊肿 liver_cyst_* 3 3
69 liver_tumor_10 肝脏肿瘤 liver_tumor_* 3 3
70 liver_hemangioma_5 肝血管瘤 liver_hemangioma_* 2 2
71 liver_cyst_29 肝囊肿 liver_cyst_* 2 2
72 Segment_1 未命名分割 Segment_* 2 2
73 Segment_2 未命名分割 Segment_* 2 2
74 liver_cyst_30 肝囊肿 liver_cyst_* 1 1
75 liver_cyst_31 肝囊肿 liver_cyst_* 1 1
76 liver_cyst_32 肝囊肿 liver_cyst_* 1 1
77 liver_cyst_33 肝囊肿 liver_cyst_* 1 1
78 liver_cyst_34 肝囊肿 liver_cyst_* 1 1
79 liver_cyst_35 肝囊肿 liver_cyst_* 1 1
80 liver_cyst_36 肝囊肿 liver_cyst_* 1 1
81 liver_cyst_37 肝囊肿 liver_cyst_* 1 1
82 Segment_3 未命名分割 Segment_* 1 1
83 liver_tumor_11 肝脏肿瘤 liver_tumor_* 1 1
84 liver_tumor_12 肝脏肿瘤 liver_tumor_* 1 1

View File

@@ -0,0 +1,875 @@
{
"processed_root": "UPP_STL处理/已处理STL数据",
"ct_count": 726,
"stl_file_count": 18507,
"unique_segment_name_count": 83,
"by_category": [
{
"category": "肝段",
"file_count": 5800,
"ct_count": 725
},
{
"category": "体表骨骼",
"file_count": 3757,
"ct_count": 726
},
{
"category": "血管胆管",
"file_count": 2891,
"ct_count": 724
},
{
"category": "肝脏主体",
"file_count": 2175,
"ct_count": 725
},
{
"category": "腹部脏器",
"file_count": 2005,
"ct_count": 724
},
{
"category": "肝囊肿",
"file_count": 945,
"ct_count": 272
},
{
"category": "肝脏肿瘤",
"file_count": 839,
"ct_count": 576
},
{
"category": "肝血管瘤",
"file_count": 74,
"ct_count": 43
},
{
"category": "胰腺肿瘤",
"file_count": 12,
"ct_count": 12
},
{
"category": "未命名分割",
"file_count": 9,
"ct_count": 5
}
],
"by_family": [
{
"family": "liver_cyst_*",
"category": "肝囊肿",
"file_count": 945,
"ct_count": 272,
"segment_name_count": 37,
"segment_names": "liver_cyst_1|liver_cyst_10|liver_cyst_11|liver_cyst_12|liver_cyst_13|liver_cyst_14|liver_cyst_15|liver_cyst_16|liver_cyst_17|liver_cyst_18|liver_cyst_19|liver_cyst_2|liver_cyst_20|liver_cyst_21|liver_cyst_22|liver_cyst_23|liver_cyst_24|liver_cyst_25|liver_cyst_26|liver_cyst_27|liver_cyst_28|liver_cyst_29|liver_cyst_3|liver_cyst_30|liver_cyst_31|liver_cyst_32|liver_cyst_33|liver_cyst_34|liver_cyst_35|liver_cyst_36|liver_cyst_37|liver_cyst_4|liver_cyst_5|liver_cyst_6|liver_cyst_7|liver_cyst_8|liver_cyst_9"
},
{
"family": "liver_tumor_*",
"category": "肝脏肿瘤",
"file_count": 839,
"ct_count": 576,
"segment_name_count": 12,
"segment_names": "liver_tumor_1|liver_tumor_10|liver_tumor_11|liver_tumor_12|liver_tumor_2|liver_tumor_3|liver_tumor_4|liver_tumor_5|liver_tumor_6|liver_tumor_7|liver_tumor_8|liver_tumor_9"
},
{
"family": "liver",
"category": "肝脏主体",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver"
},
{
"family": "liver_left",
"category": "肝脏主体",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_left"
},
{
"family": "liver_right",
"category": "肝脏主体",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_right"
},
{
"family": "liver_segment_S1",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S1"
},
{
"family": "liver_segment_S2",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S2"
},
{
"family": "liver_segment_S3",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S3"
},
{
"family": "liver_segment_S4",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S4"
},
{
"family": "liver_segment_S5",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S5"
},
{
"family": "liver_segment_S6",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S6"
},
{
"family": "liver_segment_S7",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S7"
},
{
"family": "liver_segment_S8",
"category": "肝段",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "liver_segment_S8"
},
{
"family": "skin",
"category": "体表骨骼",
"file_count": 725,
"ct_count": 725,
"segment_name_count": 1,
"segment_names": "skin"
},
{
"family": "liver_artery",
"category": "血管胆管",
"file_count": 724,
"ct_count": 724,
"segment_name_count": 1,
"segment_names": "liver_artery"
},
{
"family": "portal_vein",
"category": "血管胆管",
"file_count": 724,
"ct_count": 724,
"segment_name_count": 1,
"segment_names": "portal_vein"
},
{
"family": "rib",
"category": "体表骨骼",
"file_count": 724,
"ct_count": 724,
"segment_name_count": 1,
"segment_names": "rib"
},
{
"family": "vertebrae",
"category": "体表骨骼",
"file_count": 724,
"ct_count": 724,
"segment_name_count": 1,
"segment_names": "vertebrae"
},
{
"family": "liver_vein",
"category": "血管胆管",
"file_count": 723,
"ct_count": 723,
"segment_name_count": 1,
"segment_names": "liver_vein"
},
{
"family": "pancreas",
"category": "腹部脏器",
"file_count": 723,
"ct_count": 723,
"segment_name_count": 1,
"segment_names": "pancreas"
},
{
"family": "bile_duct",
"category": "血管胆管",
"file_count": 720,
"ct_count": 720,
"segment_name_count": 1,
"segment_names": "bile_duct"
},
{
"family": "spleen",
"category": "腹部脏器",
"file_count": 717,
"ct_count": 717,
"segment_name_count": 1,
"segment_names": "spleen"
},
{
"family": "sternum",
"category": "体表骨骼",
"file_count": 684,
"ct_count": 684,
"segment_name_count": 1,
"segment_names": "sternum"
},
{
"family": "cholecyst",
"category": "腹部脏器",
"file_count": 565,
"ct_count": 565,
"segment_name_count": 1,
"segment_names": "cholecyst"
},
{
"family": "hipbone",
"category": "体表骨骼",
"file_count": 455,
"ct_count": 455,
"segment_name_count": 1,
"segment_names": "hipbone"
},
{
"family": "sacrum",
"category": "体表骨骼",
"file_count": 445,
"ct_count": 445,
"segment_name_count": 1,
"segment_names": "sacrum"
},
{
"family": "liver_hemangioma_*",
"category": "肝血管瘤",
"file_count": 74,
"ct_count": 43,
"segment_name_count": 5,
"segment_names": "liver_hemangioma_1|liver_hemangioma_2|liver_hemangioma_3|liver_hemangioma_4|liver_hemangioma_5"
},
{
"family": "pancreas_tumor_*",
"category": "胰腺肿瘤",
"file_count": 12,
"ct_count": 12,
"segment_name_count": 1,
"segment_names": "pancreas_tumor_1"
},
{
"family": "Segment_*",
"category": "未命名分割",
"file_count": 9,
"ct_count": 5,
"segment_name_count": 4,
"segment_names": "Segment_0|Segment_1|Segment_2|Segment_3"
}
],
"by_name": [
{
"segment_name": "liver",
"category": "肝脏主体",
"family": "liver",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_left",
"category": "肝脏主体",
"family": "liver_left",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_right",
"category": "肝脏主体",
"family": "liver_right",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S1",
"category": "肝段",
"family": "liver_segment_S1",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S2",
"category": "肝段",
"family": "liver_segment_S2",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S3",
"category": "肝段",
"family": "liver_segment_S3",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S4",
"category": "肝段",
"family": "liver_segment_S4",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S5",
"category": "肝段",
"family": "liver_segment_S5",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S6",
"category": "肝段",
"family": "liver_segment_S6",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S7",
"category": "肝段",
"family": "liver_segment_S7",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_segment_S8",
"category": "肝段",
"family": "liver_segment_S8",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "skin",
"category": "体表骨骼",
"family": "skin",
"file_count": 725,
"ct_count": 725
},
{
"segment_name": "liver_artery",
"category": "血管胆管",
"family": "liver_artery",
"file_count": 724,
"ct_count": 724
},
{
"segment_name": "portal_vein",
"category": "血管胆管",
"family": "portal_vein",
"file_count": 724,
"ct_count": 724
},
{
"segment_name": "rib",
"category": "体表骨骼",
"family": "rib",
"file_count": 724,
"ct_count": 724
},
{
"segment_name": "vertebrae",
"category": "体表骨骼",
"family": "vertebrae",
"file_count": 724,
"ct_count": 724
},
{
"segment_name": "liver_vein",
"category": "血管胆管",
"family": "liver_vein",
"file_count": 723,
"ct_count": 723
},
{
"segment_name": "pancreas",
"category": "腹部脏器",
"family": "pancreas",
"file_count": 723,
"ct_count": 723
},
{
"segment_name": "bile_duct",
"category": "血管胆管",
"family": "bile_duct",
"file_count": 720,
"ct_count": 720
},
{
"segment_name": "spleen",
"category": "腹部脏器",
"family": "spleen",
"file_count": 717,
"ct_count": 717
},
{
"segment_name": "sternum",
"category": "体表骨骼",
"family": "sternum",
"file_count": 684,
"ct_count": 684
},
{
"segment_name": "liver_tumor_1",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 576,
"ct_count": 576
},
{
"segment_name": "cholecyst",
"category": "腹部脏器",
"family": "cholecyst",
"file_count": 565,
"ct_count": 565
},
{
"segment_name": "hipbone",
"category": "体表骨骼",
"family": "hipbone",
"file_count": 455,
"ct_count": 455
},
{
"segment_name": "sacrum",
"category": "体表骨骼",
"family": "sacrum",
"file_count": 445,
"ct_count": 445
},
{
"segment_name": "liver_cyst_1",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 272,
"ct_count": 272
},
{
"segment_name": "liver_cyst_2",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 154,
"ct_count": 154
},
{
"segment_name": "liver_tumor_2",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 143,
"ct_count": 143
},
{
"segment_name": "liver_cyst_3",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 102,
"ct_count": 102
},
{
"segment_name": "liver_cyst_4",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 70,
"ct_count": 70
},
{
"segment_name": "liver_cyst_5",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 59,
"ct_count": 59
},
{
"segment_name": "liver_tumor_3",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 50,
"ct_count": 50
},
{
"segment_name": "liver_cyst_6",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 49,
"ct_count": 49
},
{
"segment_name": "liver_hemangioma_1",
"category": "肝血管瘤",
"family": "liver_hemangioma_*",
"file_count": 43,
"ct_count": 43
},
{
"segment_name": "liver_cyst_7",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 36,
"ct_count": 36
},
{
"segment_name": "liver_cyst_8",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 28,
"ct_count": 28
},
{
"segment_name": "liver_tumor_4",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 25,
"ct_count": 25
},
{
"segment_name": "liver_cyst_9",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 23,
"ct_count": 23
},
{
"segment_name": "liver_cyst_10",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 20,
"ct_count": 20
},
{
"segment_name": "liver_hemangioma_2",
"category": "肝血管瘤",
"family": "liver_hemangioma_*",
"file_count": 16,
"ct_count": 16
},
{
"segment_name": "liver_cyst_11",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 15,
"ct_count": 15
},
{
"segment_name": "liver_cyst_12",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 12,
"ct_count": 12
},
{
"segment_name": "liver_cyst_13",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 12,
"ct_count": 12
},
{
"segment_name": "pancreas_tumor_1",
"category": "胰腺肿瘤",
"family": "pancreas_tumor_*",
"file_count": 12,
"ct_count": 12
},
{
"segment_name": "liver_tumor_5",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 12,
"ct_count": 12
},
{
"segment_name": "liver_cyst_14",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 11,
"ct_count": 11
},
{
"segment_name": "liver_tumor_6",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 10,
"ct_count": 10
},
{
"segment_name": "liver_cyst_15",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 9,
"ct_count": 9
},
{
"segment_name": "liver_hemangioma_3",
"category": "肝血管瘤",
"family": "liver_hemangioma_*",
"file_count": 9,
"ct_count": 9
},
{
"segment_name": "liver_tumor_7",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 9,
"ct_count": 9
},
{
"segment_name": "liver_cyst_16",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 8,
"ct_count": 8
},
{
"segment_name": "liver_cyst_17",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 7,
"ct_count": 7
},
{
"segment_name": "liver_cyst_18",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 6,
"ct_count": 6
},
{
"segment_name": "liver_cyst_19",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 6,
"ct_count": 6
},
{
"segment_name": "liver_cyst_20",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 6,
"ct_count": 6
},
{
"segment_name": "liver_cyst_21",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 5,
"ct_count": 5
},
{
"segment_name": "liver_tumor_8",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 5,
"ct_count": 5
},
{
"segment_name": "liver_hemangioma_4",
"category": "肝血管瘤",
"family": "liver_hemangioma_*",
"file_count": 4,
"ct_count": 4
},
{
"segment_name": "liver_cyst_22",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 4,
"ct_count": 4
},
{
"segment_name": "liver_cyst_23",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 4,
"ct_count": 4
},
{
"segment_name": "liver_cyst_24",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 4,
"ct_count": 4
},
{
"segment_name": "liver_cyst_25",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 4,
"ct_count": 4
},
{
"segment_name": "Segment_0",
"category": "未命名分割",
"family": "Segment_*",
"file_count": 4,
"ct_count": 4
},
{
"segment_name": "liver_tumor_9",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 4,
"ct_count": 4
},
{
"segment_name": "liver_cyst_26",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 3,
"ct_count": 3
},
{
"segment_name": "liver_cyst_27",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 3,
"ct_count": 3
},
{
"segment_name": "liver_cyst_28",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 3,
"ct_count": 3
},
{
"segment_name": "liver_tumor_10",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 3,
"ct_count": 3
},
{
"segment_name": "liver_hemangioma_5",
"category": "肝血管瘤",
"family": "liver_hemangioma_*",
"file_count": 2,
"ct_count": 2
},
{
"segment_name": "liver_cyst_29",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 2,
"ct_count": 2
},
{
"segment_name": "Segment_1",
"category": "未命名分割",
"family": "Segment_*",
"file_count": 2,
"ct_count": 2
},
{
"segment_name": "Segment_2",
"category": "未命名分割",
"family": "Segment_*",
"file_count": 2,
"ct_count": 2
},
{
"segment_name": "liver_cyst_30",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_cyst_31",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_cyst_32",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_cyst_33",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_cyst_34",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_cyst_35",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_cyst_36",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_cyst_37",
"category": "肝囊肿",
"family": "liver_cyst_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "Segment_3",
"category": "未命名分割",
"family": "Segment_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_tumor_11",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 1,
"ct_count": 1
},
{
"segment_name": "liver_tumor_12",
"category": "肝脏肿瘤",
"family": "liver_tumor_*",
"file_count": 1,
"ct_count": 1
}
]
}