|
1 |
| -import argparse |
| 1 | +# %% Import packages |
| 2 | + |
2 | 3 | import itertools
|
3 |
| -import webbrowser |
4 |
| -import neuroglancer |
5 |
| -import neuroglancer.cli |
6 | 4 | from pathlib import Path
|
7 | 5 | from bfio import BioReader
|
8 |
| -import dask |
9 |
| -import dask.array |
10 | 6 | import numpy as np
|
| 7 | +from cloudvolume import CloudVolume |
| 8 | +from cloudvolume.lib import touch |
| 9 | +import json |
| 10 | + |
| 11 | +# %% Define the path to the files |
11 | 12 |
|
12 | 13 | HERE = Path(__file__).parent
|
13 | 14 |
|
14 |
| -FILEPATH = Path("x.ome.tiff") |
15 |
| - |
16 |
| - |
17 |
| -def add_image_layer(state, path, name="image"): |
18 |
| - br = BioReader(str(path), backend="bioformats") |
19 |
| - chunk_shape = np.array([256, 256, 128, 1]) |
20 |
| - shape = np.array(br.shape) |
21 |
| - num_chunks_per_dim = np.ceil(shape / chunk_shape).astype(int) |
22 |
| - padded_chunk_shape = num_chunks_per_dim * chunk_shape |
23 |
| - |
24 |
| - def chunked_reader(x_i, y_i, z_i, c): |
25 |
| - x_start, x_end = x_i * chunk_shape[0], min((x_i + 1) * chunk_shape[0], shape[0]) |
26 |
| - y_start, y_end = y_i * chunk_shape[1], min((y_i + 1) * chunk_shape[1], shape[1]) |
27 |
| - z_start, z_end = z_i * chunk_shape[2], min((z_i + 1) * chunk_shape[2], shape[2]) |
28 |
| - |
29 |
| - # Read the chunk from the BioReader |
30 |
| - chunk = br.read( |
31 |
| - X=(x_start, x_end), Y=(y_start, y_end), Z=(z_start, z_end), C=(c,) |
32 |
| - ) |
33 |
| - # Extend the chunk to be X, Y, Z, 1 not just X, Y, Z |
34 |
| - chunk = np.expand_dims(chunk, axis=-1) |
35 |
| - # If the chunk is smaller than the padded chunk shape, pad it |
36 |
| - if chunk.shape != tuple(chunk_shape[:3]): |
37 |
| - padded_chunk = np.zeros(chunk_shape, dtype=chunk.dtype) |
38 |
| - padded_chunk[: chunk.shape[0], : chunk.shape[1], : chunk.shape[2], :] = ( |
39 |
| - chunk |
40 |
| - ) |
41 |
| - return padded_chunk |
42 |
| - return chunk |
43 |
| - |
44 |
| - def chunk_size(x_i, y_i, z_i, c): |
45 |
| - x_start, x_end = x_i * chunk_shape[0], min((x_i + 1) * chunk_shape[0], shape[0]) |
46 |
| - y_start, y_end = y_i * chunk_shape[1], min((y_i + 1) * chunk_shape[1], shape[1]) |
47 |
| - z_start, z_end = z_i * chunk_shape[2], min((z_i + 1) * chunk_shape[2], shape[2]) |
48 |
| - |
49 |
| - return (x_end - x_start, y_end - y_start, z_end - z_start, 1) |
50 |
| - |
51 |
| - lazy_reader = dask.delayed(chunked_reader) |
52 |
| - lazy_chunks = [ |
53 |
| - lazy_reader(x, y, z, c) |
54 |
| - for x, y, z, c in itertools.product(*[range(i) for i in num_chunks_per_dim]) |
55 |
| - ] |
56 |
| - # chunk_sizes = [ |
57 |
| - # chunk_size(x, y, z, c) |
58 |
| - # for x, y, z, c in itertools.product(*[range(i) for i in num_chunks_per_dim]) |
59 |
| - # ] |
60 |
| - sample = lazy_chunks[ |
61 |
| - 0 |
62 |
| - ].compute() # load the first chunk (assume rest are same shape/dtype) |
63 |
| - arrays = [ |
64 |
| - dask.array.from_delayed(lazy_chunk, dtype=sample.dtype, shape=sample.shape) |
65 |
| - for lazy_chunk in lazy_chunks |
| 15 | +FILEPATH = Path( |
| 16 | + "/media/starfish/Storage/metacell/Isl1-GFP_E13-5_F129-3_CMN-R-L_02052024-GLC-stitched.ome.tiff" |
| 17 | +) |
| 18 | +OUTPUT_PATH = Path( |
| 19 | + "/media/starfish/Storage/metacell/converted/Isl1-GFP_E13-5_F129-3_CMN-R-L_02052024-GLC-stitched" |
| 20 | +) |
| 21 | +OUTPUT_PATH.mkdir(exist_ok=True, parents=True) |
| 22 | + |
| 23 | +# %% Load the data |
| 24 | +br = BioReader(str(FILEPATH), backend="bioformats") |
| 25 | + |
| 26 | +# %% Inspect the data |
| 27 | +with open(OUTPUT_PATH / "metadata.txt", "w") as f: |
| 28 | + json.dump(br.metadata.model_dump_json(), f) |
| 29 | + f.write(br.metadata.model_dump_json()) |
| 30 | +print(br.shape) |
| 31 | + |
| 32 | +# %% Extract from the data - units are in nm |
| 33 | +size_x = br.metadata.images[0].pixels.physical_size_x |
| 34 | +if size_x is None: |
| 35 | + size_x = 1 |
| 36 | +size_y = br.metadata.images[0].pixels.physical_size_y |
| 37 | +if size_y is None: |
| 38 | + size_y = 1 |
| 39 | +size_z = br.metadata.images[0].pixels.physical_size_z |
| 40 | +if size_z is None: |
| 41 | + size_z = 1 |
| 42 | +x_unit = br.metadata.images[0].pixels.physical_size_x_unit |
| 43 | +y_unit = br.metadata.images[0].pixels.physical_size_y_unit |
| 44 | +z_unit = br.metadata.images[0].pixels.physical_size_z_unit |
| 45 | + |
| 46 | +# if the the units are um, convert to nm |
| 47 | +if str(x_unit) == "UnitsLength.MICROMETER": |
| 48 | + size_x *= 1000 |
| 49 | +if str(y_unit) == "UnitsLength.MICROMETER": |
| 50 | + size_y *= 1000 |
| 51 | +if str(z_unit) == "UnitsLength.MICROMETER": |
| 52 | + size_z *= 1000 |
| 53 | + |
| 54 | +num_channels = br.shape[-1] |
| 55 | +data_type = "uint16" |
| 56 | +chunk_size = [256, 256, 128, 1] |
| 57 | + |
| 58 | +# %% Setup the cloudvolume info |
| 59 | +info = CloudVolume.create_new_info( |
| 60 | + num_channels=num_channels, |
| 61 | + layer_type="image", |
| 62 | + data_type=data_type, |
| 63 | + encoding="raw", |
| 64 | + resolution=[size_x, size_y, size_z], |
| 65 | + voxel_offset=[0, 0, 0], |
| 66 | + chunk_size=chunk_size[:-1], |
| 67 | + volume_size=[br.shape[0], br.shape[1], br.shape[2]], |
| 68 | +) |
| 69 | +vol = CloudVolume("file://" + str(OUTPUT_PATH), info=info) |
| 70 | +vol.provenance.description = "Example data conversion" |
| 71 | +vol.commit_info() |
| 72 | +vol.commit_provenance() |
| 73 | + |
| 74 | +# %% Setup somewhere to hold progress |
| 75 | +progress_dir = OUTPUT_PATH / "progress" |
| 76 | +progress_dir.mkdir(exist_ok=True) |
| 77 | + |
| 78 | + |
| 79 | +# %% Functions for moving data |
| 80 | +shape = np.array(br.shape) |
| 81 | +chunk_shape = np.array([1024, 1024, 512, 1]) # this is for reading data |
| 82 | +num_chunks_per_dim = np.ceil(shape / chunk_shape).astype(int) |
| 83 | + |
| 84 | + |
| 85 | +def chunked_reader(x_i, y_i, z_i, c): |
| 86 | + x_start, x_end = x_i * chunk_shape[0], min((x_i + 1) * chunk_shape[0], shape[0]) |
| 87 | + y_start, y_end = y_i * chunk_shape[1], min((y_i + 1) * chunk_shape[1], shape[1]) |
| 88 | + z_start, z_end = z_i * chunk_shape[2], min((z_i + 1) * chunk_shape[2], shape[2]) |
| 89 | + |
| 90 | + # Read the chunk from the BioReader |
| 91 | + chunk = br.read(X=(x_start, x_end), Y=(y_start, y_end), Z=(z_start, z_end), C=(c,)) |
| 92 | + return np.expand_dims(chunk, axis=-1) |
| 93 | + |
| 94 | + |
| 95 | +def process(args): |
| 96 | + x_i, y_i, z_i, c = args |
| 97 | + rawdata = chunk = chunked_reader(x_i, y_i, z_i, c) |
| 98 | + start = [x_i * chunk_shape[0], y_i * chunk_shape[1], z_i * chunk_shape[2]] |
| 99 | + end = [ |
| 100 | + min((x_i + 1) * chunk_shape[0], shape[0]), |
| 101 | + min((y_i + 1) * chunk_shape[1], shape[1]), |
| 102 | + min((z_i + 1) * chunk_shape[2], shape[2]), |
66 | 103 | ]
|
67 |
| - x = dask.array.concatenate(arrays) |
68 |
| - print(x.shape, shape, np.prod(x.shape), np.prod(padded_chunk_shape)) |
69 |
| - # We need to reshape in iterations, |
70 |
| - # x.reshape(padded_chunk_shape) |
71 |
| - scales = [1, 1, 1, 1] |
72 |
| - dimensions = neuroglancer.CoordinateSpace( |
73 |
| - names=["x", "y", "z", "c"], units="um", scales=scales |
74 |
| - ) |
75 |
| - local_volume = neuroglancer.LocalVolume(x, dimensions) |
76 |
| - state.layers.append( |
77 |
| - name=name, |
78 |
| - layer=neuroglancer.ImageLayer( |
79 |
| - source=local_volume, |
80 |
| - volume_rendering_mode="ON", |
81 |
| - volume_rendering_depth_samples=400, |
82 |
| - ), |
83 |
| - shader=""" |
84 |
| -#uicontrol invlerp normalized |
85 |
| -void main() { |
86 |
| - float val = normalized(); |
87 |
| - emitRGBA(vec4(val, val, val, val)); |
88 |
| - } |
89 |
| - """, |
| 104 | + vol[start[0] : end[0], start[1] : end[1], start[2] : end[2], c] = rawdata |
| 105 | + touch( |
| 106 | + progress_dir |
| 107 | + / f"{start[0]}-{end[0]}_{start[1]}-{end[1]}_{start[2]}-{end[2]}_{c}.done" |
90 | 108 | )
|
91 |
| - state.layout = "3d" |
92 | 109 |
|
93 | 110 |
|
94 |
| -def launch_nglancer(): |
95 |
| - ap = argparse.ArgumentParser() |
96 |
| - neuroglancer.cli.add_server_arguments(ap) |
97 |
| - args = ap.parse_args() |
98 |
| - neuroglancer.cli.handle_server_arguments(args) |
99 |
| - viewer = neuroglancer.Viewer() |
100 |
| - return viewer |
| 111 | +# %% Try with a single chunk to see if it works |
| 112 | +# x_i, y_i, z_i = 0, 0, 0 |
| 113 | +# process((x_i, y_i, z_i, 0)) |
| 114 | + |
| 115 | +# %% Can't figure out the writing so do it with fake data |
| 116 | +# fake_data = np.random.randint(0, 2**16, size=chunk_size, dtype=np.uint16) |
| 117 | +# vol[0:256, 0:256, 0:128, 0] = fake_data |
| 118 | + |
| 119 | +coords = itertools.product( |
| 120 | + range(num_chunks_per_dim[0]), |
| 121 | + range(num_chunks_per_dim[1]), |
| 122 | + range(num_chunks_per_dim[2]), |
| 123 | + range(num_channels), |
| 124 | +) |
101 | 125 |
|
| 126 | +# %% Move the data across with multiple workers |
| 127 | +# max_workers = 8 |
102 | 128 |
|
103 |
| -def main(): |
104 |
| - viewer = launch_nglancer() |
105 |
| - with viewer.txn() as s: |
106 |
| - add_image_layer(s, FILEPATH, "image") |
107 |
| - webbrowser.open_new(viewer.get_viewer_url()) |
| 129 | +# with ProcessPoolExecutor(max_workers=max_workers) as executor: |
| 130 | +# executor.map(process, coords) |
108 | 131 |
|
| 132 | +# %% Move the data across with a single worker |
| 133 | +for coord in coords: |
| 134 | + process(coord) |
109 | 135 |
|
110 |
| -if __name__ == "__main__": |
111 |
| - main() |
| 136 | +# %% Serve the dataset to be used in neuroglancer |
| 137 | +vol.viewer(port=1337) |
0 commit comments