Skip to main content

ctbodycomp

CT体成分2D/3D特征提取与独立校正。支持按类别或单个名称选择基础特征,以及独立的身高、体重、BSA和骨参照校正。

安装

在本项目目录运行python -m pip install .。开发时使用python -m pip install -e .。 支持Python 3.10及以上,依赖numpy、nibabel、pandas;测试需要pytest。

四类基础特征

类别 代码 数量
形态:面积或体积 morphometry 11
一阶统计:Mean、SD、P10、P50、P90、IQR firstorder 66
组成与分布:组织比值或占比,百分比 composition 12
复合:面积或体积×平均HU composite 11
合计 100

旧脚本的8项骨参照比和8项骨参照比×HU不再混入基础输出。

组织代码:SM骨骼肌(1)、SF皮下脂肪(4)、IMF肌间脂肪(2)、VF其余内脏脂肪(3)、PVAT血管周围脂肪(6)、EAT心外膜脂肪(7)、AorticPVAT主动脉周围脂肪(8)、Bone骨(5)、CVF(6/7/8)、VF_total(3/6/7/8)、TF(2/3/4/6/7/8)。基础标签互斥。

0.1.5 缺失脊柱标签输出

新增CSV诊断列diagnostics_missing_spine_labels,放在特征列前面,记录请求范围内缺失的脊柱标签。部分标签缺失继续计算;全部缺失仍失败并保留缺失列表。直接调用可读取extractor.last_diagnostics["missing_spine_labels"]。标签是否出现不代表扫描覆盖完整。

0.1.4 自定义标签和缺失组织

相比0.1.3,本版保留BodyCompositionExtractor(..., label_map=...)的调用方式、特征名及缺失处理规则,仅将内部实现改为直接读取原始标签。数量从原始标签计数中查询;HU统计按原始标签筛选CT值。默认标签和自定义标签均不生成重编号mask,但HU筛选和数值统计仍可能产生临时数组。

label_map按以下固定顺序填写使用者原始BC mask中的标签编号,编号由使用者决定,不要求为1–8:

位置 库的标准标签 组织
1 1 SM 骨骼肌
2 2 IMF 肌间脂肪
3 3 VF 其余内脏脂肪
4 4 SF 皮下脂肪
5 5 Bone 骨
6 6 PVAT 血管周围脂肪
7 7 EAT 心外膜脂肪
8 8 AorticPVAT 主动脉周围脂肪
from ctbodycomp import BodyCompositionExtractor
from ctbodycomp.batch import extract_csv

# 这些编号只是示例,请替换成自己的分割标签。
# 第三项为0表示没有VF标签,不能把背景0当成VF。
label_map = [2, 8, 0, 5, 4, 3, 6, 1]
extractor = BodyCompositionExtractor(mode="3D", label_map=label_map)
raw = extractor.execute("ct.nii.gz", "bc_mask.nii.gz")
# 同一个extractor可用于批量CSV,病例表格式不变:
# result = extract_csv("cases.csv", "features.csv", extractor)

# 2D有脊柱时,BC标签映射与脊柱标签分别设置:
extractor_2d = BodyCompositionExtractor(mode="2D", spine_label=7, label_map=label_map)
# raw_2d = extractor_2d.execute("ct.nii.gz", "bc_mask.nii.gz", spine_mask="spine.nii.gz")

不传label_map时沿用标准顺序[1, 2, 3, 4, 5, 6, 7, 8]。传入时必须为8项整数列表或元组;缺失组织填0,非零编号必须为正整数且不能重复。未映射的其他标签不参与组织计算。0.1.4按映射直接查询原始mask中的标签计数和对应CT值,不创建转换后的标签mask,不修改原mask,也不会修改脊柱标签或影像空间。整个批次使用同一映射,标签规范不同的病例应分批设置各自的映射。

**0.1.3改变了缺失组织的处理:**在实际选中的范围内先统计标签,所选特征的依赖标签全部存在才计算。缺失组织的面积、体积、一阶统计及原始复合均为NaN,CSV保存为空单元格;保留所有选定特征列,不按病例删除列。标签没有出现表示本次无法提供该组织的测量,不再用0表示缺失。

  • CVF需要6、7、8全部存在;VF_total需要3、6、7、8全部存在;TF需要2、3、4、6、7、8全部存在。缺一个标签,对应复合组织的所有特征留空,不能用部分组织之和替代。
  • 组成与分布特征检查分子、分母所依赖的全部标签。例如MFF需要1、2;SF_to_SM需要4、1;VF_total_to_SM需要3、6、7、8、1。缺少任意依赖就留空。
  • 2D两层模式仍先逐层计算,再取等权平均;某特征在任一层缺少依赖时,最终该特征也留空。整幅影像其他层存在的标签不能补足当前测量范围的缺失。
  • 后续校正保留这些缺失值:目标组织缺失时,其校正指数与校正复合留空;骨缺失时骨校正留空,但有有效身高等参数时其他校正仍可计算。特征值留空不等于病例提取失败,批量流程仍保留病例及状态。

extractor.last_diagnostics包含所用的label_map及选中范围出现的标准present_labels。它们记录映射和观察到的标签,不证明分割边界或组织定义与原项目相同。尤其VF需要排除单独分割的6–8区域,骨和脂肪组织的具体边界需符合相应输入规范;标签映射只更换编号。

先提取第一类,再校正

from ctbodycomp import BodyCompositionExtractor, FeatureNormalizer

extractor = BodyCompositionExtractor(
    mode="3D",
    features={"morphometry": ["SM_Volume", "SF_Volume", "Bone_Volume"]},
)
raw = extractor.execute(image="ct.nii.gz", mask="bc_mask.nii.gz")

# 只校正指定组织;原始结果与校正结果是两个独立字典。
normalizer = FeatureNormalizer(
    mode="3D", methods=["height", "weight", "bsa", "bone"], tissues=["SM", "SF"]
)
corrected = normalizer.execute(raw, height_cm=170, weight_kg=65)

校正器根据第一类形态指标计算校正指数;开启include_composite=True时,同时读取一阶Mean并计算校正指数×Mean。不读取影像,不对HU本身或组成比值二次校正。可以把提取结果保存后,换一组校正方式再次计算。骨校正需提前提取同一范围的Bone_Volume或Bone_Area。

方法 公式 2D单位 3D单位
height 原始面积/体积 ÷ 身高(m)² cm²/m² cm³/m²
weight 原始面积/体积 ÷ 体重(kg) cm²/kg cm³/kg
bsa 原始面积/体积 ÷ BSA(m²) cm²/m² cm³/m²
bone 原始面积/体积 ÷ 同范围骨面积/体积 无量纲 无量纲

2D和3D都使用身高²。默认BSA使用Mosteller公式:sqrt(height_cm * weight_kg / 3600),也可通过bsa_m2直接提供已知BSA;提供值优先,来源记录在normalizer.last_diagnostics。Mosteller公式参考:原始论文。

身高、体重、BSA需为有限正数;只选择骨校正时不需要身高体重。缺少所选方法需要的参数会抛出明确错误。骨量为0时骨校正输出NaN。CSV校正失败时保留原始特征及失败原因。

若提取全部11个形态特征且启用全部校正,height、weight、bsa各11项,bone对10个非骨组织输出,共43项。与100项基础特征分开存储。骨校正包括CVF和TF两个聚合组织,不输出恒为1的Bone/Bone。实际输出量由所选组织、特征和校正方式决定。

可选:校正指数 × 平均HU

从0.1.1开始,校正指数与校正后的复合特征统一由FeatureNormalizer管理。使用include_composite=True,同一次校正返回“指数”和“指数×同组织平均HU”,每一对相邻排列。两者均属于校正模块,与四类基础特征分开。默认不开启校正复合项。

from ctbodycomp import BodyCompositionExtractor, FeatureNormalizer

extractor = BodyCompositionExtractor(
    mode="3D",
    features={
        "morphometry": ["SM_Volume", "Bone_Volume"],
        "firstorder": ["SM_Mean"],
    },
)
raw = extractor.execute("ct.nii.gz", "bc_mask.nii.gz")
normalizer = FeatureNormalizer(
    mode="3D", methods=["height", "bone"], tissues=["SM"],
    include_composite=True,
)
corrected = normalizer.execute(raw, height_cm=170)
# normalization_height_SM_Volume
# normalized_composite_height_SM_VolumeMean
# normalization_bone_SM_Volume
# normalized_composite_bone_SM_VolumeMean
units = normalizer.get_units(raw)

2D将模式改为2D,将第一类特征的Volume改为Area,输出为normalized_composite_height_SM_AreaMean等。

方法 公式,μ为同组织平均HU 2D单位 3D单位 全部组织数量
height (面积/体积 ÷ 身高(m)²) × μ cm²·HU/m² cm³·HU/m² 11
weight (面积/体积 ÷ 体重(kg)) × μ cm²·HU/kg cm³·HU/kg 11
bsa (面积/体积 ÷ BSA(m²)) × μ cm²·HU/m² cm³·HU/m² 11
bone (面积/体积 ÷ 同范围骨面积/体积) × μ HU HU 10

共43项可选校正复合特征,不输出Bone/Bone×HU。每种模式最多有100项基础特征+43项校正指数+43项校正复合特征=186项。2D和3D是两种模式,不合并计数。负平均HU保留符号;指数或均值为NaN/无穷时结果为NaN,不解释为实际组织质量。

通过methods选择校正方式,通过tissues选择组织;tissues=None从输入已有的形态指标中选择组织。开启复合项需要同时提取目标组织的Mean。可用get_normalized_composite_catalog("3D")查询完整复合名称、单位、指数键和均值键。

对于已经保存的校正指数字典,仍支持NormalizedCompositeCalculator(mode, methods, tissues).execute(indices, raw)单独补算复合项;两个字典需来自同一病例、同一范围。

2D两层的计算顺序:先将两层面积与两层平均HU分别等权平均,面积平均值进行校正,再乘HU平均值。因此结果是“校正后的平均面积指数 × 平均HU”,与基础复合类的“逐层面积×HU后平均”定义不同。例如两层面积0.06、0.18 cm²,均值10、30 HU,身高1 m:校正复合为0.12×20=2.4 cm²·HU/m²,而基础复合为(0.06×10+0.18×30)/2=3 cm²·HU。

选择类别或单项

from ctbodycomp import BodyCompositionExtractor, get_feature_catalog

extractor = BodyCompositionExtractor(mode="2D", spine_label=7)
extractor.disableAllFeatures()
extractor.enableFeaturesByName(
    morphometry=["SM_Area", "Bone_Area"],
    firstorder=["SM_Mean", "SM_P50"],
    composition=["MFF"],
)
raw = extractor.execute("ct.nii.gz", "bc_mask.nii.gz", spine_mask="spine.nii.gz")

# 按整个类别启用:
extractor.enableFeatureClassByName("firstorder")
# 查询当前模式下全部可用名称、英文输出键和单位:
catalog = get_feature_catalog("2D")

构造参数features=None默认启用全部四类;features={}不启用任何特征。映射中未列出的类别不提取,类别中的[]或None表示该类全部。enableFeaturesByName替换指定类别的选择,其他已启用类别保持不变,因此要只选择几项时先调用disableAllFeatures()。支持全部启用enableAllFeatures()和单类别关闭enableFeatureClassByName("firstorder", False)。

只输出选择的特征,内部依赖按需计算。例如只选Mean不计算分位数,只选形态不计算HU统计;只选一个组成比值时也不输出其内部依赖的组织面积/体积。

输入与选层

  • 必需:CT与BC mask。接受文件路径或nibabel图像对象,图像需3D且空间匹配,尺寸以mm解释。
  • 裁剪、选层和特征计算前,统一检查CT与BC mask的完整数组尺寸(shape)及空间变换矩阵(affine);提供Spine mask时也检查它与CT一致。affine包含体素尺寸、方向和原点,比较使用绝对容差1e-4、相对容差0。不一致直接失败,不自动重采样或对齐。CSV批量提取会记录extraction_status=failed和具体原因,并继续其他病例。
  • 可选:Spine mask。不提供时3D计算整幅CT,2D在整幅CT全部层中选中间层。
  • 2D有Spine mask时需spine_label,例如7或8。先找到该标签出现的最小、最大切片,以首末之间的整个连续区间为候选范围;奇数层取中间一层,偶数层取中间两层,即使中间层没有该脊柱标签也照常使用。
  • 3D有Spine mask时默认范围spine_range=(7, 18),可配置,取首末层之间的完整范围。新库依据affine识别上下方向,支持数组轴置换。
  • 每个选中切片的组织ROI来自完整BC mask平面,Spine mask仅定位。斜位影像保持原切片平面。
  • 2D两层基础特征先逐层计算再等权平均。独立校正使用这个平均后的形态值;骨校正因此是“平均组织面积/平均骨面积”,不同于旧脚本的“两层骨比值平均”。
  • SD使用ddof=0;分位数为线性插值;非有限CT值不进入HU统计;缺失组织的面积/体积及其他特征均为NaN;存在组织但没有有限HU样本时,数量可计算而HU统计与乘HU项为NaN;任一层某基础指标NaN,则两层平均也为NaN。

CSV输出顺序为:原输入信息列 → diagnostics_scope、diagnostics_slices、diagnostics_missing_spine_labels、extraction_status → 所选特征列 → extraction_error。连续切片简写为42-374,单层为42;二维中间两层同样表示为42-43。extractor.last_diagnostics["selected_slices"]仍保留完整列表,只有CSV显示采用简写。

diagnostics_missing_spine_labels记录请求的脊柱标签中未在输入Spine mask出现的编号,例如[9, 12];已检查且没有缺失时为[],无Spine mask或尚未完成标签检查时为空。2D只检查spine_label;3D检查spine_range包含的全部整数标签。部分标签缺失仍按实际找到的首末切片计算;全部缺失仍失败,但缺失列表保留。该字段只检查标签是否出现,不判断扫描覆盖完整性或椎体边界截断。

直接路径或图像对象调用后,可从extractor.last_diagnostics["missing_spine_labels"]取得相同的整数列表;同时提供requested_spine_labels及present_spine_labels。未提供Spine mask时这三个列表均为[],应结合scope="full_image"解释。因目标标签全部缺失而失败时,异常仍会抛出,缺失列表保留在last_diagnostics中;若在读取文件或空间校验时已失败,则没有这些标签检查结果。

诊断信息在extractor.last_diagnostics,与纯数值特征分开。无Spine mask请传None;填写了错误文件路径不会自动退回全图计算。

CSV分两步运行

新库的CSV使用有意义的列名(区分大小写),不同于旧脚本按列位置读取:

ID,Image,Mask,SpineMask,Height_cm,Weight_kg
001,ct.nii.gz,bc_mask.nii.gz,,170,65

必需Image、Mask,其他列可选;SpineMask可省略或留空。相对路径相对于输入CSV目录。Height_cm、Weight_kg、可选BSA_m2由校正步骤读取,逐病例值优先于命令行公共值。

# 查询特征名
ctbodycomp features --mode 3D

# 仅提取肌肉、皮下脂肪和骨的体积
ctbodycomp extract cases.csv raw.csv --mode 3D --config examples/morphometry_3d.json

# 对提取结果进行校正,原始列也保留在输出中
ctbodycomp normalize raw.csv corrected.csv --mode 3D --methods height weight bsa bone --tissues SM SF

# 2D单标签、部分特征
ctbodycomp extract cases.csv raw_2d.csv --mode 2D --label 7 --config examples/selected_2d.json
ctbodycomp normalize raw_2d.csv corrected_2d.csv --mode 2D --methods bone --tissues SM

# 自定义BC标签:按SM、IMF、VF、SF、Bone、PVAT、EAT、AorticPVAT顺序;0表示缺失
ctbodycomp extract cases.csv raw_mapped.csv --mode 3D --label-map 2 8 0 5 4 3 6 1

需要校正复合时,提取配置必须包含目标组织的firstorder_*_Mean。在同一校正步骤增加--include-composite即可:

# 示例配置包含肌肉、骨体积和肌肉Mean
ctbodycomp extract cases.csv raw.csv --mode 3D --config examples/normalized_composite_3d.json
ctbodycomp normalize raw.csv corrected.csv --mode 3D --methods height weight bsa bone --tissues SM --include-composite

# 查询校正复合特征及所需输入
ctbodycomp features --mode 3D --normalized-composite

校正CSV保留基础特征,按“校正指数、对应复合项”的顺序追加列,使用统一的normalization_status、normalization_error记录结果。缺少所需Mean列时记录失败;Mean数值为空时对应复合项为NaN。影像不存在也不影响已成功提取结果的后续计算。

如需对已有校正CSV补算复合项,也保留单独命令:ctbodycomp normalized-composite corrected.csv products.csv --mode 3D --methods height bone --tissues SM。

配置JSON直接写“类别→特征名称列表”。命令返回0表示成功,1表示输出中存在失败病例,参数错误返回2。输出目录需存在,输出不能覆盖输入文件;重复输出路径会替换先前结果。

完整使用示例

源码包中的 examples/reference_usage/README.md 提供五种完整提取示例:2D T12、2D无脊柱、3D T12、3D T1–T12、3D无脊柱,以及独立校正示例。每种情况同时提供可运行的Python脚本和Markdown教程,不依赖额外本地辅助脚本。

输入使用 cases.example.csv 和 cases_without_spine.example.csv 通用模板。模板不包含实际病例数据,使用前替换为自己的影像路径。示例计算结果写入示例目录下的 results/,不会纳入发布包。

测试和构建

在项目根目录运行:

python -m pip install -e '.[test]'
python -m pytest -q -p no:cacheprovider
python -m pip install --upgrade build twine
python -m build
python -m twine check dist/ctbodycomp-0.1.5-py3-none-any.whl dist/ctbodycomp-0.1.5.tar.gz

wheel包含库模块、许可证和安装元数据;源码包另外包含测试与使用教程。虚拟环境、缓存、本地影像、实际病例表和计算结果不纳入发布文件。PUBLISHING.md 仅保留在开发目录供维护者参考,不随发布包分发。

许可证

本版本采用 PolyForm Noncommercial License 1.0.0,完整原文随发布包附于 LICENSE。该许可证不包含两年后自动转换为MIT的条款;不在其许可范围内的商业用途需版权持有人另行授权。

本许可允许在许可范围内修改和分发代码,未设“禁止修改”限制。教育机构、公共研究机构、公共卫生或健康组织等的使用,被其机构条款明确列为允许用途,不因资金来源或相关资金义务改变;不能把本许可理解为所有医院收费场景都必须另行申请。实际允许范围以完整原文为准。

本项目为公开源码、限制用途的软件库,不声明为OSI认可的开源许可证。作者姓名及邮箱元数据仍未填写。

Metadata

Release files for ctbodycomp 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ctbodycomp 0.1.5
File Size Uploaded
ctbodycomp-0.1.5.tar.gz 53.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ctbodycomp 0.1.5
File Interpreter ABI Platform
ctbodycomp-0.1.5-py3-none-any.whl Python 3 none any Details

Total release size: 80.5 kB

Release files / ctbodycomp-0.1.5.tar.gz

Download URL ctbodycomp-0.1.5.tar.gz
Size 53.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2725abe9462bc51542c59f5c5b49d7a77b072d49ea55be514bed1ec18e805f0f
BLAKE2b-256 checksum
How to use checksums
1180f9763fc52648aeaa922afc5a5d86584c38c719b68b4ad0273db50522e3de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / ctbodycomp-0.1.5-py3-none-any.whl

Download URL ctbodycomp-0.1.5-py3-none-any.whl
Size 27.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
671ca0529db26afd29887fe5df11a5fbcc84e5b399a9894675a6a41fd09b20d2
BLAKE2b-256 checksum
How to use checksums
f54c2a5065dd42cd724caad971a1a1cd4f5426104c1b7287b9330cde078ff1b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release history Release notifications | RSS feed

This release

0.1.5 This release

2 release files

0.1.4

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page