Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update content for 0.4.0 release #91

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions docs/examples/sample_props.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"acquisition_dimensions": [],
"enable_multiscale": false,
"external_metadata_json": "",
"filename": "",
"uri": "",
"first_frame_id": 0,
"pixel_scale_um": [
0.0,
Expand Down Expand Up @@ -172,7 +172,7 @@
"acquisition_dimensions": [],
"enable_multiscale": false,
"external_metadata_json": "",
"filename": "",
"uri": "",
"first_frame_id": 0,
"pixel_scale_um": [
0.0,
Expand Down
8 changes: 4 additions & 4 deletions docs/get_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,15 @@ For now, we'll simply specify the output file name.
config.video[0].storage.identifier = dm.select(acquire.DeviceKind.Storage, "Tiff")

# what file or directory to write the data to
config.video[0].storage.settings.filename = "output1.tif"
config.video[0].storage.settings.uri = "output1.tif"
```


```python
config.video[1].storage.identifier = dm.select(acquire.DeviceKind.Storage, "Tiff")

# what file or directory to write the data to
config.video[1].storage.settings.filename = "output2.tif"
config.video[1].storage.settings.uri = "output2.tif"
```

Finally, let's specify how many frames to generate for each camera before stopping our simulated acquisition.
Expand Down Expand Up @@ -205,8 +205,8 @@ We'll load each Zarr dataset as a Dask array and inspect its dimensions, then we
from skimage.io import imread
import napari

data1 = imread(config.video[0].storage.settings.filename)
data2 = imread(config.video[1].storage.settings.filename)
data1 = imread(config.video[0].storage.settings.uri)
data2 = imread(config.video[1].storage.settings.uri)

viewer1 = napari.view_image(data1)

Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/setup_acquisition/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ config.video[0].camera.settings.pixel_type = acquire.SampleType.U16
## Configure `Storage`
`Storage` objects have 2 attributes, `settings`, a `StorageProperties` object, and an optional attribute `identifier`, which is an instance of the `DeviceIdentifier` class described above.

`StorageProperties` has 2 attributes `external_metadata_json` and `filename` which are strings of the filename or filetree of the output metadata in JSON format and image data in whatever format corresponds to the selected storage device, respectively. `first_frame_id` is an integer ID that corresponds to the first frame of the current acquisition and is typically 0. `pixel_scale_um` is the camera pixel size in microns. `acquisition_dimensions` is a list of `StorageDimension`, one for each acquisition dimension, ordered from fastest changing to slowest changing. For more information on using the `StorageDimension` class, check out [Chunking Data for Zarr Storage](../zarr/chunked.md). `enable_multiscale` is a boolean used to specify if the data should be saved as an image pyramid. See the [Multiscale tutorial](../zarr/multiscale.md) for more information.
`StorageProperties` has 2 attributes `external_metadata_json` and `uri` which are strings of the filename or filetree of the output metadata in JSON format and image data in whatever format corresponds to the selected storage device, respectively. `first_frame_id` is an integer ID that corresponds to the first frame of the current acquisition and is typically 0. `pixel_scale_um` is the camera pixel size in microns. `acquisition_dimensions` is a list of `StorageDimension`, one for each acquisition dimension, ordered from fastest changing to slowest changing. For more information on using the `StorageDimension` class, check out [Chunking Data for Zarr Storage](../zarr/chunked.md). `enable_multiscale` is a boolean used to specify if the data should be saved as an image pyramid. See the [Multiscale tutorial](../zarr/multiscale.md) for more information.

We'll specify the name of the output image file below.

```python
# Set the output file to out.tiff
config.video[0].storage.settings.filename = "out.tiff"
config.video[0].storage.settings.uri = "out.tiff"
```

# Update Configuration Settings
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/setup_acquisition/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ config.video[0].camera.identifier = runtime.device_manager().select(acquire.Devi
config.video[0].storage.identifier = runtime.device_manager().select(acquire.DeviceKind.Storage, "Zarr")

# Set the output file to out.zarr
config.video[0].storage.settings.filename = "out.zarr"
config.video[0].storage.settings.uri = "out.zarr"
```

In either case, we can update the configuration settings using:
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/setup_acquisition/start_stop.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ runtime = acquire.Runtime()
config = acquire.setup(runtime, "simulated: radial sin", "Tiff")

# Specify settings
config.video[0].storage.settings.filename == "out.tif"
config.video[0].storage.settings.uri == "out.tif"
config.video[0].camera.settings.shape = (192, 108)
config.video[0].camera.settings.exposure_time_us = 10e4
config.video[0].max_frame_count = 10
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorials/setup_acquisition/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ Zarr has additional capabilities relative to the basic storage devices, namely _

- **trash** - Writes nothing. Discards incoming data. Useful for live streaming applications.

- **tiff-json** - Stores the video stream in a [bigtiff](http://bigtiff.org/), and stores metadata in a `JSON` file. Both are located in a folder identified by the `filename` property.
- **tiff-json** - Stores the video stream in a [bigtiff](http://bigtiff.org/), and stores metadata in a `JSON` file. Both are located in a folder identified by the `uri` property.

- **Zarr** - Streams data to a [Zarr V2](https://zarr.readthedocs.io/en/stable/spec/v2.html) file with associated metadata.

- **ZarrBlosc1ZstdByteShuffle** - Streams compressed data (_zstd_ codec) to a [Zarr V2](https://zarr.readthedocs.io/en/stable/spec/v2.html) file with associated metadata.

- **ZarrBlosc1Lz4ByteShuffle** - Streams compressed data (_lz4_ codec) to a [Zarr V2](https://zarr.readthedocs.io/en/stable/spec/v2.html) file with associated metadata.

- - **ZarrV3** - Streams data to a [Zarr V3](https://zarr-specs.readthedocs.io/en/latest/specs.html) file with associated metadata.
- **ZarrV3** - Streams data to a [Zarr V3](https://zarr-specs.readthedocs.io/en/latest/specs.html) file with associated metadata.

- **ZarrV3Blosc1ZstdByteShuffle** - Streams compressed data (_zstd_ codec) to a [Zarr V3](https://zarr-specs.readthedocs.io/en/latest/specs.html) file with associated metadata.

Expand All @@ -71,7 +71,7 @@ config = runtime.get_configuration()
config.video[0].storage.identifier = dm.select(acquire.DeviceKind.Storage, "tiff")

# Set the data filename to out.tif in your current directory (provide the whole filetree to save to a different directory)
config.video[0].storage.settings.filename = "out.tif"
config.video[0].storage.settings.uri = "out.tif"
```

Before proceeding, complete the `Camera` setup and call `set_configuration` to save those new configuration settings.
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/setup_acquisition/trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ config.video[0].camera.settings.output_triggers.exposure = acquire.Trigger(

```python
config.video[0].storage.identifier = runtime.device_manager().select(acquire.DeviceKind.Storage,'zarr')
config.video[0].storage.settings.filename="out.zarr"
config.video[0].storage.settings.uri = "out.zarr"
```

## Save configuration
Expand Down
169 changes: 169 additions & 0 deletions docs/tutorials/zarr/chunked.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Configuring Zarr storage with chunking

This tutorial will provide an example of writing chunked data to a Zarr storage device.

Zarr has additional capabilities relative to the basic storage devices, namely _chunking_, _sharding_ (in the case of Zarr V3)_, _compression_, and _multiscale storage_.
To enable _chunking_, in the `StorageDimension` class, set the `chunk_size_px` attribute, which is size of a chunk along this dimension in pixels, to a number greater than 1 for each acquisition dimension.
You can learn more about the Zarr capabilities in Acquire [here](https://github.com/acquire-project/acquire-driver-zarr).

## Configure the acquisition
To start, we'll create a `Runtime` object and configure the streaming process, selecting `Zarr` as the storage device to enable chunking the data.

```python
import acquire

# Initialize a Runtime object
runtime = acquire.Runtime()

# Initialize the device manager
dm = runtime.device_manager()

# Grab the current configuration
config = runtime.get_configuration()

# Select the radial sine simulated camera as the video source
config.video[0].camera.identifier = dm.select(acquire.DeviceKind.Camera, "simulated: radial sin")

# Use a storage device that supports chunking, in this case, Zarr
config.video[0].storage.identifier = dm.select(acquire.DeviceKind.Storage, "Zarr")

# Delay between each frame
config.video[0].camera.settings.exposure_time_us = 7e4 # 70 ms

# Size of image region of interest on the camera (x, y)
config.video[0].camera.settings.shape = (1920, 1080)

# Specify the pixel datatype as uint8
config.video[0].camera.settings.pixel_type = acquire.SampleType.U8

# Set the output file to out.zarr
config.video[0].storage.settings.uri = "out.zarr"
```

### Storage dimensions

Because Zarr supports n-dimensional arrays, we need to describe how the data we stream should be interpreted.
We do this by specifying *storage dimensions*, which correspond to the dimensionality of the output array.

Acquire requires at least 3 dimensions: frame width, frame height, and an append dimension.
The first 2 dimensions are required and may be followed by optional internal dimensions.
The final "append" dimension is also required.

Each dimension must have a type, for example, space, channel, time, or other.
This tutorial will use 5 dimensions following the [OME-NGFF specification](https://ngff.openmicroscopy.org/latest/#multiscale-md).
That is, we will use TCZYX order, with T corresponding to time, C to channel, Z to depth, Y to height, and X to width.

```python
dimension_x = acquire.StorageDimension(
name="x",
kind="Space",
array_size_px=1920,
chunk_size_px=960
)

dimension_y = acquire.StorageDimension(
name="y",
kind="Space",
array_size_px=1080,
chunk_size_px=540
)

dimension_z = acquire.StorageDimension(
name="z",
kind="Space",
array_size_px=10,
chunk_size_px=5
)

dimension_c = acquire.StorageDimension(
name="c",
kind="Channel",
array_size_px=3,
chunk_size_px=1
)

dimension_t = acquire.StorageDimension(
name="t",
kind="Time",
array_size_px=0,
chunk_size_px=10
)

config.video[0].storage.settings.acquisition_dimensions = [
dimension_x,
dimension_y,
dimension_z,
dimension_c,
dimension_t
]
```

Notice that each `StorageDimension` object has several attributes.
- `name` is the name of the dimension. It is used to identify the dimension in the output array and must be unique.
- `kind` is the type of dimension. It can be `Space`, `Channel`, `Time`, or `Other`.
- `array_size_px` is the size of the dimension in pixels. It is the total size of the dimension.
- `chunk_size_px` is the size of the chunks in pixels. It is the size of the chunks in which the data will be stored.

There is an additional field, `shard_size_chunks`, which is used to specify the number of chunks per shard, but it is
only used in Zarr V3, which we will discuss in a future tutorial.

The order in which we specify the dimensions is important.
The order of the dimensions in the `acquisition_dimensions` list determines the order of the dimensions in the output array.
In this case, the order is `x`, `y`, `z`, `c`, `t`, which corresponds to the order `TCZYX`.

Notice that the first two dimensions' `array_size_px` is the same as the camera's shape.
This is because the first two dimensions correspond to the spatial dimensions of the camera.

Another thing to notice is that the final dimension, `t`, has an `array_size_px` of 0.
This is because the size of the append dimension is not known in advance.
At most, we can say that the size of the append dimension is no larger than the number of frames we want to collect, but
because acquisition may terminate at any point before reaching the maximum frame count, we set the `array_size_px` to 0.

The number of frames to collect will now depend on the sizes of the internal dimensions.
For our example, to fill just one chunk of the `c` dimension, we will need to collect
`dimension_z.array_size_px * dimension_t.chunk_size_px` frames, or in other words, 10 frames.
To fill a single chunk of the `t` dimension, we will need to collect
`dimension_z.array_size_px * dimension_c.array_size_px * dimension_t.chunk_size_px` frames, or in other words, 300
frames.

Below we'll configure the max frame count and update all settings with the `set_configuration` method.

```python
config.video[0].max_frame_count = dimension_z.array_size_px * dimension_c.array_size_px * dimension_t.chunk_size_px # 300

config = runtime.set_configuration(config)
```

## Collect and Inspect the Data

```python
# collect data
runtime.start()
runtime.stop()
```

You can inspect the Zarr file directory to check that the data saved as expected. Alternatively, you can inspect the data programmatically with:

```python
# Utilize the zarr library to open the data
import zarr

# create a zarr Group object
group = zarr.open(config.video[0].storage.settings.uri)

# check for the expected # of directories in the zarr container
assert len(group) == 1

# inspect the characteristics of the data
print(group["0"])
```

The output will be:
```
<zarr.core.Array '/0' (10, 3, 10, 1080, 1920) uint8>
```

The overall array shape is (10, 3, 10, 1080, 1920), corresponding to 10 time points, 3 channels, 10 planes, and a height and width of 1080
and 1920, respectively, for each acquired frame.

[Download this tutorial as a Python script](chunked.py){ .md-button .md-button-center }
4 changes: 2 additions & 2 deletions docs/tutorials/zarr/compressed.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ config.video[0].camera.settings.shape = (1024, 768)
config.video[0].max_frame_count = 100 # collect 100 frames

# Set the output location to out.zarr
config.video[0].storage.settings.filename = "out.zarr"
config.video[0].storage.settings.uri = "out.zarr"

# Update the configuration with the chosen parameters
config = runtime.set_configuration(config)
Expand All @@ -60,7 +60,7 @@ We'll use the [zarr-python package](https://zarr.readthedocs.io/en/stable/) to r
import zarr

# load from Zarr
compressed = zarr.open(config.video[0].storage.settings.filename)
compressed = zarr.open(config.video[0].storage.settings.uri)
```

We'll print some of the data properties to illustrate how the data was compressed. Since we have not enabled [multiscale](multiscale.md) output, `out.zarr` will only have one top level array`"0"`.
Expand Down
Loading