figpack-spike-sorting 0.1.7__py3-none-any.whl → 0.1.8__py3-none-any.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.
- figpack_spike_sorting/__init__.py +1 -1
- figpack_spike_sorting/figpack_spike_sorting.js +2629 -48
- figpack_spike_sorting/views/TiledImage.py +159 -0
- figpack_spike_sorting/views/__init__.py +3 -0
- {figpack_spike_sorting-0.1.7.dist-info → figpack_spike_sorting-0.1.8.dist-info}/METADATA +1 -1
- {figpack_spike_sorting-0.1.7.dist-info → figpack_spike_sorting-0.1.8.dist-info}/RECORD +8 -7
- {figpack_spike_sorting-0.1.7.dist-info → figpack_spike_sorting-0.1.8.dist-info}/WHEEL +1 -1
- {figpack_spike_sorting-0.1.7.dist-info → figpack_spike_sorting-0.1.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TiledImage view for figpack - displays large images using tiled rendering with deck.gl
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from tempfile import TemporaryDirectory
|
|
7
|
+
from typing import Any, List, Union
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
import figpack
|
|
12
|
+
from ..spike_sorting_extension import spike_sorting_extension
|
|
13
|
+
|
|
14
|
+
pyvips_installation_msg = "To use TiledImage you need to install pyvips (conda install -c conda-forge pyvips recommended)"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TiledImageLayer:
|
|
18
|
+
def __init__(self, label: str, data: Union[np.ndarray, Any]) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Initialize a TiledImageLayer
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
label: Label for the layer
|
|
24
|
+
data: Image data as numpy array (uint8, shape (height, width, 3) for RGB)
|
|
25
|
+
or pyvips.Image object
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
import pyvips
|
|
29
|
+
except ImportError:
|
|
30
|
+
raise ImportError(pyvips_installation_msg)
|
|
31
|
+
|
|
32
|
+
self.label = label
|
|
33
|
+
self.data = data
|
|
34
|
+
|
|
35
|
+
if isinstance(data, pyvips.Image):
|
|
36
|
+
image: pyvips.Image = data
|
|
37
|
+
else:
|
|
38
|
+
if not isinstance(data, np.ndarray):
|
|
39
|
+
raise TypeError("Data must be a numpy array or pyvips.Image")
|
|
40
|
+
if data.dtype != np.uint8:
|
|
41
|
+
raise ValueError("Data must be of type uint8")
|
|
42
|
+
# Convert numpy array to pyvips image
|
|
43
|
+
image: pyvips.Image = pyvips.Image.new_from_array(data)
|
|
44
|
+
|
|
45
|
+
self.image = image
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TiledImage(figpack.ExtensionView):
|
|
49
|
+
"""
|
|
50
|
+
A view that displays large images using tiled rendering for efficient viewing
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self, *, tile_size: int, layers: List[TiledImageLayer], verbose: bool = False
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Initialize a TiledImage view
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
tile_size: Size of each tile in pixels (e.g., 256)
|
|
61
|
+
layers: List of TiledImageLayer objects
|
|
62
|
+
verbose: Whether to print verbose output during tile generation
|
|
63
|
+
"""
|
|
64
|
+
super().__init__(
|
|
65
|
+
extension=spike_sorting_extension, view_type="spike_sorting.TiledImage"
|
|
66
|
+
)
|
|
67
|
+
self.tile_size = tile_size
|
|
68
|
+
self.layers = layers
|
|
69
|
+
self.verbose = verbose
|
|
70
|
+
|
|
71
|
+
def write_to_zarr_group(self, group: figpack.Group) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Write the TiledImage data to a Zarr group
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
group: Zarr group to write data into
|
|
77
|
+
"""
|
|
78
|
+
import os
|
|
79
|
+
import pyvips
|
|
80
|
+
|
|
81
|
+
super().write_to_zarr_group(group)
|
|
82
|
+
|
|
83
|
+
group.attrs["tile_size"] = self.tile_size
|
|
84
|
+
group.attrs["num_layers"] = len(self.layers)
|
|
85
|
+
|
|
86
|
+
for layer_idx, layer in enumerate(self.layers):
|
|
87
|
+
if self.verbose:
|
|
88
|
+
print(
|
|
89
|
+
f"Processing layer {layer_idx + 1}/{len(self.layers)}: {layer.label}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
layer_group = group.create_group(f"layer_{layer_idx}")
|
|
93
|
+
layer_group.attrs["label"] = layer.label
|
|
94
|
+
|
|
95
|
+
image: pyvips.Image = layer.image
|
|
96
|
+
layer_group.attrs["width"] = image.width
|
|
97
|
+
layer_group.attrs["height"] = image.height
|
|
98
|
+
|
|
99
|
+
# Generate tiled JPEG images using pyvips dzsave
|
|
100
|
+
with TemporaryDirectory() as tmpdir:
|
|
101
|
+
# Generate the tile pyramid
|
|
102
|
+
image.dzsave(
|
|
103
|
+
f"{tmpdir}/output",
|
|
104
|
+
overlap=0,
|
|
105
|
+
tile_size=self.tile_size,
|
|
106
|
+
layout=pyvips.enums.ForeignDzLayout.DZ,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
output_dirname = f"{tmpdir}/output_files"
|
|
110
|
+
|
|
111
|
+
# Count zoom levels
|
|
112
|
+
num_zoom_levels = 0
|
|
113
|
+
z = 1
|
|
114
|
+
while True:
|
|
115
|
+
dirname = f"{output_dirname}/{z}"
|
|
116
|
+
if not os.path.exists(dirname):
|
|
117
|
+
break
|
|
118
|
+
num_zoom_levels = z
|
|
119
|
+
z += 1
|
|
120
|
+
|
|
121
|
+
layer_group.attrs["num_zoom_levels"] = num_zoom_levels
|
|
122
|
+
|
|
123
|
+
# Store each tile as a zarr dataset containing JPEG bytes
|
|
124
|
+
tiles_group = layer_group.create_group("tiles")
|
|
125
|
+
|
|
126
|
+
for z in range(1, num_zoom_levels + 1):
|
|
127
|
+
dirname = f"{output_dirname}/{z}"
|
|
128
|
+
j = 0
|
|
129
|
+
while True:
|
|
130
|
+
if not os.path.exists(f"{dirname}/{j}_0.jpeg"):
|
|
131
|
+
break
|
|
132
|
+
k = 0
|
|
133
|
+
while True:
|
|
134
|
+
tile_path = f"{dirname}/{j}_{k}.jpeg"
|
|
135
|
+
if not os.path.exists(tile_path):
|
|
136
|
+
break
|
|
137
|
+
|
|
138
|
+
# Read the JPEG file as bytes
|
|
139
|
+
with open(tile_path, "rb") as f:
|
|
140
|
+
jpeg_bytes = f.read()
|
|
141
|
+
|
|
142
|
+
# Store as uint8 array in zarr
|
|
143
|
+
tile_key = f"{z}_{j}_{k}"
|
|
144
|
+
jpeg_array = np.frombuffer(jpeg_bytes, dtype=np.uint8)
|
|
145
|
+
tiles_group.create_dataset(tile_key, data=jpeg_array)
|
|
146
|
+
|
|
147
|
+
if self.verbose:
|
|
148
|
+
print(
|
|
149
|
+
f" Stored tile {tile_key}: {len(jpeg_bytes)} bytes"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
k += 1
|
|
153
|
+
j += 1
|
|
154
|
+
|
|
155
|
+
if self.verbose:
|
|
156
|
+
print(
|
|
157
|
+
f" Layer {layer.label}: {image.width}x{image.height}, "
|
|
158
|
+
f"{num_zoom_levels} zoom levels"
|
|
159
|
+
)
|
|
@@ -20,6 +20,7 @@ from .UnitMetricsGraph import (
|
|
|
20
20
|
UnitMetricsGraphUnit,
|
|
21
21
|
)
|
|
22
22
|
from .SortingCuration import SortingCuration
|
|
23
|
+
from .TiledImage import TiledImage, TiledImageLayer
|
|
23
24
|
|
|
24
25
|
__all__ = [
|
|
25
26
|
"Autocorrelograms",
|
|
@@ -42,4 +43,6 @@ __all__ = [
|
|
|
42
43
|
"UnitMetricsGraphMetric",
|
|
43
44
|
"UnitMetricsGraphUnit",
|
|
44
45
|
"SortingCuration",
|
|
46
|
+
"TiledImage",
|
|
47
|
+
"TiledImageLayer",
|
|
45
48
|
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
figpack_spike_sorting/__init__.py,sha256=
|
|
2
|
-
figpack_spike_sorting/figpack_spike_sorting.js,sha256=
|
|
1
|
+
figpack_spike_sorting/__init__.py,sha256=X3iWqqx6ep-Gznn4FbdnV5eUsflN-q7z-JiF4IalB3M,100
|
|
2
|
+
figpack_spike_sorting/figpack_spike_sorting.js,sha256=D3FB0vIMOM3WN-P5VUIZZIVI4iBMp7NjXpwSis5gMkw,1509907
|
|
3
3
|
figpack_spike_sorting/spike_sorting_extension.py,sha256=ugpk8hDsO4RmyQRNjfhB4Pw5p10FmhQeu3koBynSPHU,755
|
|
4
4
|
figpack_spike_sorting/style.css,sha256=R4pcnrpSMpOsBn7X1t-JBP8ODktsc5e8o95Fl_0LVps,3208
|
|
5
5
|
figpack_spike_sorting/views/AutocorrelogramItem.py,sha256=qHmvIdHpbfVA_utPb5N2oP3hSP2cGnlT8VLaxOXV4UM,738
|
|
@@ -12,14 +12,15 @@ figpack_spike_sorting/views/RasterPlotItem.py,sha256=iW7fuDEjSfvf5YMIwrF_6cmKvD7
|
|
|
12
12
|
figpack_spike_sorting/views/SortingCuration.py,sha256=J3p9vrhbnRt05qeihZk_i6EBWDcTGYs293WVW_9qLSk,1243
|
|
13
13
|
figpack_spike_sorting/views/SpikeAmplitudes.py,sha256=H_VV0Z1kAVCztNaTYnOW8tqyu6FLtgFPWNrlmokVKeI,13290
|
|
14
14
|
figpack_spike_sorting/views/SpikeAmplitudesItem.py,sha256=j5Na-diY-vRUAPu0t0VkyFCSKFnQ_f5HT077mB3Cy8c,1134
|
|
15
|
+
figpack_spike_sorting/views/TiledImage.py,sha256=_nvFhvRoGWd7MN84d9CphB0bK4j0txlx035zm-hCDic,5457
|
|
15
16
|
figpack_spike_sorting/views/UnitLocations.py,sha256=K18oJFnKshn0fZ9NtgKXcWGroJGc_OaF3f_ZEEInZPA,2336
|
|
16
17
|
figpack_spike_sorting/views/UnitMetricsGraph.py,sha256=8YZo2-5d-RHKSfyUKYAfDvW9hZDCTzmnUAyb3fPWo3I,3373
|
|
17
18
|
figpack_spike_sorting/views/UnitSimilarityScore.py,sha256=cJA9MkETos9qHhV1tqgA7SfNEaPo-duXYCE76hSFGnA,948
|
|
18
19
|
figpack_spike_sorting/views/UnitsTable.py,sha256=8aluJ2tCnwsFOLbjbJDyY2z1ml9VK7Sz5_bxdRUAeDI,2758
|
|
19
20
|
figpack_spike_sorting/views/UnitsTableColumn.py,sha256=zBnuoeILTuiVLDvtcOxqa37E5WlbR12rlwNJUeWXxY4,847
|
|
20
21
|
figpack_spike_sorting/views/UnitsTableRow.py,sha256=rEb2hMTA_pl2fTW1nOvnGir0ysfNx4uww3aekZzfWjk,720
|
|
21
|
-
figpack_spike_sorting/views/__init__.py,sha256=
|
|
22
|
-
figpack_spike_sorting-0.1.
|
|
23
|
-
figpack_spike_sorting-0.1.
|
|
24
|
-
figpack_spike_sorting-0.1.
|
|
25
|
-
figpack_spike_sorting-0.1.
|
|
22
|
+
figpack_spike_sorting/views/__init__.py,sha256=gUt261Id35gNiJyne6GY-NzIGyTfRDmL25tNjyC2dLY,1409
|
|
23
|
+
figpack_spike_sorting-0.1.8.dist-info/METADATA,sha256=48QIQNnjJ4_RpsX7uhn7fvtKN8kpJepvtM4Lxj1U78U,1218
|
|
24
|
+
figpack_spike_sorting-0.1.8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
25
|
+
figpack_spike_sorting-0.1.8.dist-info/top_level.txt,sha256=CAkWrQc1wGK5laYDkT2GBjGgAuNwWNXKodTQlTcpxus,22
|
|
26
|
+
figpack_spike_sorting-0.1.8.dist-info/RECORD,,
|
{figpack_spike_sorting-0.1.7.dist-info → figpack_spike_sorting-0.1.8.dist-info}/top_level.txt
RENAMED
|
File without changes
|