mrxsslide 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.
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .superpowers/
8
+ .reference/
9
+ docs/
10
+ tests/sample.mrxs
11
+ tests/sample
12
+ benchmarks/results/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yifan Feng
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,328 @@
1
+ Metadata-Version: 2.4
2
+ Name: mrxsslide
3
+ Version: 0.1.0
4
+ Summary: Pure Python MRXS (3DHISTECH MIRAX) whole-slide image reader with OpenSlide-compatible API
5
+ Project-URL: Homepage, https://github.com/yifanfeng97/mrxsslide
6
+ Project-URL: Repository, https://github.com/yifanfeng97/mrxsslide
7
+ Project-URL: Issues, https://github.com/yifanfeng97/mrxsslide/issues
8
+ Author-email: Yifan Feng <evanfeng97@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: 3dhistech,digital-pathology,mirax,mrxs,openslide,pathology,whole-slide-image,wsi
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
21
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: pillow>=9.1.0
24
+ Provides-Extra: batch
25
+ Requires-Dist: numpy>=1.24; extra == 'batch'
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy; extra == 'dev'
28
+ Requires-Dist: openslide-bin>=4.0.1.2; extra == 'dev'
29
+ Requires-Dist: openslide-python; extra == 'dev'
30
+ Requires-Dist: pytest-cov; extra == 'dev'
31
+ Requires-Dist: pytest>=7.0; extra == 'dev'
32
+ Requires-Dist: ruff; extra == 'dev'
33
+ Provides-Extra: jxr
34
+ Requires-Dist: imagecodecs>=2023.1.23; extra == 'jxr'
35
+ Description-Content-Type: text/markdown
36
+
37
+ <h1 align="center">MRXSSlide</h1>
38
+
39
+ <p align="center">
40
+ <strong>纯 Python 实现的 MRXS(3DHISTECH MIRAX)数字病理切片读取库,提供与 OpenSlide 完全兼容的 API</strong>
41
+ </p>
42
+
43
+ <p align="center">
44
+ <a href="README_EN.md">English</a> |
45
+ <a href="README.md">简体中文</a>
46
+ </p>
47
+
48
+ <p align="center">
49
+ <a href="https://pypi.org/project/mrxsslide/">
50
+ <img src="https://img.shields.io/pypi/v/mrxsslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=3776ab" alt="PyPI Version">
51
+ </a>
52
+ <a href="https://python.org">
53
+ <img src="https://img.shields.io/badge/python-3.10%2B-3776ab?style=for-the-badge&logo=python&logoColor=white&labelColor=1a1a2e" alt="Python Version">
54
+ </a>
55
+ <a href="LICENSE">
56
+ <img src="https://img.shields.io/badge/license-MIT-06b6d4?style=for-the-badge&labelColor=1a1a2e" alt="License">
57
+ </a>
58
+ <a href="https://pypi.org/project/mrxsslide/">
59
+ <img src="https://img.shields.io/pypi/dm/mrxsslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=f97316" alt="Downloads">
60
+ </a>
61
+ <a href="https://github.com/yifanfeng97/mrxsslide/stargazers">
62
+ <img src="https://img.shields.io/github/stars/yifanfeng97/mrxsslide?style=for-the-badge&logo=github&labelColor=1a1a2e&color=facc15" alt="GitHub Stars">
63
+ </a>
64
+ </p>
65
+
66
+ <p align="center">
67
+ <a href="#-特性">✨ 特性</a> •
68
+ <a href="#-安装">📦 安装</a> •
69
+ <a href="#-快速开始">🚀 快速开始</a> •
70
+ <a href="#-api-参考">📖 API</a> •
71
+ <a href="#-性能">⚡ 性能</a>
72
+ </p>
73
+
74
+ ---
75
+
76
+ ## ✨ 特性
77
+
78
+ - 🐍 **纯 Python 实现** — 仅依赖 Pillow,零原生依赖,跨平台开箱即用
79
+ - 🔄 **OpenSlide 兼容 API** — 可直接替换 `openslide-python`,无需修改业务代码
80
+ - ⚡ **并行解码 + LRU 帧缓存** — 多线程解码 JPEG/PNG 帧,重复读取显著加速
81
+ - 📁 **支持直接传数据目录** — `.mrxs` 主文件缺失时可直接打开同名数据目录
82
+ - 🔺 **金字塔多层级读取** — 自动解析 MRXS 全部缩放层级(含由底层帧派生的高层级)
83
+ - 🖼️ **关联图像读取** — 支持 macro、label、thumbnail
84
+ - 📊 **完整元数据支持** — MPP、扫描倍率、背景色及全部 INI 键(`mirax.GROUP.KEY`)
85
+ - 🧪 **实验性 JPEG-XR 支持** — `pip install mrxsslide[jxr]`(基于 imagecodecs)
86
+
87
+ ---
88
+
89
+ ## 📦 安装
90
+
91
+ ### 使用 uv(推荐)
92
+
93
+ ```bash
94
+ uv pip install mrxsslide
95
+ ```
96
+
97
+ ### 使用 pip
98
+
99
+ ```bash
100
+ pip install mrxsslide
101
+ ```
102
+
103
+ 仅依赖 Pillow,任何平台都能直接安装。
104
+
105
+ ### JPEG-XR 支持(实验性)
106
+
107
+ MRXS 切片也可能使用 JPEG-XR 压缩,需安装可选依赖:
108
+
109
+ ```bash
110
+ pip install mrxsslide[jxr]
111
+ ```
112
+
113
+ ### 开发环境
114
+
115
+ ```bash
116
+ uv sync --extra dev --extra batch
117
+ ```
118
+
119
+ `dev` 含测试/ lint 依赖及 openslide 对比测试所需的 `openslide-python` +
120
+ `openslide-bin`(C 库);`batch` 含 `read_regions_batch` 与
121
+ `tests/test_batch.py` 所需的 numpy。缺 `batch` 时批量测试会在收集期
122
+ ImportError,缺 `openslide-bin` 时 17 个 openslide 对比测试会被静默 skip。
123
+
124
+ ---
125
+
126
+ ## 🚀 快速开始
127
+
128
+ ### 作为 OpenSlide 的 drop-in 替代品
129
+
130
+ ```python
131
+ import mrxsslide as openslide
132
+
133
+ slide = openslide.OpenSlide("path/to/sample.mrxs")
134
+
135
+ print(f"层级数: {slide.level_count}")
136
+ print(f"Level 0 尺寸: {slide.dimensions}")
137
+ for i in range(slide.level_count):
138
+ print(f" Level {i}: {slide.level_dimensions[i]} "
139
+ f"downsample={slide.level_downsamples[i]}")
140
+
141
+ # 读取区域(location 为 level 0 坐标,返回 RGBA)
142
+ img = slide.read_region((100000, 100000), 0, (512, 512))
143
+ img.save("region.png")
144
+
145
+ # 缩略图
146
+ thumb = slide.get_thumbnail((512, 512))
147
+ thumb.save("thumbnail.png")
148
+
149
+ # 关联图像
150
+ macro = slide.associated_images["macro"]
151
+ macro.save("macro.png")
152
+
153
+ # 属性读取
154
+ vendor = slide.properties[openslide.PROPERTY_NAME_VENDOR]
155
+ mpp_x = slide.properties[openslide.PROPERTY_NAME_MPP_X]
156
+
157
+ slide.close()
158
+ ```
159
+
160
+ ### 上下文管理器
161
+
162
+ ```python
163
+ with openslide.OpenSlide("sample.mrxs") as slide:
164
+ img = slide.read_region((0, 0), 0, (256, 256))
165
+ # 自动 close
166
+ ```
167
+
168
+ ### 批量读取
169
+
170
+ ML pipeline 按批量读 patch 时,`read_regions_batch` 把所有坐标命中的
171
+ 缺失帧合并为一次并行解码,并直接返回 numpy ndarray。适用场景:解码/磁盘
172
+ 开销占主导(冷页缓存、大帧、JPEG-XR)或下游需要 ndarray 形态;小帧 +
173
+ 热页缓存场景下不优于逐次 `read_region`。
174
+ 结果第 i 项与 `read_region(locs[i], level, size)` 逐像素一致。
175
+ 需要可选依赖 numpy:`pip install mrxsslide[batch]`。
176
+
177
+ ```python
178
+ locs = [(50000, 90000), (20000, 3000), (500, 60000)]
179
+ batch = slide.read_regions_batch(locs, 0, (512, 512))
180
+ print(batch.shape, batch.dtype) # (3, 512, 512, 4) uint8
181
+
182
+ # mode="RGB":透明区合成到 openslide.background-color 背景色上
183
+ rgb = slide.read_regions_batch(locs, 0, (512, 512), mode="RGB")
184
+ print(rgb.shape) # (3, 512, 512, 3)
185
+ ```
186
+
187
+ ### 直接打开数据目录
188
+
189
+ `.mrxs` 主文件丢失或仅拷贝了数据目录时,可直接传入目录路径:
190
+
191
+ ```python
192
+ slide = openslide.OpenSlide("path/to/sample") # 与 sample.mrxs 同名的数据目录
193
+ ```
194
+
195
+ ### 命令行示例
196
+
197
+ ```bash
198
+ python examples/read_region.py sample.mrxs 100000 100000 0 512 512
199
+ ```
200
+
201
+ ---
202
+
203
+ ## 📖 API 参考
204
+
205
+ ### `OpenSlide(filename, max_workers=0)`
206
+
207
+ 打开一个 MRXS 切片(`.mrxs` 文件或同名数据目录)。
208
+ `max_workers=0` 表示自动选择解码线程数(最多 16)。
209
+
210
+ ### 类方法
211
+
212
+ | 方法 | 说明 |
213
+ |------|------|
214
+ | `OpenSlide.detect_format(filename)` | 检测文件格式,返回 `"mirax"` 或 `None` |
215
+
216
+ ### 属性
217
+
218
+ | 属性 | 类型 | 说明 |
219
+ |------|------|------|
220
+ | `level_count` | `int` | 金字塔层级数 |
221
+ | `dimensions` | `(int, int)` | Level 0 尺寸(最高分辨率) |
222
+ | `level_dimensions` | `Tuple[(w, h), ...]` | 每层尺寸 |
223
+ | `level_downsamples` | `Tuple[float, ...]` | 每层下采样倍数 |
224
+ | `properties` | `Mapping[str, str]` | 元数据属性(只读映射) |
225
+ | `associated_images` | `Mapping[str, PIL.Image]` | 关联图像:macro、label、thumbnail |
226
+ | `color_profile` | `object \| None` | ICC 颜色配置文件(当前返回 `None`) |
227
+
228
+ ### 方法
229
+
230
+ | 方法 | 说明 |
231
+ |------|------|
232
+ | `read_region(location, level, size)` | 读取指定区域,返回 **RGBA** 图像 |
233
+ | `read_regions_batch(locations, level, size, mode="RGBA")` | 批量读取,返回 numpy `(N, h, w, C)` uint8(需 `mrxsslide[batch]`) |
234
+ | `get_best_level_for_downsample(downsample)` | 根据下采样倍数选择最佳层级 |
235
+ | `get_thumbnail(size)` | 生成缩略图(RGB,LANCZOS 重采样) |
236
+ | `set_cache(cache)` | API 兼容方法(当前为 no-op) |
237
+ | `close()` | 关闭并释放资源 |
238
+
239
+ ### 属性常量
240
+
241
+ ```python
242
+ from mrxsslide import (
243
+ PROPERTY_NAME_VENDOR, # "openslide.vendor"
244
+ PROPERTY_NAME_MPP_X, # "openslide.mpp-x"
245
+ PROPERTY_NAME_MPP_Y, # "openslide.mpp-y"
246
+ PROPERTY_NAME_OBJECTIVE_POWER, # "openslide.objective-power"
247
+ PROPERTY_NAME_BACKGROUND_COLOR, # "openslide.background-color"
248
+ PROPERTY_NAME_BOUNDS_X, # "openslide.bounds-x"
249
+ PROPERTY_NAME_BOUNDS_Y, # "openslide.bounds-y"
250
+ PROPERTY_NAME_BOUNDS_WIDTH, # "openslide.bounds-width"
251
+ PROPERTY_NAME_BOUNDS_HEIGHT, # "openslide.bounds-height"
252
+ PROPERTY_NAME_QUICKHASH1, # "openslide.quickhash-1"
253
+ )
254
+ ```
255
+
256
+ ### 异常
257
+
258
+ `OpenSlideError`、`OpenSlideUnsupportedFormatError`(与 openslide-python 同名),
259
+ 以及对应别名 `MrxsError`、`MrxsOpenError`、`MrxsUnsupportedFormatError`。
260
+
261
+ ---
262
+
263
+ ## ⚡ 性能
264
+
265
+ 与 OpenSlide(C 实现)读取**同一** MRXS 文件对比(测试脚本见
266
+ `benchmarks/compare_mrxs_openslide.py`):
267
+
268
+ | 场景 | mrxsslide | OpenSlide | 加速比 |
269
+ |------|-----------|-----------|--------|
270
+ | 随机读取 50 × 512×512(level 0) | 0.02 s | 0.10 s | **6.2×** |
271
+ | 同 50 区域第二遍(LRU 帧缓存热) | 0.02 s | 0.12 s | **6.6×** |
272
+ | 重复读取同一区域 ×50(LRU 缓存) | 0.01 s | 0.03 s | **2.2×** |
273
+ | 顺序扫描 400 × 256×256(level 4) | 1.5 s | 1.6 s | 1.2× |
274
+
275
+ > 测试文件:`1053891-15 pou2F3.mrxs`(83,379 × 185,672,9 层,JPEG 压缩)。
276
+ > 环境:Intel Xeon E5-2678 v3 / Python 3.11 / Pillow 12.3.0 / openslide-python 1.4.6(OpenSlide 4.0.1),测试时机器负载较高。
277
+ > 方法:每个场景 mrxsslide 与 OpenSlide 交替运行 3 次取中位数,共 4 轮取代表值,保证两者面对相同的页缓存热度;该口径下页缓存已被预热,不反映页缓存完全冷的首次读取。
278
+ > 说明:随机与重复读取场景 mrxsslide 明显更快(并行解码 + LRU 帧缓存);顺序扫描场景基本持平(4 轮加速比 0.95–1.21×)。不同样本、压缩格式与硬件会导致差异。
279
+
280
+ ---
281
+
282
+ ## 🏗️ 架构
283
+
284
+ MRXSSlide 完全基于纯 Python 实现,通过直接解析 MRXS 格式完成图像读取:
285
+
286
+ - **无需任何 C/C++ 扩展或系统动态库**
287
+ - **不依赖 OpenSlide、libjpeg、openjpeg 等外部库**
288
+ - `read_region` 三阶段流水线:收集命中 tile → 线程池并行解码帧 → 仿射对齐 + alpha 合成
289
+ - 每个数据文件共享只读 fd + `os.pread`,无线程锁
290
+ - **适合服务器、容器等不便安装原生依赖的场景**
291
+
292
+ ---
293
+
294
+ ## 📁 项目结构
295
+
296
+ ```
297
+ mrxsslide/
298
+ ├── src/mrxsslide/
299
+ │ ├── __init__.py # 包入口,导出 OpenSlide API
300
+ │ ├── _slide.py # OpenSlide 主类(三阶段 read_region)
301
+ │ ├── _mrxsformat.py # MRXS / Slidedat / 索引页解析
302
+ │ ├── _codecs.py # JPEG / PNG / JPEG-XR 解码
303
+ │ ├── _cache.py # LRU 帧缓存
304
+ │ └── _exceptions.py # OpenSlideError / 兼容异常
305
+ ├── tests/ # 测试(含 sample.mrxs 软链)
306
+ ├── examples/ # 示例脚本
307
+ ├── benchmarks/ # 与 OpenSlide 的对比基准
308
+ ├── scripts/ # 冒烟工具
309
+ ├── README.md
310
+ ├── LICENSE
311
+ └── pyproject.toml
312
+ ```
313
+
314
+ ---
315
+
316
+ ## ⚠️ 已知限制
317
+
318
+ 1. **只读**:目前不支持写入 MRXS 文件。
319
+ 2. **JPEG-XR 为实验性**:代码路径就绪但缺少真实样本验证。
320
+ 3. **首次冷读未覆盖**:基准为页缓存预热后的交替 median-of-3 口径,该口径下 mrxsslide 各场景均不慢于 OpenSlide;页缓存完全冷的首次读取对比暂无数据(详见[性能](#-性能))。
321
+
322
+ ---
323
+
324
+ ## 📄 License
325
+
326
+ [MIT](LICENSE)
327
+
328
+ Copyright (c) 2026 Yifan Feng
@@ -0,0 +1,292 @@
1
+ <h1 align="center">MRXSSlide</h1>
2
+
3
+ <p align="center">
4
+ <strong>纯 Python 实现的 MRXS(3DHISTECH MIRAX)数字病理切片读取库,提供与 OpenSlide 完全兼容的 API</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="README_EN.md">English</a> |
9
+ <a href="README.md">简体中文</a>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://pypi.org/project/mrxsslide/">
14
+ <img src="https://img.shields.io/pypi/v/mrxsslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=3776ab" alt="PyPI Version">
15
+ </a>
16
+ <a href="https://python.org">
17
+ <img src="https://img.shields.io/badge/python-3.10%2B-3776ab?style=for-the-badge&logo=python&logoColor=white&labelColor=1a1a2e" alt="Python Version">
18
+ </a>
19
+ <a href="LICENSE">
20
+ <img src="https://img.shields.io/badge/license-MIT-06b6d4?style=for-the-badge&labelColor=1a1a2e" alt="License">
21
+ </a>
22
+ <a href="https://pypi.org/project/mrxsslide/">
23
+ <img src="https://img.shields.io/pypi/dm/mrxsslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=f97316" alt="Downloads">
24
+ </a>
25
+ <a href="https://github.com/yifanfeng97/mrxsslide/stargazers">
26
+ <img src="https://img.shields.io/github/stars/yifanfeng97/mrxsslide?style=for-the-badge&logo=github&labelColor=1a1a2e&color=facc15" alt="GitHub Stars">
27
+ </a>
28
+ </p>
29
+
30
+ <p align="center">
31
+ <a href="#-特性">✨ 特性</a> •
32
+ <a href="#-安装">📦 安装</a> •
33
+ <a href="#-快速开始">🚀 快速开始</a> •
34
+ <a href="#-api-参考">📖 API</a> •
35
+ <a href="#-性能">⚡ 性能</a>
36
+ </p>
37
+
38
+ ---
39
+
40
+ ## ✨ 特性
41
+
42
+ - 🐍 **纯 Python 实现** — 仅依赖 Pillow,零原生依赖,跨平台开箱即用
43
+ - 🔄 **OpenSlide 兼容 API** — 可直接替换 `openslide-python`,无需修改业务代码
44
+ - ⚡ **并行解码 + LRU 帧缓存** — 多线程解码 JPEG/PNG 帧,重复读取显著加速
45
+ - 📁 **支持直接传数据目录** — `.mrxs` 主文件缺失时可直接打开同名数据目录
46
+ - 🔺 **金字塔多层级读取** — 自动解析 MRXS 全部缩放层级(含由底层帧派生的高层级)
47
+ - 🖼️ **关联图像读取** — 支持 macro、label、thumbnail
48
+ - 📊 **完整元数据支持** — MPP、扫描倍率、背景色及全部 INI 键(`mirax.GROUP.KEY`)
49
+ - 🧪 **实验性 JPEG-XR 支持** — `pip install mrxsslide[jxr]`(基于 imagecodecs)
50
+
51
+ ---
52
+
53
+ ## 📦 安装
54
+
55
+ ### 使用 uv(推荐)
56
+
57
+ ```bash
58
+ uv pip install mrxsslide
59
+ ```
60
+
61
+ ### 使用 pip
62
+
63
+ ```bash
64
+ pip install mrxsslide
65
+ ```
66
+
67
+ 仅依赖 Pillow,任何平台都能直接安装。
68
+
69
+ ### JPEG-XR 支持(实验性)
70
+
71
+ MRXS 切片也可能使用 JPEG-XR 压缩,需安装可选依赖:
72
+
73
+ ```bash
74
+ pip install mrxsslide[jxr]
75
+ ```
76
+
77
+ ### 开发环境
78
+
79
+ ```bash
80
+ uv sync --extra dev --extra batch
81
+ ```
82
+
83
+ `dev` 含测试/ lint 依赖及 openslide 对比测试所需的 `openslide-python` +
84
+ `openslide-bin`(C 库);`batch` 含 `read_regions_batch` 与
85
+ `tests/test_batch.py` 所需的 numpy。缺 `batch` 时批量测试会在收集期
86
+ ImportError,缺 `openslide-bin` 时 17 个 openslide 对比测试会被静默 skip。
87
+
88
+ ---
89
+
90
+ ## 🚀 快速开始
91
+
92
+ ### 作为 OpenSlide 的 drop-in 替代品
93
+
94
+ ```python
95
+ import mrxsslide as openslide
96
+
97
+ slide = openslide.OpenSlide("path/to/sample.mrxs")
98
+
99
+ print(f"层级数: {slide.level_count}")
100
+ print(f"Level 0 尺寸: {slide.dimensions}")
101
+ for i in range(slide.level_count):
102
+ print(f" Level {i}: {slide.level_dimensions[i]} "
103
+ f"downsample={slide.level_downsamples[i]}")
104
+
105
+ # 读取区域(location 为 level 0 坐标,返回 RGBA)
106
+ img = slide.read_region((100000, 100000), 0, (512, 512))
107
+ img.save("region.png")
108
+
109
+ # 缩略图
110
+ thumb = slide.get_thumbnail((512, 512))
111
+ thumb.save("thumbnail.png")
112
+
113
+ # 关联图像
114
+ macro = slide.associated_images["macro"]
115
+ macro.save("macro.png")
116
+
117
+ # 属性读取
118
+ vendor = slide.properties[openslide.PROPERTY_NAME_VENDOR]
119
+ mpp_x = slide.properties[openslide.PROPERTY_NAME_MPP_X]
120
+
121
+ slide.close()
122
+ ```
123
+
124
+ ### 上下文管理器
125
+
126
+ ```python
127
+ with openslide.OpenSlide("sample.mrxs") as slide:
128
+ img = slide.read_region((0, 0), 0, (256, 256))
129
+ # 自动 close
130
+ ```
131
+
132
+ ### 批量读取
133
+
134
+ ML pipeline 按批量读 patch 时,`read_regions_batch` 把所有坐标命中的
135
+ 缺失帧合并为一次并行解码,并直接返回 numpy ndarray。适用场景:解码/磁盘
136
+ 开销占主导(冷页缓存、大帧、JPEG-XR)或下游需要 ndarray 形态;小帧 +
137
+ 热页缓存场景下不优于逐次 `read_region`。
138
+ 结果第 i 项与 `read_region(locs[i], level, size)` 逐像素一致。
139
+ 需要可选依赖 numpy:`pip install mrxsslide[batch]`。
140
+
141
+ ```python
142
+ locs = [(50000, 90000), (20000, 3000), (500, 60000)]
143
+ batch = slide.read_regions_batch(locs, 0, (512, 512))
144
+ print(batch.shape, batch.dtype) # (3, 512, 512, 4) uint8
145
+
146
+ # mode="RGB":透明区合成到 openslide.background-color 背景色上
147
+ rgb = slide.read_regions_batch(locs, 0, (512, 512), mode="RGB")
148
+ print(rgb.shape) # (3, 512, 512, 3)
149
+ ```
150
+
151
+ ### 直接打开数据目录
152
+
153
+ `.mrxs` 主文件丢失或仅拷贝了数据目录时,可直接传入目录路径:
154
+
155
+ ```python
156
+ slide = openslide.OpenSlide("path/to/sample") # 与 sample.mrxs 同名的数据目录
157
+ ```
158
+
159
+ ### 命令行示例
160
+
161
+ ```bash
162
+ python examples/read_region.py sample.mrxs 100000 100000 0 512 512
163
+ ```
164
+
165
+ ---
166
+
167
+ ## 📖 API 参考
168
+
169
+ ### `OpenSlide(filename, max_workers=0)`
170
+
171
+ 打开一个 MRXS 切片(`.mrxs` 文件或同名数据目录)。
172
+ `max_workers=0` 表示自动选择解码线程数(最多 16)。
173
+
174
+ ### 类方法
175
+
176
+ | 方法 | 说明 |
177
+ |------|------|
178
+ | `OpenSlide.detect_format(filename)` | 检测文件格式,返回 `"mirax"` 或 `None` |
179
+
180
+ ### 属性
181
+
182
+ | 属性 | 类型 | 说明 |
183
+ |------|------|------|
184
+ | `level_count` | `int` | 金字塔层级数 |
185
+ | `dimensions` | `(int, int)` | Level 0 尺寸(最高分辨率) |
186
+ | `level_dimensions` | `Tuple[(w, h), ...]` | 每层尺寸 |
187
+ | `level_downsamples` | `Tuple[float, ...]` | 每层下采样倍数 |
188
+ | `properties` | `Mapping[str, str]` | 元数据属性(只读映射) |
189
+ | `associated_images` | `Mapping[str, PIL.Image]` | 关联图像:macro、label、thumbnail |
190
+ | `color_profile` | `object \| None` | ICC 颜色配置文件(当前返回 `None`) |
191
+
192
+ ### 方法
193
+
194
+ | 方法 | 说明 |
195
+ |------|------|
196
+ | `read_region(location, level, size)` | 读取指定区域,返回 **RGBA** 图像 |
197
+ | `read_regions_batch(locations, level, size, mode="RGBA")` | 批量读取,返回 numpy `(N, h, w, C)` uint8(需 `mrxsslide[batch]`) |
198
+ | `get_best_level_for_downsample(downsample)` | 根据下采样倍数选择最佳层级 |
199
+ | `get_thumbnail(size)` | 生成缩略图(RGB,LANCZOS 重采样) |
200
+ | `set_cache(cache)` | API 兼容方法(当前为 no-op) |
201
+ | `close()` | 关闭并释放资源 |
202
+
203
+ ### 属性常量
204
+
205
+ ```python
206
+ from mrxsslide import (
207
+ PROPERTY_NAME_VENDOR, # "openslide.vendor"
208
+ PROPERTY_NAME_MPP_X, # "openslide.mpp-x"
209
+ PROPERTY_NAME_MPP_Y, # "openslide.mpp-y"
210
+ PROPERTY_NAME_OBJECTIVE_POWER, # "openslide.objective-power"
211
+ PROPERTY_NAME_BACKGROUND_COLOR, # "openslide.background-color"
212
+ PROPERTY_NAME_BOUNDS_X, # "openslide.bounds-x"
213
+ PROPERTY_NAME_BOUNDS_Y, # "openslide.bounds-y"
214
+ PROPERTY_NAME_BOUNDS_WIDTH, # "openslide.bounds-width"
215
+ PROPERTY_NAME_BOUNDS_HEIGHT, # "openslide.bounds-height"
216
+ PROPERTY_NAME_QUICKHASH1, # "openslide.quickhash-1"
217
+ )
218
+ ```
219
+
220
+ ### 异常
221
+
222
+ `OpenSlideError`、`OpenSlideUnsupportedFormatError`(与 openslide-python 同名),
223
+ 以及对应别名 `MrxsError`、`MrxsOpenError`、`MrxsUnsupportedFormatError`。
224
+
225
+ ---
226
+
227
+ ## ⚡ 性能
228
+
229
+ 与 OpenSlide(C 实现)读取**同一** MRXS 文件对比(测试脚本见
230
+ `benchmarks/compare_mrxs_openslide.py`):
231
+
232
+ | 场景 | mrxsslide | OpenSlide | 加速比 |
233
+ |------|-----------|-----------|--------|
234
+ | 随机读取 50 × 512×512(level 0) | 0.02 s | 0.10 s | **6.2×** |
235
+ | 同 50 区域第二遍(LRU 帧缓存热) | 0.02 s | 0.12 s | **6.6×** |
236
+ | 重复读取同一区域 ×50(LRU 缓存) | 0.01 s | 0.03 s | **2.2×** |
237
+ | 顺序扫描 400 × 256×256(level 4) | 1.5 s | 1.6 s | 1.2× |
238
+
239
+ > 测试文件:`1053891-15 pou2F3.mrxs`(83,379 × 185,672,9 层,JPEG 压缩)。
240
+ > 环境:Intel Xeon E5-2678 v3 / Python 3.11 / Pillow 12.3.0 / openslide-python 1.4.6(OpenSlide 4.0.1),测试时机器负载较高。
241
+ > 方法:每个场景 mrxsslide 与 OpenSlide 交替运行 3 次取中位数,共 4 轮取代表值,保证两者面对相同的页缓存热度;该口径下页缓存已被预热,不反映页缓存完全冷的首次读取。
242
+ > 说明:随机与重复读取场景 mrxsslide 明显更快(并行解码 + LRU 帧缓存);顺序扫描场景基本持平(4 轮加速比 0.95–1.21×)。不同样本、压缩格式与硬件会导致差异。
243
+
244
+ ---
245
+
246
+ ## 🏗️ 架构
247
+
248
+ MRXSSlide 完全基于纯 Python 实现,通过直接解析 MRXS 格式完成图像读取:
249
+
250
+ - **无需任何 C/C++ 扩展或系统动态库**
251
+ - **不依赖 OpenSlide、libjpeg、openjpeg 等外部库**
252
+ - `read_region` 三阶段流水线:收集命中 tile → 线程池并行解码帧 → 仿射对齐 + alpha 合成
253
+ - 每个数据文件共享只读 fd + `os.pread`,无线程锁
254
+ - **适合服务器、容器等不便安装原生依赖的场景**
255
+
256
+ ---
257
+
258
+ ## 📁 项目结构
259
+
260
+ ```
261
+ mrxsslide/
262
+ ├── src/mrxsslide/
263
+ │ ├── __init__.py # 包入口,导出 OpenSlide API
264
+ │ ├── _slide.py # OpenSlide 主类(三阶段 read_region)
265
+ │ ├── _mrxsformat.py # MRXS / Slidedat / 索引页解析
266
+ │ ├── _codecs.py # JPEG / PNG / JPEG-XR 解码
267
+ │ ├── _cache.py # LRU 帧缓存
268
+ │ └── _exceptions.py # OpenSlideError / 兼容异常
269
+ ├── tests/ # 测试(含 sample.mrxs 软链)
270
+ ├── examples/ # 示例脚本
271
+ ├── benchmarks/ # 与 OpenSlide 的对比基准
272
+ ├── scripts/ # 冒烟工具
273
+ ├── README.md
274
+ ├── LICENSE
275
+ └── pyproject.toml
276
+ ```
277
+
278
+ ---
279
+
280
+ ## ⚠️ 已知限制
281
+
282
+ 1. **只读**:目前不支持写入 MRXS 文件。
283
+ 2. **JPEG-XR 为实验性**:代码路径就绪但缺少真实样本验证。
284
+ 3. **首次冷读未覆盖**:基准为页缓存预热后的交替 median-of-3 口径,该口径下 mrxsslide 各场景均不慢于 OpenSlide;页缓存完全冷的首次读取对比暂无数据(详见[性能](#-性能))。
285
+
286
+ ---
287
+
288
+ ## 📄 License
289
+
290
+ [MIT](LICENSE)
291
+
292
+ Copyright (c) 2026 Yifan Feng