mergechannels 0.1.1__cp310-cp310-win_amd64.whl → 0.2.0__cp310-cp310-win_amd64.whl
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.
Potentially problematic release.
This version of mergechannels might be problematic. Click here for more details.
- mergechannels/__init__.py +7 -7
- mergechannels/__init__.pyi +20 -17
- mergechannels/_internal.py +27 -20
- mergechannels/mergechannels.cp310-win_amd64.pyd +0 -0
- {mergechannels-0.1.1.dist-info → mergechannels-0.2.0.dist-info}/METADATA +59 -26
- mergechannels-0.2.0.dist-info/RECORD +11 -0
- {mergechannels-0.1.1.dist-info → mergechannels-0.2.0.dist-info}/WHEEL +1 -1
- {mergechannels-0.1.1.dist-info → mergechannels-0.2.0.dist-info}/licenses/LICENSE +1 -1
- mergechannels-0.1.1.dist-info/RECORD +0 -11
mergechannels/__init__.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Import Rust functions
|
|
2
2
|
from .mergechannels import ( # type: ignore
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
)
|
|
6
|
-
|
|
3
|
+
dispatch_single_channel,
|
|
4
|
+
dispatch_multi_channel,
|
|
5
|
+
)
|
|
7
6
|
from ._internal import merge
|
|
8
7
|
|
|
8
|
+
|
|
9
9
|
__all__ = [
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
'dispatch_single_channel',
|
|
11
|
+
'dispatch_multi_channel',
|
|
12
|
+
'merge',
|
|
13
13
|
]
|
mergechannels/__init__.pyi
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
from typing import Sequence, Literal
|
|
2
1
|
|
|
2
|
+
from typing import Literal, Sequence
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
|
-
from .
|
|
6
|
-
from .
|
|
5
|
+
from python.mergechannels._blending import BLENDING_OPTIONS
|
|
6
|
+
from python.mergechannels._luts import COLORMAPS
|
|
7
|
+
|
|
7
8
|
|
|
8
9
|
def merge(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
arrs: np.ndarray | Sequence[np.ndarray],
|
|
11
|
+
colors: Sequence[COLORMAPS] = (),
|
|
12
|
+
blending: Literal[BLENDING_OPTIONS] = 'max',
|
|
12
13
|
saturation_limits: tuple[float, float] = (0.2, 99.8),
|
|
13
14
|
) -> np.ndarray:
|
|
14
15
|
...
|
|
15
16
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
cmap_name:
|
|
19
|
-
|
|
20
|
-
) -> np.ndarray:
|
|
17
|
+
def dispatch_single_channel(
|
|
18
|
+
array_reference: np.ndarray,
|
|
19
|
+
cmap_name: str,
|
|
20
|
+
limits: tuple[float, float],
|
|
21
|
+
) -> np.ndarray:
|
|
22
|
+
...
|
|
21
23
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
blending: Literal[BLENDING_OPTIONS]
|
|
26
|
-
|
|
27
|
-
) -> np.ndarray:
|
|
24
|
+
def dispatch_multi_channel(
|
|
25
|
+
array_references: Sequence[np.ndarray],
|
|
26
|
+
cmap_names: Sequence[str],
|
|
27
|
+
blending: Literal[BLENDING_OPTIONS],
|
|
28
|
+
limits: Sequence[tuple[float, float]],
|
|
29
|
+
) -> np.ndarray:
|
|
30
|
+
...
|
mergechannels/_internal.py
CHANGED
|
@@ -3,8 +3,8 @@ from typing import Sequence
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
5
|
from mergechannels import (
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
dispatch_single_channel,
|
|
7
|
+
dispatch_multi_channel,
|
|
8
8
|
)
|
|
9
9
|
from ._luts import COLORMAPS
|
|
10
10
|
from ._blending import BLENDING_OPTIONS
|
|
@@ -13,6 +13,7 @@ def merge(
|
|
|
13
13
|
arrs: Sequence[np.ndarray],
|
|
14
14
|
colors: Sequence[COLORMAPS],
|
|
15
15
|
blending: BLENDING_OPTIONS = 'max',
|
|
16
|
+
saturation_limits: tuple[float, float] = (0.011, 0.999),
|
|
16
17
|
) -> np.ndarray:
|
|
17
18
|
'''
|
|
18
19
|
apply cmaps to arrays and blend the colors
|
|
@@ -30,9 +31,9 @@ def merge(
|
|
|
30
31
|
raise ValueError(
|
|
31
32
|
f'Expected every array to have the same shape, got {arr_shapes}'
|
|
32
33
|
)
|
|
33
|
-
if
|
|
34
|
+
if len(arr_shapes[0]) not in (2, 3):
|
|
34
35
|
raise ValueError(
|
|
35
|
-
f'Expected every array to be 2D, got {arr_shapes[0]}'
|
|
36
|
+
f'Expected every array to be 2D or 3D, got {arr_shapes[0]}'
|
|
36
37
|
)
|
|
37
38
|
arr_dtypes = [arr.dtype for arr in arrs]
|
|
38
39
|
if not len(set(arr_dtypes)) == 1:
|
|
@@ -40,19 +41,25 @@ def merge(
|
|
|
40
41
|
f'Expected every array to have the same dtype, got {arr_dtypes}'
|
|
41
42
|
)
|
|
42
43
|
# endregion
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
if n_arrs == 1:
|
|
45
|
+
if arrs[0].dtype == 'uint8':
|
|
46
|
+
limits = (0, 255)
|
|
47
|
+
else:
|
|
48
|
+
low, high = np.percentile(arrs[0], np.array(saturation_limits) * 100)
|
|
49
|
+
limits = (low, high)
|
|
50
|
+
return dispatch_single_channel(
|
|
51
|
+
array_reference=arrs[0],
|
|
52
|
+
cmap_name=colors[0],
|
|
53
|
+
limits=limits,
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
if all(arr.dtype == 'uint8' for arr in arrs):
|
|
57
|
+
limits = (0, 255)
|
|
58
|
+
else:
|
|
59
|
+
limits = tuple(np.percentile(arr, np.array(saturation_limits) * 100) for arr in arrs)
|
|
60
|
+
return dispatch_multi_channel(
|
|
61
|
+
array_references=arrs,
|
|
62
|
+
cmap_names=colors,
|
|
63
|
+
blending=blending,
|
|
64
|
+
limits=limits, # type: ignore
|
|
65
|
+
)
|
|
Binary file
|
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mergechannels
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
7
|
Classifier: License :: OSI Approved :: MIT License
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
10
9
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Requires-Dist: numpy>1.25.0
|
|
15
|
+
Provides-Extra: nvim
|
|
15
16
|
License-File: LICENSE
|
|
17
|
+
Summary: Apply and merge colormaps
|
|
18
|
+
Author-email: Zac Swider <zac.swider@gmail.com>
|
|
16
19
|
License: MIT
|
|
17
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.9, <=3.13
|
|
18
21
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
19
22
|
|
|
20
23
|
[](https://github.com/zacswider/mergechannels/actions/workflows/CI.yml)
|
|
21
|
-

|
|
25
|
+

|
|
22
26
|

|
|
23
27
|
|
|
24
28
|
# mergechannels
|
|
@@ -59,7 +63,7 @@ plt.show()
|
|
|
59
63
|
print(colorized.shape, colorized.dtype)
|
|
60
64
|
>> (512, 512, 3) uint8
|
|
61
65
|
```
|
|
62
|
-

|
|
63
67
|
|
|
64
68
|
|
|
65
69
|
### apply a different colormap to each channel
|
|
@@ -69,38 +73,66 @@ import matplotlib.pyplot as plt
|
|
|
69
73
|
import mergechannels as mc
|
|
70
74
|
|
|
71
75
|
cells, nuclei = data.cells3d().max(axis=0)
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
assert cells.dtype == 'uint16' and nuclei.dtype == 'uint16'
|
|
77
|
+
fig, axes = plt.subplots(1, 2, figsize=(3, 6), dpi=300)
|
|
78
|
+
for ax in axes.ravel(): ax.axis('off')
|
|
79
|
+
(a, b) = axes.ravel()
|
|
80
|
+
a.imshow(mc.merge([cells, nuclei],['Orange Hot', 'Cyan Hot']))
|
|
81
|
+
b.imshow(mc.merge([cells, nuclei],['I Blue', 'I Forest'], blending='min'))
|
|
82
|
+
fig.tight_layout()
|
|
83
|
+
plt.show()
|
|
84
|
+
```
|
|
85
|
+

|
|
86
|
+
|
|
87
|
+
### apply a colormap to a whole stack
|
|
88
|
+
```python
|
|
89
|
+
from skimage import data
|
|
90
|
+
from matplotlib import pyplot as plt
|
|
91
|
+
import mergechannels as mc
|
|
92
|
+
|
|
93
|
+
volume = data.cells3d()
|
|
94
|
+
cells = volume[:, 0]
|
|
95
|
+
nuclei = volume[:, 1]
|
|
96
|
+
merged = mc.merge([cells, nuclei],['Orange Hot', 'Cyan Hot'])
|
|
97
|
+
plt.imshow(merged[24]); plt.show()
|
|
98
|
+
```
|
|
99
|
+

|
|
74
100
|
|
|
75
|
-
|
|
101
|
+
### adjust the saturation limits when applying colormaps
|
|
102
|
+
``` python
|
|
103
|
+
from skimage import data
|
|
104
|
+
import matplotlib.pyplot as plt
|
|
105
|
+
import mergechannels as mc
|
|
106
|
+
|
|
107
|
+
cells, nuclei = data.cells3d().max(axis=0)
|
|
108
|
+
channels = [cells, nuclei]
|
|
109
|
+
colormaps = ['I Blue', 'I Forest']
|
|
110
|
+
fig, axes = plt.subplots(1, 2, figsize=(3, 6), dpi=300)
|
|
76
111
|
for ax in axes.ravel(): ax.axis('off')
|
|
77
|
-
(a, b
|
|
78
|
-
a.imshow(
|
|
79
|
-
b.imshow(
|
|
80
|
-
c.imshow(
|
|
112
|
+
(a, b) = axes.ravel()
|
|
113
|
+
a.imshow(mc.merge(channels, colormaps, blending='min'))
|
|
114
|
+
b.imshow(
|
|
81
115
|
mc.merge(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
['I Blue', 'I Forest'],
|
|
90
|
-
blending='min', # use minimum blending with inverted colormaps
|
|
116
|
+
channels,
|
|
117
|
+
colormaps,
|
|
118
|
+
blending='min',
|
|
119
|
+
saturation_limits=(
|
|
120
|
+
0.01, # bottom 1% of pixels set to black point
|
|
121
|
+
0.97, # top 3% of pixels set to white point
|
|
122
|
+
),
|
|
91
123
|
),
|
|
92
124
|
)
|
|
93
125
|
fig.tight_layout()
|
|
94
126
|
plt.show()
|
|
95
127
|
```
|
|
96
|
-

|
|
97
129
|
|
|
98
130
|
|
|
99
131
|
## Roadmap
|
|
100
|
-
mergechannels is currently incredibly simple. It can apply one or more colormaps to one or more 2D 8-bit images and that's it.
|
|
132
|
+
mergechannels is currently incredibly simple. It can apply one or more colormaps to one or more 2D and 3D 8-bit or 16-bit images and that's it.
|
|
101
133
|
- Add support for any numerical dtype
|
|
102
|
-
- Add support for 3D images
|
|
103
134
|
- Add option to return any colormap as a matplotlib colormap
|
|
135
|
+
- Add option to pass external colormaps to mergechannels
|
|
104
136
|
- Add support for directly passing matplotlib colormaps instead of colormap names
|
|
105
137
|
- Parallelize colormap application on large images (if it's helpful)
|
|
106
138
|
- Add option to overlay binary or instance masks onto colorized images
|
|
@@ -109,4 +141,5 @@ mergechannels is currently incredibly simple. It can apply one or more colormaps
|
|
|
109
141
|
|
|
110
142
|
There are other great colormapping libraries available (e.g., [microfilm](https://github.com/guiwitz/microfilm), [cmap](https://github.com/pyapp-kit/cmap)) that are more feature-rich than this one, but which don't address my goals. My hope is that this project can fill an un-met niche and otherwise maintain full compatibility with these and similar libraries.
|
|
111
143
|
|
|
112
|
-
This project incorporates a number of colormaps that were hand-crafted by Christophe Leterrier and were originally distributed here under the MIT license: https://github.com/cleterrier/ChrisLUTs
|
|
144
|
+
This project incorporates a number of colormaps that were hand-crafted by Christophe Leterrier and were originally distributed here under the MIT license: https://github.com/cleterrier/ChrisLUTs
|
|
145
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
mergechannels-0.2.0.dist-info/METADATA,sha256=k5x2tptazozlFMop2CjUJSMhfRV561HKMOYjFQq1xrw,5847
|
|
2
|
+
mergechannels-0.2.0.dist-info/WHEEL,sha256=tvMehAuwYswG9dMmwHb6RW7O-9AAAcnneXQ2akCAgrw,96
|
|
3
|
+
mergechannels-0.2.0.dist-info/licenses/LICENSE,sha256=13XDkLAzpvwoVSoi1BksE9O_nNxoO9WVFRcF1HPMvJo,1092
|
|
4
|
+
mergechannels/__init__.py,sha256=xbqeTJbcgfawYB0ZngDmCtLFCjlSNNz7DbgyFHqKELI,245
|
|
5
|
+
mergechannels/__init__.pyi,sha256=Zg7q86czacOLaWtUp11le88DPm3Qc50VdNH-SpQh3L8,779
|
|
6
|
+
mergechannels/_blending.py,sha256=fyw9IV1QzSR_RKCJtI-q67ylnBv9PrA4ofX-uvXXJaY,87
|
|
7
|
+
mergechannels/_internal.py,sha256=IBM21aiaderimszSQ85x0cC8xkvw-xKuJaPo73x8udY,1838
|
|
8
|
+
mergechannels/_luts.py,sha256=QtQPO-6P-x0vjGaizsEOgth0miX0vIcZEHVaVzlumBY,1116
|
|
9
|
+
mergechannels/mergechannels.cp310-win_amd64.pyd,sha256=FHRphWI3I7JQzYVRHY7_nxrsUS1SaAPvgr4014F3Yj4,507392
|
|
10
|
+
mergechannels/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
mergechannels-0.2.0.dist-info/RECORD,,
|
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
mergechannels-0.1.1.dist-info/METADATA,sha256=O_22ND9cZ3QI3ciFGZAFKM-uekER4eiuh5qtk01FKFI,4612
|
|
2
|
-
mergechannels-0.1.1.dist-info/WHEEL,sha256=77DqkvxB4HqZitBRK_M49NRS207JKb0MotMEjnxEWQ8,96
|
|
3
|
-
mergechannels-0.1.1.dist-info/licenses/LICENSE,sha256=qBDSUScUj9TtHM9cTe1mi7luamcrO--X01yLnx1iS5Q,1090
|
|
4
|
-
mergechannels/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
mergechannels/_blending.py,sha256=fyw9IV1QzSR_RKCJtI-q67ylnBv9PrA4ofX-uvXXJaY,87
|
|
6
|
-
mergechannels/_internal.py,sha256=iX5LY9_ac03ugzcnT7UV54kezPlI-ADD8ah3Y1LNiNU,1325
|
|
7
|
-
mergechannels/_luts.py,sha256=QtQPO-6P-x0vjGaizsEOgth0miX0vIcZEHVaVzlumBY,1116
|
|
8
|
-
mergechannels/__init__.py,sha256=nmEYzwQvfD9BydS564upIiIyFA15wKhUGtAqe3RH490,237
|
|
9
|
-
mergechannels/__init__.pyi,sha256=cekSFzU1_uFnCdxnOQCa_RGs8eNu_hsnTCbHwK_bZFM,736
|
|
10
|
-
mergechannels/mergechannels.cp310-win_amd64.pyd,sha256=MADOJ5ZIWCGWUIadYAqxej2na7ihtdQO8mkiyKbITK4,449536
|
|
11
|
-
mergechannels-0.1.1.dist-info/RECORD,,
|