tmapslide 0.2.0__tar.gz → 0.2.1__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,188 @@
1
+ Metadata-Version: 2.5
2
+ Name: tmapslide
3
+ Version: 0.2.1
4
+ Summary: Pure Python UNIC TMAP whole-slide image reader with OpenSlide-compatible API
5
+ Project-URL: Homepage, https://github.com/yifanfeng97/tmapslide
6
+ Project-URL: Documentation, https://github.com/yifanfeng97/tmapslide#readme
7
+ Project-URL: Repository, https://github.com/yifanfeng97/tmapslide
8
+ Project-URL: Issues, https://github.com/yifanfeng97/tmapslide/issues
9
+ Author-email: Yifan Feng <evanfeng97@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: digital-pathology,openslide,pathology,tmap,tmapslide,unic,whole-slide-image,wsi
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
22
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: pillow>=9.0.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: mypy; extra == 'dev'
27
+ Requires-Dist: pytest-cov; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0; extra == 'dev'
29
+ Requires-Dist: ruff; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ <div align="center">
33
+
34
+ <img src="docs/hero.jpg" alt="TmapSlide — UNIC TMAP whole-slide images in pure Python" width="800" style="max-width: 100%;">
35
+
36
+ <br/>
37
+
38
+ **TmapSlide**
39
+
40
+ **Pure Python reader for UNIC TMAP whole-slide images — no SDK, no native deps.**
41
+
42
+ *以纯 Python 读取联影 TMAP 全切片图像,开箱即用*
43
+
44
+ <p align="center">
45
+ <a href="https://pypi.org/project/tmapslide/">
46
+ <img src="https://img.shields.io/pypi/v/tmapslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=3776ab" alt="PyPI Version">
47
+ </a>
48
+ <a href="https://pypi.org/project/tmapslide/">
49
+ <img src="https://img.shields.io/pypi/dm/tmapslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=3776ab" alt="PyPI Downloads">
50
+ </a>
51
+ <a href="https://python.org">
52
+ <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">
53
+ </a>
54
+ <a href="LICENSE">
55
+ <img src="https://img.shields.io/badge/license-MIT-06b6d4?style=for-the-badge&logo=openaccess&logoColor=white&labelColor=1a1a2e" alt="License">
56
+ </a>
57
+ <a href="https://github.com/yifanfeng97/tmapslide/actions/workflows/test.yml">
58
+ <img src="https://img.shields.io/github/actions/yifanfeng97/tmapslide/test.yml?branch=main&style=for-the-badge&logo=githubactions&logoColor=white&labelColor=1a1a2e&label=tests" alt="Tests">
59
+ </a>
60
+ <a href="https://github.com/yifanfeng97/tmapslide/stargazers">
61
+ <img src="https://img.shields.io/github/stars/yifanfeng97/tmapslide?style=for-the-badge&logo=github&labelColor=1a1a2e&color=facc15" alt="GitHub Stars">
62
+ </a>
63
+ </p>
64
+
65
+ [📖 English](#-quick-start) · [中文说明](#-中文说明)
66
+
67
+ </div>
68
+
69
+ ## ⚡ Quick Start
70
+
71
+ **1. Install:**
72
+
73
+ ```bash
74
+ pip install tmapslide
75
+ ```
76
+
77
+ **2. Read a slide:**
78
+
79
+ ```python
80
+ import tmapslide
81
+
82
+ slide = tmapslide.OpenSlide("sample.TMAP")
83
+
84
+ print(slide.dimensions) # (71424, 72704)
85
+ print(slide.level_count) # 10
86
+ print(slide.level_downsamples) # (1.0, 2.0, 4.0, ...)
87
+
88
+ region = slide.read_region((512, 512), 0, (1024, 1024)) # RGBA PIL image
89
+ thumb = slide.get_thumbnail((512, 512))
90
+ macro = slide.associated_images["macro"]
91
+ ```
92
+
93
+ ## ✨ Features
94
+
95
+ - **Pure Python** — no vendor SDK, no native dependencies; only Pillow
96
+ - **OpenSlide-compatible API** — drop-in for code written against
97
+ `openslide` / `kfbslide`: `read_region`, `get_thumbnail`, `dimensions`,
98
+ `level_count`, `level_dimensions`, `level_downsamples`, `properties`,
99
+ `associated_images`
100
+ - **Both known TMAP variants** — `TMAP06` (3-level pyramid) and
101
+ `TMAP07` (up to 10 levels)
102
+ - **Fork-safe file handles** — safe with PyTorch `DataLoader` workers
103
+ - **LRU decoded-tile cache** — fast repeated reads
104
+ - **Thread-safe reads** — concurrent `read_region` from worker threads
105
+
106
+ ## 🏎️ Performance
107
+
108
+ Benchmark vs [ASlide](https://github.com/MrPeterJin/ASlide)'s pure-Python
109
+ TMAP backend (median of 5 runs, same files, same machine):
110
+
111
+ | Scenario | TMAP07 | TMAP06 |
112
+ |---|---|---|
113
+ | Open slide | **281 ms** vs 43 ms ⚠️ | **101 ms** vs 224 ms (2.2×) |
114
+ | Cold 1024² region @L0 | **4.1 ms** vs 15.7 ms (3.8×) | **2.6 ms** vs 14.8 ms (5.6×) |
115
+ | Random 512² region @L0 | **1.1 ms** vs 5.7 ms (5.3×) | **0.9 ms** vs 6.3 ms (6.7×) |
116
+ | Warm 512² region ×50 | **42 ms** vs 217 ms (5.1×) | **54 ms** vs 408 ms (7.6×) |
117
+
118
+ > ASlide's TMAP backend re-decodes every tile on every call; tmapslide adds
119
+ > an LRU decoded-tile cache and per-tile culling, so warm reads and random
120
+ > access are several times faster.
121
+
122
+ ## 📖 API
123
+
124
+ ### `tmapslide.OpenSlide(filename)`
125
+
126
+ | Member | Description |
127
+ | --- | --- |
128
+ | `dimensions` | `(width, height)` at level 0 |
129
+ | `level_count` | number of pyramid levels |
130
+ | `level_dimensions` | `(w, h)` per level |
131
+ | `level_downsamples` | downsample factor per level |
132
+ | `properties` | read-only metadata mapping (`openslide.vendor=unic`, `tmap.*`) |
133
+ | `associated_images` | lazy mapping, typically `macro` / `label` / `thumbnail` |
134
+ | `read_region(loc, level, size)` | `PIL.Image` (RGBA) of the region |
135
+ | `get_thumbnail(size)` | thumbnail from the lowest resolution level |
136
+ | `get_best_level_for_downsample(ds)` | best level for a downsample factor |
137
+ | `close()` / context manager | release resources |
138
+
139
+ ### `tmapslide.open_slide(filename)`
140
+
141
+ Alias of `OpenSlide(filename)`.
142
+
143
+ ## 📦 Supported Formats
144
+
145
+ | Format | Extension | Vendor | Backend |
146
+ | --- | --- | --- | --- |
147
+ | TMAP 06 | `.TMAP` | UNIC (United Imaging) | Pure Python |
148
+ | TMAP 07 | `.TMAP` | UNIC (United Imaging) | Pure Python |
149
+
150
+ ## 🧪 Testing
151
+
152
+ Tests run against real TMAP samples when the `scce_external_center` data
153
+ directory (or `TAPSLIDE_TEST_DATA`) is present next to the repo; synthetic
154
+ fixtures keep the core parser covered everywhere else.
155
+
156
+ ```bash
157
+ pip install -e .[dev]
158
+ pytest
159
+ ```
160
+
161
+ ## 📄 Format Notes (reverse-engineered)
162
+
163
+ TMAP is an undocumented proprietary format. This reader is built from
164
+ binary analysis of real scanner output, cross-validated against
165
+ [ASlide](https://github.com/MrPeterJin/ASlide). Both variants store plain
166
+ JPEG tiles with a small binary header and index tables; there is no
167
+ encryption.
168
+
169
+ - TMAP06 stores 3 pyramid levels (40x / 10x / 2.5x); TMAP07 stores up to
170
+ 10 levels (40x down to 0.078x, halving each level).
171
+ - TMAP06 level-2 previews come from pre-rendered `ShrinkTile` entries and
172
+ are JPEG-compressed at that scale.
173
+ - `iter_tiles()` exposes the stored tile grid directly — useful for
174
+ tile-based ML pipelines.
175
+
176
+ ## 📄 License
177
+
178
+ [MIT](LICENSE)
179
+
180
+ ## 🙏 Acknowledgments
181
+
182
+ - [kfbslide](https://github.com/yifanfeng97/kfbslide) — the KFB reader this
183
+ project is modelled after
184
+ - [OpenSlide](https://openslide.org/) — the API this library mimics
185
+ - [ASlide](https://github.com/MrPeterJin/ASlide) — its independent
186
+ reverse-engineering of the TMAP06 layer/block structures (from decompiled
187
+ vendor SDK) was used to cross-validate this implementation. tmapslide is
188
+ an independent MIT-licensed implementation and ships no ASlide code
@@ -0,0 +1,157 @@
1
+ <div align="center">
2
+
3
+ <img src="docs/hero.jpg" alt="TmapSlide — UNIC TMAP whole-slide images in pure Python" width="800" style="max-width: 100%;">
4
+
5
+ <br/>
6
+
7
+ **TmapSlide**
8
+
9
+ **Pure Python reader for UNIC TMAP whole-slide images — no SDK, no native deps.**
10
+
11
+ *以纯 Python 读取联影 TMAP 全切片图像,开箱即用*
12
+
13
+ <p align="center">
14
+ <a href="https://pypi.org/project/tmapslide/">
15
+ <img src="https://img.shields.io/pypi/v/tmapslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=3776ab" alt="PyPI Version">
16
+ </a>
17
+ <a href="https://pypi.org/project/tmapslide/">
18
+ <img src="https://img.shields.io/pypi/dm/tmapslide?style=for-the-badge&logo=pypi&logoColor=white&labelColor=1a1a2e&color=3776ab" alt="PyPI Downloads">
19
+ </a>
20
+ <a href="https://python.org">
21
+ <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">
22
+ </a>
23
+ <a href="LICENSE">
24
+ <img src="https://img.shields.io/badge/license-MIT-06b6d4?style=for-the-badge&logo=openaccess&logoColor=white&labelColor=1a1a2e" alt="License">
25
+ </a>
26
+ <a href="https://github.com/yifanfeng97/tmapslide/actions/workflows/test.yml">
27
+ <img src="https://img.shields.io/github/actions/yifanfeng97/tmapslide/test.yml?branch=main&style=for-the-badge&logo=githubactions&logoColor=white&labelColor=1a1a2e&label=tests" alt="Tests">
28
+ </a>
29
+ <a href="https://github.com/yifanfeng97/tmapslide/stargazers">
30
+ <img src="https://img.shields.io/github/stars/yifanfeng97/tmapslide?style=for-the-badge&logo=github&labelColor=1a1a2e&color=facc15" alt="GitHub Stars">
31
+ </a>
32
+ </p>
33
+
34
+ [📖 English](#-quick-start) · [中文说明](#-中文说明)
35
+
36
+ </div>
37
+
38
+ ## ⚡ Quick Start
39
+
40
+ **1. Install:**
41
+
42
+ ```bash
43
+ pip install tmapslide
44
+ ```
45
+
46
+ **2. Read a slide:**
47
+
48
+ ```python
49
+ import tmapslide
50
+
51
+ slide = tmapslide.OpenSlide("sample.TMAP")
52
+
53
+ print(slide.dimensions) # (71424, 72704)
54
+ print(slide.level_count) # 10
55
+ print(slide.level_downsamples) # (1.0, 2.0, 4.0, ...)
56
+
57
+ region = slide.read_region((512, 512), 0, (1024, 1024)) # RGBA PIL image
58
+ thumb = slide.get_thumbnail((512, 512))
59
+ macro = slide.associated_images["macro"]
60
+ ```
61
+
62
+ ## ✨ Features
63
+
64
+ - **Pure Python** — no vendor SDK, no native dependencies; only Pillow
65
+ - **OpenSlide-compatible API** — drop-in for code written against
66
+ `openslide` / `kfbslide`: `read_region`, `get_thumbnail`, `dimensions`,
67
+ `level_count`, `level_dimensions`, `level_downsamples`, `properties`,
68
+ `associated_images`
69
+ - **Both known TMAP variants** — `TMAP06` (3-level pyramid) and
70
+ `TMAP07` (up to 10 levels)
71
+ - **Fork-safe file handles** — safe with PyTorch `DataLoader` workers
72
+ - **LRU decoded-tile cache** — fast repeated reads
73
+ - **Thread-safe reads** — concurrent `read_region` from worker threads
74
+
75
+ ## 🏎️ Performance
76
+
77
+ Benchmark vs [ASlide](https://github.com/MrPeterJin/ASlide)'s pure-Python
78
+ TMAP backend (median of 5 runs, same files, same machine):
79
+
80
+ | Scenario | TMAP07 | TMAP06 |
81
+ |---|---|---|
82
+ | Open slide | **281 ms** vs 43 ms ⚠️ | **101 ms** vs 224 ms (2.2×) |
83
+ | Cold 1024² region @L0 | **4.1 ms** vs 15.7 ms (3.8×) | **2.6 ms** vs 14.8 ms (5.6×) |
84
+ | Random 512² region @L0 | **1.1 ms** vs 5.7 ms (5.3×) | **0.9 ms** vs 6.3 ms (6.7×) |
85
+ | Warm 512² region ×50 | **42 ms** vs 217 ms (5.1×) | **54 ms** vs 408 ms (7.6×) |
86
+
87
+ > ASlide's TMAP backend re-decodes every tile on every call; tmapslide adds
88
+ > an LRU decoded-tile cache and per-tile culling, so warm reads and random
89
+ > access are several times faster.
90
+
91
+ ## 📖 API
92
+
93
+ ### `tmapslide.OpenSlide(filename)`
94
+
95
+ | Member | Description |
96
+ | --- | --- |
97
+ | `dimensions` | `(width, height)` at level 0 |
98
+ | `level_count` | number of pyramid levels |
99
+ | `level_dimensions` | `(w, h)` per level |
100
+ | `level_downsamples` | downsample factor per level |
101
+ | `properties` | read-only metadata mapping (`openslide.vendor=unic`, `tmap.*`) |
102
+ | `associated_images` | lazy mapping, typically `macro` / `label` / `thumbnail` |
103
+ | `read_region(loc, level, size)` | `PIL.Image` (RGBA) of the region |
104
+ | `get_thumbnail(size)` | thumbnail from the lowest resolution level |
105
+ | `get_best_level_for_downsample(ds)` | best level for a downsample factor |
106
+ | `close()` / context manager | release resources |
107
+
108
+ ### `tmapslide.open_slide(filename)`
109
+
110
+ Alias of `OpenSlide(filename)`.
111
+
112
+ ## 📦 Supported Formats
113
+
114
+ | Format | Extension | Vendor | Backend |
115
+ | --- | --- | --- | --- |
116
+ | TMAP 06 | `.TMAP` | UNIC (United Imaging) | Pure Python |
117
+ | TMAP 07 | `.TMAP` | UNIC (United Imaging) | Pure Python |
118
+
119
+ ## 🧪 Testing
120
+
121
+ Tests run against real TMAP samples when the `scce_external_center` data
122
+ directory (or `TAPSLIDE_TEST_DATA`) is present next to the repo; synthetic
123
+ fixtures keep the core parser covered everywhere else.
124
+
125
+ ```bash
126
+ pip install -e .[dev]
127
+ pytest
128
+ ```
129
+
130
+ ## 📄 Format Notes (reverse-engineered)
131
+
132
+ TMAP is an undocumented proprietary format. This reader is built from
133
+ binary analysis of real scanner output, cross-validated against
134
+ [ASlide](https://github.com/MrPeterJin/ASlide). Both variants store plain
135
+ JPEG tiles with a small binary header and index tables; there is no
136
+ encryption.
137
+
138
+ - TMAP06 stores 3 pyramid levels (40x / 10x / 2.5x); TMAP07 stores up to
139
+ 10 levels (40x down to 0.078x, halving each level).
140
+ - TMAP06 level-2 previews come from pre-rendered `ShrinkTile` entries and
141
+ are JPEG-compressed at that scale.
142
+ - `iter_tiles()` exposes the stored tile grid directly — useful for
143
+ tile-based ML pipelines.
144
+
145
+ ## 📄 License
146
+
147
+ [MIT](LICENSE)
148
+
149
+ ## 🙏 Acknowledgments
150
+
151
+ - [kfbslide](https://github.com/yifanfeng97/kfbslide) — the KFB reader this
152
+ project is modelled after
153
+ - [OpenSlide](https://openslide.org/) — the API this library mimics
154
+ - [ASlide](https://github.com/MrPeterJin/ASlide) — its independent
155
+ reverse-engineering of the TMAP06 layer/block structures (from decompiled
156
+ vendor SDK) was used to cross-validate this implementation. tmapslide is
157
+ an independent MIT-licensed implementation and ships no ASlide code
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tmapslide"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Pure Python UNIC TMAP whole-slide image reader with OpenSlide-compatible API"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -20,7 +20,7 @@ from ._exceptions import (
20
20
  )
21
21
  from ._slide import OpenSlide, TmapSlide, open_slide
22
22
 
23
- __version__ = "0.2.0"
23
+ __version__ = "0.2.1"
24
24
 
25
25
  # Standard OpenSlide property name constants
26
26
  PROPERTY_NAME_VENDOR = "openslide.vendor"
@@ -408,6 +408,7 @@ class OpenSlide:
408
408
  props = info.properties
409
409
  img_w = int(props.get("tmap.img_width", 2448))
410
410
  img_h = int(props.get("tmap.img_height", 2048))
411
+ tile_img_w, tile_img_h = info.__dict__.get("_tile_jpeg_size", (612, 512))
411
412
  fh = self._ensure_open_handle()
412
413
 
413
414
  # clamp level to what we can render
@@ -487,25 +488,47 @@ class OpenSlide:
487
488
  n_x, n_y = blk["n_x"], blk["n_y"]
488
489
  if n_x + img_w < x0_l0 or n_x > x0_l0 + w0 or n_y + img_h < y0_l0 or n_y > y0_l0 + h0:
489
490
  continue
491
+ jw, jh = info.__dict__.get("_tile_jpeg_size", (tile_img_w, tile_img_h))
490
492
  for layer_no, col, row, off, length in blk["tiles"]:
491
493
  if layer_no != level:
492
494
  continue
493
- tile_img = self._get_cached_tile(off, length)
494
495
  if layer_no > 0:
495
496
  # one tile covers the whole block
496
497
  tile_x0, tile_y0 = n_x, n_y
497
498
  cover_w, cover_h = img_w, img_h
498
499
  else:
499
- tile_x0 = n_x + col * tile_img.width
500
- tile_y0 = n_y + row * tile_img.height
501
- cover_w, cover_h = tile_img.width, tile_img.height
500
+ tile_x0 = n_x + col * jw
501
+ tile_y0 = n_y + row * jh
502
+ cover_w, cover_h = jw, jh
503
+ # cull tiles that miss the region BEFORE decoding
504
+ if (
505
+ tile_x0 + cover_w <= x0_l0
506
+ or tile_x0 >= x0_l0 + w0
507
+ or tile_y0 + cover_h <= y0_l0
508
+ or tile_y0 >= y0_l0 + h0
509
+ ):
510
+ continue
511
+ tile_img = self._get_cached_tile(off, length)
502
512
  paste_scaled(tile_img, tile_x0, tile_y0, cover_w, cover_h)
503
513
  return out
504
514
 
505
515
  def get_thumbnail(self, size: Tuple[int, int]) -> Image.Image:
506
- """Get a thumbnail image no larger than `size`."""
516
+ """Get a thumbnail image no larger than `size`.
517
+
518
+ Prefers the stored thumbnail associated image when the file
519
+ provides one; otherwise renders the lowest-resolution level.
520
+ """
507
521
  self._check_open()
508
- # Prefer the lowest-resolution pyramid level.
522
+ names = set()
523
+ try:
524
+ names = set(self.associated_images)
525
+ except Exception:
526
+ names = set()
527
+ if "thumbnail" in names:
528
+ thumb = self.associated_images["thumbnail"].convert("RGB")
529
+ thumb.thumbnail(size, Image.LANCZOS)
530
+ return thumb
531
+ # Fall back to the lowest-resolution pyramid level.
509
532
  level = self.level_count - 1
510
533
  dims = self.level_dimensions[level]
511
534
  region = self.read_region((0, 0), level, dims)
tmapslide-0.2.0/PKG-INFO DELETED
@@ -1,142 +0,0 @@
1
- Metadata-Version: 2.5
2
- Name: tmapslide
3
- Version: 0.2.0
4
- Summary: Pure Python UNIC TMAP whole-slide image reader with OpenSlide-compatible API
5
- Project-URL: Homepage, https://github.com/yifanfeng97/tmapslide
6
- Project-URL: Documentation, https://github.com/yifanfeng97/tmapslide#readme
7
- Project-URL: Repository, https://github.com/yifanfeng97/tmapslide
8
- Project-URL: Issues, https://github.com/yifanfeng97/tmapslide/issues
9
- Author-email: Yifan Feng <evanfeng97@gmail.com>
10
- License-Expression: MIT
11
- License-File: LICENSE
12
- Keywords: digital-pathology,openslide,pathology,tmap,tmapslide,unic,whole-slide-image,wsi
13
- Classifier: Development Status :: 3 - Alpha
14
- Classifier: Intended Audience :: Science/Research
15
- Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
20
- Classifier: Programming Language :: Python :: 3.13
21
- Classifier: Topic :: Scientific/Engineering :: Image Processing
22
- Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
23
- Requires-Python: >=3.10
24
- Requires-Dist: pillow>=9.0.0
25
- Provides-Extra: dev
26
- Requires-Dist: mypy; extra == 'dev'
27
- Requires-Dist: pytest-cov; extra == 'dev'
28
- Requires-Dist: pytest>=7.0; extra == 'dev'
29
- Requires-Dist: ruff; extra == 'dev'
30
- Description-Content-Type: text/markdown
31
-
32
- # TmapSlide
33
-
34
- ![tmapslide — TMAP whole-slide images in pure Python](docs/hero.jpg)
35
-
36
- Pure Python reader for **UNIC TMAP** whole-slide images (WSI) with an
37
- [OpenSlide](https://openslide.org/)-compatible API — the companion library to
38
- [kfbslide](https://github.com/yifanfeng97/kfbslide) (KFB format).
39
-
40
- ```python
41
- import tmapslide
42
-
43
- slide = tmapslide.OpenSlide("sample.TMAP")
44
-
45
- print(slide.dimensions) # (71424, 72704)
46
- print(slide.level_count) # 10
47
- print(slide.level_downsamples) # (1.0, 2.0, 4.0, ...)
48
-
49
- region = slide.read_region((512, 512), 0, (1024, 1024)) # RGBA PIL image
50
- thumb = slide.get_thumbnail((512, 512))
51
- macro = slide.associated_images["macro"]
52
- ```
53
-
54
- ## Features
55
-
56
- - **Pure Python** — no vendor SDK, no native dependencies; only Pillow.
57
- - **OpenSlide-compatible API** — drop-in for code written against
58
- `openslide`/`kfbslide`: `read_region`, `get_thumbnail`, `dimensions`,
59
- `level_count`, `level_dimensions`, `level_downsamples`, `properties`,
60
- `associated_images`.
61
- - **Both known TMAP variants**: `TMAP06` (single pixel level, 612x512
62
- overlapping tiles) and `TMAP07` (full pyramid, up to 10 levels, 256x256
63
- tiles).
64
- - **Fork-safe file handles** — safe to use from PyTorch `DataLoader` workers.
65
- - LRU tile cache for fast repeated reads.
66
-
67
- ## Installation
68
-
69
- ```bash
70
- pip install tmapslide
71
- # or from source
72
- pip install git+https://github.com/yifanfeng97/tmapslide.git
73
- ```
74
-
75
- ## Supported formats
76
-
77
- | Format | Extension | Vendor | Backend |
78
- | ------- | --------- | ------------------ | ----------- |
79
- | TMAP 06 | `.TMAP` | UNIC (United Imaging) | Pure Python |
80
- | TMAP 07 | `.TMAP` | UNIC (United Imaging) | Pure Python |
81
-
82
- ## API
83
-
84
- ### `tmapslide.OpenSlide(filename)`
85
-
86
- | Member | Description |
87
- | --------------------- | -------------------------------------------------- |
88
- | `dimensions` | `(width, height)` at level 0 |
89
- | `level_count` | number of pyramid levels |
90
- | `level_dimensions` | `(w, h)` per level |
91
- | `level_downsamples` | downsample factor per level |
92
- | `properties` | read-only metadata mapping (`openslide.vendor=unic`, `tmap.*`) |
93
- | `associated_images` | lazy mapping, typically `macro` / `label` |
94
- | `read_region(loc, level, size)` | `PIL.Image` (RGBA) of the region |
95
- | `get_thumbnail(size)` | thumbnail from the lowest resolution level |
96
- | `get_best_level_for_downsample(ds)` | best level for a downsample factor |
97
- | `close()` / context manager | release resources |
98
-
99
- ### `tmapslide.open_slide(filename)`
100
-
101
- Alias of `OpenSlide(filename)`.
102
-
103
- ## Format notes (reverse-engineered)
104
-
105
- TMAP is an undocumented proprietary format. This reader is built from
106
- binary analysis of real scanner output, cross-validated against
107
- [ASlide](https://github.com/MrPeterJin/ASlide). In short: both variants
108
- store plain JPEG tiles with a small binary header and index tables; there
109
- is no encryption.
110
-
111
- - TMAP06 stores 3 pyramid levels (40x / 10x / 2.5x); TMAP07 stores up to
112
- 10 levels (40x down to 0.078x, halving each level).
113
- - TMAP06 level-2 previews come from pre-rendered `ShrinkTile` entries and
114
- are JPEG-compressed at that scale (slightly softer than downsampling
115
- level 0 yourself).
116
- - `iter_tiles()` exposes the stored tile grid directly, useful for
117
- tile-based ML pipelines.
118
-
119
- ## Testing
120
-
121
- Tests run against real TMAP samples when the `scce_external_center` data
122
- directory (or `TAPSLIDE_TEST_DATA`) is present next to the repo, and are
123
- skipped otherwise.
124
-
125
- ```bash
126
- pip install -e .[dev]
127
- pytest
128
- ```
129
-
130
- ## License
131
-
132
- MIT — see [LICENSE](LICENSE).
133
-
134
- ## Acknowledgments
135
-
136
- - [kfbslide](https://github.com/yifanfeng97/kfbslide) — the KFB reader this
137
- project is modelled after.
138
- - [OpenSlide](https://openslide.org/) — the API this library mimics.
139
- - [ASlide](https://github.com/MrPeterJin/ASlide) — its independent
140
- reverse-engineering of the TMAP06 layer/block structures (from decompiled
141
- vendor SDK) was used to cross-validate this implementation. tmapslide is
142
- an independent MIT-licensed implementation and ships no ASlide code.
tmapslide-0.2.0/README.md DELETED
@@ -1,111 +0,0 @@
1
- # TmapSlide
2
-
3
- ![tmapslide — TMAP whole-slide images in pure Python](docs/hero.jpg)
4
-
5
- Pure Python reader for **UNIC TMAP** whole-slide images (WSI) with an
6
- [OpenSlide](https://openslide.org/)-compatible API — the companion library to
7
- [kfbslide](https://github.com/yifanfeng97/kfbslide) (KFB format).
8
-
9
- ```python
10
- import tmapslide
11
-
12
- slide = tmapslide.OpenSlide("sample.TMAP")
13
-
14
- print(slide.dimensions) # (71424, 72704)
15
- print(slide.level_count) # 10
16
- print(slide.level_downsamples) # (1.0, 2.0, 4.0, ...)
17
-
18
- region = slide.read_region((512, 512), 0, (1024, 1024)) # RGBA PIL image
19
- thumb = slide.get_thumbnail((512, 512))
20
- macro = slide.associated_images["macro"]
21
- ```
22
-
23
- ## Features
24
-
25
- - **Pure Python** — no vendor SDK, no native dependencies; only Pillow.
26
- - **OpenSlide-compatible API** — drop-in for code written against
27
- `openslide`/`kfbslide`: `read_region`, `get_thumbnail`, `dimensions`,
28
- `level_count`, `level_dimensions`, `level_downsamples`, `properties`,
29
- `associated_images`.
30
- - **Both known TMAP variants**: `TMAP06` (single pixel level, 612x512
31
- overlapping tiles) and `TMAP07` (full pyramid, up to 10 levels, 256x256
32
- tiles).
33
- - **Fork-safe file handles** — safe to use from PyTorch `DataLoader` workers.
34
- - LRU tile cache for fast repeated reads.
35
-
36
- ## Installation
37
-
38
- ```bash
39
- pip install tmapslide
40
- # or from source
41
- pip install git+https://github.com/yifanfeng97/tmapslide.git
42
- ```
43
-
44
- ## Supported formats
45
-
46
- | Format | Extension | Vendor | Backend |
47
- | ------- | --------- | ------------------ | ----------- |
48
- | TMAP 06 | `.TMAP` | UNIC (United Imaging) | Pure Python |
49
- | TMAP 07 | `.TMAP` | UNIC (United Imaging) | Pure Python |
50
-
51
- ## API
52
-
53
- ### `tmapslide.OpenSlide(filename)`
54
-
55
- | Member | Description |
56
- | --------------------- | -------------------------------------------------- |
57
- | `dimensions` | `(width, height)` at level 0 |
58
- | `level_count` | number of pyramid levels |
59
- | `level_dimensions` | `(w, h)` per level |
60
- | `level_downsamples` | downsample factor per level |
61
- | `properties` | read-only metadata mapping (`openslide.vendor=unic`, `tmap.*`) |
62
- | `associated_images` | lazy mapping, typically `macro` / `label` |
63
- | `read_region(loc, level, size)` | `PIL.Image` (RGBA) of the region |
64
- | `get_thumbnail(size)` | thumbnail from the lowest resolution level |
65
- | `get_best_level_for_downsample(ds)` | best level for a downsample factor |
66
- | `close()` / context manager | release resources |
67
-
68
- ### `tmapslide.open_slide(filename)`
69
-
70
- Alias of `OpenSlide(filename)`.
71
-
72
- ## Format notes (reverse-engineered)
73
-
74
- TMAP is an undocumented proprietary format. This reader is built from
75
- binary analysis of real scanner output, cross-validated against
76
- [ASlide](https://github.com/MrPeterJin/ASlide). In short: both variants
77
- store plain JPEG tiles with a small binary header and index tables; there
78
- is no encryption.
79
-
80
- - TMAP06 stores 3 pyramid levels (40x / 10x / 2.5x); TMAP07 stores up to
81
- 10 levels (40x down to 0.078x, halving each level).
82
- - TMAP06 level-2 previews come from pre-rendered `ShrinkTile` entries and
83
- are JPEG-compressed at that scale (slightly softer than downsampling
84
- level 0 yourself).
85
- - `iter_tiles()` exposes the stored tile grid directly, useful for
86
- tile-based ML pipelines.
87
-
88
- ## Testing
89
-
90
- Tests run against real TMAP samples when the `scce_external_center` data
91
- directory (or `TAPSLIDE_TEST_DATA`) is present next to the repo, and are
92
- skipped otherwise.
93
-
94
- ```bash
95
- pip install -e .[dev]
96
- pytest
97
- ```
98
-
99
- ## License
100
-
101
- MIT — see [LICENSE](LICENSE).
102
-
103
- ## Acknowledgments
104
-
105
- - [kfbslide](https://github.com/yifanfeng97/kfbslide) — the KFB reader this
106
- project is modelled after.
107
- - [OpenSlide](https://openslide.org/) — the API this library mimics.
108
- - [ASlide](https://github.com/MrPeterJin/ASlide) — its independent
109
- reverse-engineering of the TMAP06 layer/block structures (from decompiled
110
- vendor SDK) was used to cross-validate this implementation. tmapslide is
111
- an independent MIT-licensed implementation and ships no ASlide code.
File without changes
File without changes
File without changes