musclemap-mlx 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- musclemap_mlx-0.1.0/.gitignore +38 -0
- musclemap_mlx-0.1.0/LICENSE +21 -0
- musclemap_mlx-0.1.0/PKG-INFO +155 -0
- musclemap_mlx-0.1.0/README.md +120 -0
- musclemap_mlx-0.1.0/THIRD_PARTY_NOTICES.md +35 -0
- musclemap_mlx-0.1.0/UPSTREAM_LICENSE +21 -0
- musclemap_mlx-0.1.0/pyproject.toml +116 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/__init__.py +11 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/assets.py +45 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/cli.py +147 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/config.py +343 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/geometry.py +662 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/labels.py +123 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/medmlx.py +213 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/network.py +291 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/pipeline.py +149 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/py.typed +0 -0
- musclemap_mlx-0.1.0/src/musclemap_mlx/weights.py +152 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# MedMLX standard
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.hypothesis/
|
|
12
|
+
.coverage
|
|
13
|
+
htmlcov/
|
|
14
|
+
.DS_Store
|
|
15
|
+
# Never commit secrets (Hugging Face tokens)
|
|
16
|
+
.env
|
|
17
|
+
# Never commit data or weights
|
|
18
|
+
/data/
|
|
19
|
+
/work/
|
|
20
|
+
/weights/
|
|
21
|
+
/models/
|
|
22
|
+
/checkpoints/
|
|
23
|
+
/outputs/
|
|
24
|
+
*.nii
|
|
25
|
+
*.nii.gz
|
|
26
|
+
*.dcm
|
|
27
|
+
*.nrrd
|
|
28
|
+
*.mha
|
|
29
|
+
*.mhd
|
|
30
|
+
*.safetensors
|
|
31
|
+
*.pt
|
|
32
|
+
*.pth
|
|
33
|
+
*.ckpt
|
|
34
|
+
*.bin
|
|
35
|
+
*.h5
|
|
36
|
+
*.npy
|
|
37
|
+
*.npz
|
|
38
|
+
!tests/fixtures/**/*.npz
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MuscleMap
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: musclemap-mlx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Segment 113 whole-body muscle and bone structures with MuscleMap v1.4 and native MLX
|
|
5
|
+
Project-URL: Homepage, https://medmlx.org
|
|
6
|
+
Project-URL: Repository, https://github.com/MedMLX/musclemap-mlx
|
|
7
|
+
Project-URL: Issues, https://github.com/MedMLX/musclemap-mlx/issues
|
|
8
|
+
Project-URL: Weights, https://huggingface.co/MedMLX/musclemap-mlx
|
|
9
|
+
Project-URL: Upstream, https://github.com/MuscleMap/MuscleMap
|
|
10
|
+
Project-URL: MedMLX, https://github.com/MedMLX/medmlx
|
|
11
|
+
Author: Joseph Sandoval, Alfredo Sandoval Jr.
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
License-File: THIRD_PARTY_NOTICES.md
|
|
15
|
+
License-File: UPSTREAM_LICENSE
|
|
16
|
+
Keywords: apple-silicon,ct,medical-imaging,metal,mlx,mri,musclemap,segmentation,whole-body
|
|
17
|
+
Classifier: Development Status :: 3 - Alpha
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: Intended Audience :: Science/Research
|
|
20
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: <3.14,>=3.13
|
|
29
|
+
Requires-Dist: medmlx-core<0.3,>=0.2.7
|
|
30
|
+
Requires-Dist: mlx==0.32.4; platform_system == 'Darwin' and platform_machine == 'arm64'
|
|
31
|
+
Requires-Dist: nibabel<6,>=5.3
|
|
32
|
+
Requires-Dist: numpy<3,>=2.0
|
|
33
|
+
Requires-Dist: scipy<2,>=1.14
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# musclemap-mlx
|
|
37
|
+
|
|
38
|
+
Segment 113 muscle and bone structures in MRI or CT NIfTI images with
|
|
39
|
+
MuscleMap v1.4, running natively through MLX and Metal on Apple Silicon.
|
|
40
|
+
Weights download automatically on first use.
|
|
41
|
+
|
|
42
|
+
## Install and segment
|
|
43
|
+
|
|
44
|
+
Requires an Apple Silicon Mac, Metal and Python 3.13. With
|
|
45
|
+
[uv](https://docs.astral.sh/uv/):
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
uv venv --python 3.13
|
|
49
|
+
source .venv/bin/activate
|
|
50
|
+
uv pip install musclemap-mlx
|
|
51
|
+
|
|
52
|
+
# Real MRI example from TotalSegmentator's official test data.
|
|
53
|
+
curl -L https://raw.githubusercontent.com/wasserth/TotalSegmentator/d89e47f73d24f5fba732a715aeb6fdc0ae34526b/tests/reference_files/example_mr_sm.nii.gz -o mri.nii.gz
|
|
54
|
+
musclemap-mlx segment --input mri.nii.gz --output-dir out
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The command writes `out/mask.nii.gz` and prints JSON containing the output path,
|
|
58
|
+
label table, device, memory use and chunking settings. To use the corresponding
|
|
59
|
+
CT example, replace `example_mr_sm.nii.gz` with `example_ct_sm.nii.gz`.
|
|
60
|
+
|
|
61
|
+
### Python
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from musclemap_mlx.medmlx import run
|
|
65
|
+
|
|
66
|
+
result = run(image_path="mri.nii.gz", output_dir="out-python")
|
|
67
|
+
print(result["outputs"]["mask"])
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### MedMLX
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
uv pip install 'medmlx[segment]>=0.9.1,<0.10'
|
|
74
|
+
medmlx describe musclemap
|
|
75
|
+
medmlx setup musclemap
|
|
76
|
+
medmlx run musclemap --arg image_path=mri.nii.gz output_dir=out-medmlx
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Or use its Python interface:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import medmlx
|
|
83
|
+
|
|
84
|
+
medmlx.setup_model("musclemap")
|
|
85
|
+
job = medmlx.run("musclemap", arguments={
|
|
86
|
+
"image_path": "mri.nii.gz",
|
|
87
|
+
"output_dir": "out-medmlx-python",
|
|
88
|
+
})
|
|
89
|
+
print(job["result"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The MCP server exposes the same model through `list_models`, `describe_model`,
|
|
93
|
+
`model_readiness`, `prepare_model` and `run_model`; poll `get_job` for setup and
|
|
94
|
+
segmentation results. Start it with `medmlx serve --root .`.
|
|
95
|
+
|
|
96
|
+
## Inputs and outputs
|
|
97
|
+
|
|
98
|
+
Input is one finite 3D `.nii` or `.nii.gz` MRI or CT volume. Millimetre spatial
|
|
99
|
+
units are supported; an unspecified unit is interpreted as millimetres, matching
|
|
100
|
+
upstream. Other declared spatial units and 4D images are rejected.
|
|
101
|
+
|
|
102
|
+
The output is an `int16` mask with background 0 and the original 113 anatomical
|
|
103
|
+
IDs, including 1101 for left levator scapulae and 8222 for right plantaris. Its
|
|
104
|
+
shape, affine, qform, sform and spatial-unit metadata follow the input. The
|
|
105
|
+
largest 6-connected component of each label is retained after chunk assembly.
|
|
106
|
+
The JSON result lists every anatomical ID and name.
|
|
107
|
+
|
|
108
|
+
| Option | Meaning | Default |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `--input` | 3D NIfTI image | Required |
|
|
111
|
+
| `--output-dir` | Destination for `mask.nii.gz` | Required |
|
|
112
|
+
| `--model-dir` | Local folder containing `model.safetensors` | Automatic download/cache |
|
|
113
|
+
| `--chunk-size` | Source storage-axis slices per independent pass | Eight |
|
|
114
|
+
| `--overlap` | Gaussian window overlap, integer 0–99 percent | 90 |
|
|
115
|
+
| `--overwrite` | Replace an existing output | Off |
|
|
116
|
+
|
|
117
|
+
Each chunk is independently normalized. For integer source images, chunking
|
|
118
|
+
also reproduces upstream's source-header NIfTI round trip before normalization.
|
|
119
|
+
Changing chunk size can change the mask. A chunk size at least the source depth
|
|
120
|
+
uses one whole-volume pass. Inference uses MLX on Metal; there is no CPU or
|
|
121
|
+
PyTorch network fallback.
|
|
122
|
+
|
|
123
|
+
## Weights and offline use
|
|
124
|
+
|
|
125
|
+
The approximately 105 MB float32 bundle is hosted at
|
|
126
|
+
[MedMLX/musclemap-mlx](https://huggingface.co/MedMLX/musclemap-mlx/tree/a798b3073e3b2c449987c13aa193626aaacc3092),
|
|
127
|
+
pinned to revision `a798b3073e3b2c449987c13aa193626aaacc3092`. It includes the
|
|
128
|
+
publisher configuration, original label metadata and MIT notices.
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
# Pre-download to the shared cache; subsequent runs can work offline.
|
|
132
|
+
musclemap-mlx download
|
|
133
|
+
|
|
134
|
+
# Export a local bundle, then supply it explicitly.
|
|
135
|
+
musclemap-mlx download --model-dir musclemap-weights
|
|
136
|
+
musclemap-mlx segment --input mri.nii.gz --model-dir musclemap-weights --output-dir out-local
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
MedMLX and this package share the `MEDMLX_ASSET_CACHE` cache setting. Python
|
|
140
|
+
callers supply a local bundle with `weights_path="musclemap-weights"`.
|
|
141
|
+
|
|
142
|
+
## Qualification and license
|
|
143
|
+
|
|
144
|
+
The native implementation was compared with the pinned MuscleMap v1.4 reference
|
|
145
|
+
on real CT and MRI examples. Reproduction commands, acceptance criteria and
|
|
146
|
+
measurements are in
|
|
147
|
+
[docs/technical.md](https://github.com/MedMLX/musclemap-mlx/blob/dev/docs/technical.md).
|
|
148
|
+
These two compact examples establish implementation parity; anatomical accuracy
|
|
149
|
+
against manual segmentations and coverage of all 113 structures were not measured.
|
|
150
|
+
|
|
151
|
+
Code and publisher weights are MIT licensed. See
|
|
152
|
+
[UPSTREAM_LICENSE](https://github.com/MedMLX/musclemap-mlx/blob/dev/UPSTREAM_LICENSE)
|
|
153
|
+
and [THIRD_PARTY_NOTICES.md](https://github.com/MedMLX/musclemap-mlx/blob/dev/THIRD_PARTY_NOTICES.md).
|
|
154
|
+
The original checkpoint and configuration are published in
|
|
155
|
+
[Zenodo record 21929873](https://zenodo.org/records/21929873).
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# musclemap-mlx
|
|
2
|
+
|
|
3
|
+
Segment 113 muscle and bone structures in MRI or CT NIfTI images with
|
|
4
|
+
MuscleMap v1.4, running natively through MLX and Metal on Apple Silicon.
|
|
5
|
+
Weights download automatically on first use.
|
|
6
|
+
|
|
7
|
+
## Install and segment
|
|
8
|
+
|
|
9
|
+
Requires an Apple Silicon Mac, Metal and Python 3.13. With
|
|
10
|
+
[uv](https://docs.astral.sh/uv/):
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
uv venv --python 3.13
|
|
14
|
+
source .venv/bin/activate
|
|
15
|
+
uv pip install musclemap-mlx
|
|
16
|
+
|
|
17
|
+
# Real MRI example from TotalSegmentator's official test data.
|
|
18
|
+
curl -L https://raw.githubusercontent.com/wasserth/TotalSegmentator/d89e47f73d24f5fba732a715aeb6fdc0ae34526b/tests/reference_files/example_mr_sm.nii.gz -o mri.nii.gz
|
|
19
|
+
musclemap-mlx segment --input mri.nii.gz --output-dir out
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The command writes `out/mask.nii.gz` and prints JSON containing the output path,
|
|
23
|
+
label table, device, memory use and chunking settings. To use the corresponding
|
|
24
|
+
CT example, replace `example_mr_sm.nii.gz` with `example_ct_sm.nii.gz`.
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from musclemap_mlx.medmlx import run
|
|
30
|
+
|
|
31
|
+
result = run(image_path="mri.nii.gz", output_dir="out-python")
|
|
32
|
+
print(result["outputs"]["mask"])
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### MedMLX
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
uv pip install 'medmlx[segment]>=0.9.1,<0.10'
|
|
39
|
+
medmlx describe musclemap
|
|
40
|
+
medmlx setup musclemap
|
|
41
|
+
medmlx run musclemap --arg image_path=mri.nii.gz output_dir=out-medmlx
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or use its Python interface:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import medmlx
|
|
48
|
+
|
|
49
|
+
medmlx.setup_model("musclemap")
|
|
50
|
+
job = medmlx.run("musclemap", arguments={
|
|
51
|
+
"image_path": "mri.nii.gz",
|
|
52
|
+
"output_dir": "out-medmlx-python",
|
|
53
|
+
})
|
|
54
|
+
print(job["result"])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The MCP server exposes the same model through `list_models`, `describe_model`,
|
|
58
|
+
`model_readiness`, `prepare_model` and `run_model`; poll `get_job` for setup and
|
|
59
|
+
segmentation results. Start it with `medmlx serve --root .`.
|
|
60
|
+
|
|
61
|
+
## Inputs and outputs
|
|
62
|
+
|
|
63
|
+
Input is one finite 3D `.nii` or `.nii.gz` MRI or CT volume. Millimetre spatial
|
|
64
|
+
units are supported; an unspecified unit is interpreted as millimetres, matching
|
|
65
|
+
upstream. Other declared spatial units and 4D images are rejected.
|
|
66
|
+
|
|
67
|
+
The output is an `int16` mask with background 0 and the original 113 anatomical
|
|
68
|
+
IDs, including 1101 for left levator scapulae and 8222 for right plantaris. Its
|
|
69
|
+
shape, affine, qform, sform and spatial-unit metadata follow the input. The
|
|
70
|
+
largest 6-connected component of each label is retained after chunk assembly.
|
|
71
|
+
The JSON result lists every anatomical ID and name.
|
|
72
|
+
|
|
73
|
+
| Option | Meaning | Default |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| `--input` | 3D NIfTI image | Required |
|
|
76
|
+
| `--output-dir` | Destination for `mask.nii.gz` | Required |
|
|
77
|
+
| `--model-dir` | Local folder containing `model.safetensors` | Automatic download/cache |
|
|
78
|
+
| `--chunk-size` | Source storage-axis slices per independent pass | Eight |
|
|
79
|
+
| `--overlap` | Gaussian window overlap, integer 0–99 percent | 90 |
|
|
80
|
+
| `--overwrite` | Replace an existing output | Off |
|
|
81
|
+
|
|
82
|
+
Each chunk is independently normalized. For integer source images, chunking
|
|
83
|
+
also reproduces upstream's source-header NIfTI round trip before normalization.
|
|
84
|
+
Changing chunk size can change the mask. A chunk size at least the source depth
|
|
85
|
+
uses one whole-volume pass. Inference uses MLX on Metal; there is no CPU or
|
|
86
|
+
PyTorch network fallback.
|
|
87
|
+
|
|
88
|
+
## Weights and offline use
|
|
89
|
+
|
|
90
|
+
The approximately 105 MB float32 bundle is hosted at
|
|
91
|
+
[MedMLX/musclemap-mlx](https://huggingface.co/MedMLX/musclemap-mlx/tree/a798b3073e3b2c449987c13aa193626aaacc3092),
|
|
92
|
+
pinned to revision `a798b3073e3b2c449987c13aa193626aaacc3092`. It includes the
|
|
93
|
+
publisher configuration, original label metadata and MIT notices.
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
# Pre-download to the shared cache; subsequent runs can work offline.
|
|
97
|
+
musclemap-mlx download
|
|
98
|
+
|
|
99
|
+
# Export a local bundle, then supply it explicitly.
|
|
100
|
+
musclemap-mlx download --model-dir musclemap-weights
|
|
101
|
+
musclemap-mlx segment --input mri.nii.gz --model-dir musclemap-weights --output-dir out-local
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
MedMLX and this package share the `MEDMLX_ASSET_CACHE` cache setting. Python
|
|
105
|
+
callers supply a local bundle with `weights_path="musclemap-weights"`.
|
|
106
|
+
|
|
107
|
+
## Qualification and license
|
|
108
|
+
|
|
109
|
+
The native implementation was compared with the pinned MuscleMap v1.4 reference
|
|
110
|
+
on real CT and MRI examples. Reproduction commands, acceptance criteria and
|
|
111
|
+
measurements are in
|
|
112
|
+
[docs/technical.md](https://github.com/MedMLX/musclemap-mlx/blob/dev/docs/technical.md).
|
|
113
|
+
These two compact examples establish implementation parity; anatomical accuracy
|
|
114
|
+
against manual segmentations and coverage of all 113 structures were not measured.
|
|
115
|
+
|
|
116
|
+
Code and publisher weights are MIT licensed. See
|
|
117
|
+
[UPSTREAM_LICENSE](https://github.com/MedMLX/musclemap-mlx/blob/dev/UPSTREAM_LICENSE)
|
|
118
|
+
and [THIRD_PARTY_NOTICES.md](https://github.com/MedMLX/musclemap-mlx/blob/dev/THIRD_PARTY_NOTICES.md).
|
|
119
|
+
The original checkpoint and configuration are published in
|
|
120
|
+
[Zenodo record 21929873](https://zenodo.org/records/21929873).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
## MuscleMap
|
|
4
|
+
|
|
5
|
+
- Source: https://github.com/MuscleMap/MuscleMap
|
|
6
|
+
- Revision: `35a2138a4a3a63be713bae5584632ffc6433b960`
|
|
7
|
+
- Copyright: Copyright (c) 2023 MuscleMap
|
|
8
|
+
- License: MIT; original text retained in [UPSTREAM_LICENSE](UPSTREAM_LICENSE).
|
|
9
|
+
The package [LICENSE](LICENSE) retains the same MIT terms.
|
|
10
|
+
- Adapted: native MLX implementation of the published 2D MONAI UNet and its
|
|
11
|
+
inference preprocessing/postprocessing. The original anatomical label IDs,
|
|
12
|
+
configuration semantics and largest-connected-component helper are preserved.
|
|
13
|
+
Upstream source remains unchanged and read-only in the development-only
|
|
14
|
+
`reference/` submodule.
|
|
15
|
+
|
|
16
|
+
## Dependencies, weights and data
|
|
17
|
+
|
|
18
|
+
Dependencies declared in [pyproject.toml](pyproject.toml) are installed
|
|
19
|
+
separately and keep their own licenses. MLX is the runtime backend. MONAI 1.3.2
|
|
20
|
+
and PyTorch 2.4.1 are used only in the development reference environment; the
|
|
21
|
+
converter uses PyTorch's restricted `weights_only=True` deserializer and the
|
|
22
|
+
runtime never imports Torch.
|
|
23
|
+
|
|
24
|
+
Zenodo record [21929873](https://zenodo.org/records/21929873) supplies the
|
|
25
|
+
contrast-agnostic wholebody v1.4 checkpoint, original JSON configuration and
|
|
26
|
+
publisher `LICENSE` under MIT. The license grant matches
|
|
27
|
+
[UPSTREAM_LICENSE](UPSTREAM_LICENSE). Converted weights retain all original
|
|
28
|
+
float32 parameters and are hosted at
|
|
29
|
+
[MedMLX/musclemap-mlx](https://huggingface.co/MedMLX/musclemap-mlx/tree/a798b3073e3b2c449987c13aa193626aaacc3092),
|
|
30
|
+
with configuration, labels, license and attribution. No weights or medical images
|
|
31
|
+
are included in package distributions.
|
|
32
|
+
|
|
33
|
+
## Showcase image
|
|
34
|
+
|
|
35
|
+
No showcase image or patient data is included.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MuscleMap
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "musclemap-mlx"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Segment 113 whole-body muscle and bone structures with MuscleMap v1.4 and native MLX"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13,<3.14"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE", "UPSTREAM_LICENSE", "THIRD_PARTY_NOTICES.md"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Joseph Sandoval" },
|
|
15
|
+
{ name = "Alfredo Sandoval Jr." },
|
|
16
|
+
]
|
|
17
|
+
keywords = [
|
|
18
|
+
"mlx",
|
|
19
|
+
"apple-silicon",
|
|
20
|
+
"metal",
|
|
21
|
+
"medical-imaging",
|
|
22
|
+
"musclemap",
|
|
23
|
+
"whole-body",
|
|
24
|
+
"segmentation",
|
|
25
|
+
"mri",
|
|
26
|
+
"ct",
|
|
27
|
+
]
|
|
28
|
+
classifiers = [
|
|
29
|
+
"Development Status :: 3 - Alpha",
|
|
30
|
+
"Intended Audience :: Developers",
|
|
31
|
+
"Intended Audience :: Science/Research",
|
|
32
|
+
"Operating System :: MacOS :: MacOS X",
|
|
33
|
+
"Programming Language :: Python :: 3",
|
|
34
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
35
|
+
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
37
|
+
"Topic :: Scientific/Engineering :: Image Processing",
|
|
38
|
+
"Topic :: Scientific/Engineering :: Medical Science Apps.",
|
|
39
|
+
"Typing :: Typed",
|
|
40
|
+
]
|
|
41
|
+
dependencies = [
|
|
42
|
+
"medmlx-core>=0.2.7,<0.3",
|
|
43
|
+
"mlx==0.32.4; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
|
44
|
+
"nibabel>=5.3,<6",
|
|
45
|
+
"numpy>=2.0,<3",
|
|
46
|
+
"scipy>=1.14,<2",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Homepage = "https://medmlx.org"
|
|
51
|
+
Repository = "https://github.com/MedMLX/musclemap-mlx"
|
|
52
|
+
Issues = "https://github.com/MedMLX/musclemap-mlx/issues"
|
|
53
|
+
Weights = "https://huggingface.co/MedMLX/musclemap-mlx"
|
|
54
|
+
Upstream = "https://github.com/MuscleMap/MuscleMap"
|
|
55
|
+
MedMLX = "https://github.com/MedMLX/medmlx"
|
|
56
|
+
[project.scripts]
|
|
57
|
+
"musclemap-mlx" = "musclemap_mlx.cli:main"
|
|
58
|
+
|
|
59
|
+
[project.entry-points."medmlx.models"]
|
|
60
|
+
musclemap = "musclemap_mlx.medmlx:MODEL"
|
|
61
|
+
[dependency-groups]
|
|
62
|
+
dev = [
|
|
63
|
+
"monai==1.6.1",
|
|
64
|
+
"pyright==1.1.414",
|
|
65
|
+
"pytest>=8.4,<10",
|
|
66
|
+
"ruff>=0.13,<1",
|
|
67
|
+
"safetensors>=0.4,<1",
|
|
68
|
+
"scipy-stubs>=1.18.1,<1.19",
|
|
69
|
+
"torch==2.14.1",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[tool.uv]
|
|
73
|
+
environments = ["sys_platform == 'darwin' and platform_machine == 'arm64'"]
|
|
74
|
+
required-environments = ["sys_platform == 'darwin' and platform_machine == 'arm64'"]
|
|
75
|
+
constraint-dependencies = ["setuptools>=84.1.0", "filelock>=4.2.1"]
|
|
76
|
+
[tool.hatch.build.targets.wheel]
|
|
77
|
+
packages = ["src/musclemap_mlx"]
|
|
78
|
+
[tool.hatch.build.targets.sdist]
|
|
79
|
+
only-include = [
|
|
80
|
+
"src/musclemap_mlx",
|
|
81
|
+
"pyproject.toml",
|
|
82
|
+
"README.md",
|
|
83
|
+
"LICENSE",
|
|
84
|
+
"THIRD_PARTY_NOTICES.md",
|
|
85
|
+
"UPSTREAM_LICENSE",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.ruff]
|
|
89
|
+
line-length = 100
|
|
90
|
+
target-version = "py313"
|
|
91
|
+
extend-exclude = ["reference"]
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
94
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "TID251", "TID252", "ANN401"]
|
|
95
|
+
|
|
96
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
97
|
+
ban-relative-imports = "all"
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
|
100
|
+
"typing.Any" = { msg = "Use a precise type and parse untyped values at the boundary." }
|
|
101
|
+
"typing_extensions.Any" = { msg = "Use a precise type and parse untyped values at the boundary." }
|
|
102
|
+
[tool.pyright]
|
|
103
|
+
include = ["src", "tests"]
|
|
104
|
+
extraPaths = ["src"]
|
|
105
|
+
pythonVersion = "3.13"
|
|
106
|
+
typeCheckingMode = "strict"
|
|
107
|
+
enableTypeIgnoreComments = false
|
|
108
|
+
reportUnnecessaryTypeIgnoreComment = "error"
|
|
109
|
+
|
|
110
|
+
[tool.pytest.ini_options]
|
|
111
|
+
testpaths = ["tests"]
|
|
112
|
+
addopts = "--strict-config --strict-markers"
|
|
113
|
+
markers = [
|
|
114
|
+
"metal: needs the MLX Metal GPU and the shared Perfkit GPU lock",
|
|
115
|
+
"oracle: compares against a recorded MONAI 1.3.2 reference; opt-in",
|
|
116
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""MuscleMap v1.4 whole-body segmentation with native MLX on Apple Silicon."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import version
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def __getattr__(name: str) -> str:
|
|
9
|
+
if name == "__version__":
|
|
10
|
+
return version("musclemap-mlx")
|
|
11
|
+
raise AttributeError(name)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Check publisher asset completeness without deserializing checkpoint data."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import cast
|
|
8
|
+
|
|
9
|
+
from musclemap_mlx.config import CONFIG_NAME, MODEL_ID
|
|
10
|
+
|
|
11
|
+
ASSETS = {
|
|
12
|
+
"contrast_agnostic_wholebody_model.pth": 104884148,
|
|
13
|
+
"contrast_agnostic_wholebody_model.json": 10555,
|
|
14
|
+
"LICENSE": 1087,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def verify_assets(folder: Path) -> dict[str, str]:
|
|
19
|
+
"""Check publisher file sizes and return each asset's absolute path.
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
ValueError: An asset is missing or incomplete for Zenodo record 21929873.
|
|
23
|
+
"""
|
|
24
|
+
paths: dict[str, str] = {}
|
|
25
|
+
for name, size in ASSETS.items():
|
|
26
|
+
path = folder / name
|
|
27
|
+
if not path.is_file():
|
|
28
|
+
raise ValueError(f"Missing asset: {path}; obtain it from Zenodo record 21929873")
|
|
29
|
+
if path.stat().st_size != size:
|
|
30
|
+
raise ValueError(f"Unexpected size for {name}; expected {size} bytes")
|
|
31
|
+
paths[name] = path.absolute().as_posix()
|
|
32
|
+
return paths
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def inspect_assets(folder: Path) -> dict[str, object]:
|
|
36
|
+
"""Check asset completeness and return the original configuration as JSON data."""
|
|
37
|
+
verify_assets(folder)
|
|
38
|
+
config = cast(object, json.loads((folder / CONFIG_NAME).read_text(encoding="utf-8")))
|
|
39
|
+
return {
|
|
40
|
+
"status": "assets-complete",
|
|
41
|
+
"model_id": MODEL_ID,
|
|
42
|
+
"record": "https://zenodo.org/records/21929873",
|
|
43
|
+
"configuration": config,
|
|
44
|
+
"checkpoint_deserialized": False,
|
|
45
|
+
}
|