Data adapters

Writing an adapter

Each scanner or data source, has different formats for storing projection data, specifying the (acquisition) geometry, and different requirements on data preprocessing.

An adapter generates and sends three types of standardized packets from the specific scanner data:

  • Scan settings: tomop.scan_settings_packet
  • Geometry: geometry_specification_packet for the volume geometry, and e.g. tomop.parallel_beam_geometry_packet for the acquisition geometry.
  • Projection data: tomop.projection_packet

This gets sent (one way) to the projection server which is ‘scanner agnostic’. To support a different scanner (or microscope, beamline, dataset), you can write a straightforward data adapter in the form of simple Python script. Example adapters can be found in the examples directory of TomoPackets.

To implement an adapter, we have to send the three types of packets to a listening reconstructor. To send these packets, we can use a tomop.publisher object. An example is provided below::

import tomop
import numpy as np
import argparse


def push_zero_projections(resolution, host="localhost", port=5558):
    m = resolution
    proj_count, rows, cols = m, m, m
    scene_id = 0

    pub = tomop.publisher(host, port)

    # We let the server know we will send one dark field, and one flat field
    num_darks, num_flats = 1, 1
    packet_scan_settings = tomop.scan_settings_packet(scene_id, num_darks,
                                                      num_flats, False)
    pub.send(packet_scan_settings)

    # Initialize volume and acquisition geometry
    packet_vol_geom = tomop.geometry_specification_packet(
        scene_id, [0, 0, 0], [1, 1, 1])
    pub.send(packet_vol_geom)

    angles = np.linspace(0, np.pi, proj_count, endpoint=False)
    packet_geometry = tomop.parallel_beam_geometry_packet(
        scene_id, rows, cols, proj_count, angles)
    pub.send(packet_geometry)

    # Send dark(s) and flat(s)
    dark = np.zeros((rows, cols), dtype=np.float32).ravel()
    packet_dark = tomop.projection_packet(0, 0, [rows, cols], dark)
    pub.send(packet_dark)

    flat = np.ones((rows, cols), dtype=np.float32).ravel()
    packet_flat = tomop.projection_packet(1, 0, [rows, cols], flat)
    pub.send(packet_flat)

    # Create and send projection data consisting of zeros
    proj_data = np.zeros((proj_count, rows, cols))
    for i in np.arange(0, proj_count):
        packet_proj = tomop.projection_packet(2, i, [rows, cols],
                                              proj_data[i, :, :].ravel())
        pub.send(packet_proj)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Push a data set consisting of zeros to Slicerecon.')

    parser.add_argument('--host',
                        default="localhost",
                        help='the projection server host')
    parser.add_argument('--port',
                        type=int,
                        default=5558,
                        help='the projection server port')

    parser.add_argument(
        '--resolution',
        type=int,
        default=512,
        help='the number of detector rows, detector columns, and angles')

    args = parser.parse_args()

    push_zero_projections(args.resolution, host=args.host, port=args.port)