Processing Light-Sheet Microscopy Data Using Julia

Light-sheet microscopy is a technique that volumetrically scans fluorescently tagged biological specimens efficiently at high rates of speed. This data consists of three dimensional data collected over time across color channels and multiple views and results in a data flow of tens of gigabytes per second. I will discuss using Julia to meet the near acquisition demands of handling such data including 1) unpacking 12-bit integers, 2) compression, 3) chunking into subvolumes, and 4) storage.

To handle the high rate of data produced by multiple view light-sheet microscopy requires processing gigabtyes per seconds in order to overcome the limitations of microscope attached storage. Additionally since the data involves six dimensions, three spatial dimensions across time, wavelength, and view, there is a need to efficiently store the data in the form of localized subvolume blocks. I will discuss the experience of using Julia in combination with other tools in C++, Java, and LabView to handle data unpacking, compression, chunking and storage.
Microscopy data is collected by scientific cameras via frame grabbers which emit frames of 8, 12, or 16-bit unsigned integers depending on the configuration of the experiment. Collecting 12-bit data is a popular choice since it provides a large dynamic range relative to 8-bit data over thousands of integer values while allowing for relatively fast acquisition and moderate data throughput versus 16-bit data. Since 12-bit data is usually packed and not aligned to byte boundaries, processing the data posses an initial problem that complicates data handling and compression. Successfully addressing this problem allows for biological experiments under the microscope to be extended since the data requires less storage or transfer bandwidth.
Julia’s abstract array interfaces as well as its ability to access low level vector intrinsics through SIMD.jl that allow for effective initial handling of 12-bit integers with the help of the BitIntegers.jl package. This enables the data to be efficiently processed in part or in whole. Also, this provides opportunities to rearrange, or shuffle, the data to prepare it for compression. Through APIs such as TranscodingStreams.jl, Julia code can access a multitude of compression schemes which allow for rapid prototyping. Additionally, Julia’s ability to access native and asynchronous I/O allows for flexible storage through multiple formats which allow for automatic chunking and division into subvolumes. An example of this is the ImarisWriter.jl library produced as part of our efforts.
A key aspect of using Julia in such an environment is it’s ability to interoperate with multiple languages. Many of the tools that we needed to integrate have C or C++ interfaces, and Julia excelled at efficiently calling them without the need of much effort. Additionally, the ability to embed Julia in other applications such as LabView makes it easy to integrate the developed Julia code with existing solutions. Finally, much of the tooling for downstream image analysis exists in Java so the ability to use JavaCall.jl to integrate the near acquisition and image processing is another critical feature.
Overall, the experience of using Julia for near acquisition microscopy tasks has been successful and rewarding. We’ve been able to use Julia to address complex tasks in real time in order to meet demanding needs, and this is greatly facilitated by Julia’s flexibility of both high-level abstraction and low level efficient code compilation. Ultimately, it has been the ease of use factor which has allowed my colleagues and I to adopt Julia as a critical technology in our workflow.