Ceed data analysis

Reads a combined Ceed and MCS H5 data file that has been merged with merge_data, using CeedDataReader.

During an experiment Ceed saves its projector data to a NIX H5 file and MCS saves its electrode data to a proprietary file. After the experiment, one exports the MCS electrode data to an H5 file through the MCS software. Then, following merge_data, one creates a new Ceed based file that also contains the electrode data as well as an alignment between the Ceed video frames and their temporal location in the MCS data. CeedDataReader can be used to load this combined file.

See the examples directory in the Ceed Github repository and Example Code for complete worked out examples using the data.

Loading data

CeedDataReader is used to read the MCS electrode data and Ceed data. A typical example is:

# this internally opens the file and ensures it's closed when the with
# block exits, even if there was an exception
with CeedDataReader('data.h5') as f:
    # load the electrode data
    f.load_mcs_data()

    print('Found experiments: {}. Number of saved images are: {}'.format(
        f.experiments_in_file, f.num_images_in_file))

    # load a specific experiment
    f.load_experiment(0)
    # do something with the experiment data. E.g. save the image
    f.save_image('image.bmp', f.experiment_cam_image)

The overall steps are to create the CeedDataReader and enter its with block. This internally calls CeedDataReader.open_h5() and CeedDataReader.close_h5().

Next, you can load the MCS electrode data and metadata using CeedDataReader.load_mcs_data(). This loads it into memory as sets the instance properties with the data (see CeedDataReader.electrodes_data and CeedDataReader.electrodes_metadata).

Finally, you can load the Ceed data as well as the Ceed-MCS alignment for a specific experiment using CeedDataReader.load_experiment(). This sets further class properties and allows you to use instance methods that require the Ceed data. E.g. see CeedDataReader.shapes_intensity, CeedDataReader.shapes_intensity_rendered_gpu_rate, CeedDataReader.electrode_intensity_alignment, and CeedDataReader.electrode_intensity_alignment_gpu_rate.

Loading images

If you have an image file that you would like to use during analysis, you can read it using ffpyplayer.pic.ImageLoader. E.g.:

>>> from ffpyplayer.pic import ImageLoader
>>> images = [m for m in ImageLoader('filename.bmp')]
>>> images
Out[4]: [(<ffpyplayer.pic.Image at 0x232dceb8a68>, 0.0)]
>>> images[0][0]
Out[5]: <ffpyplayer.pic.Image at 0x232dceb8a68>

It returns a list of images and the timestamp of the images, e.g. in case it is a gif. If the file contains only one image, just access the first.

Experiment movie

CeedDataReader can create a movie that replays the experiment and the intensity of the shapes. It can also draw the tissue image, MEA electrode grid, and the electrode voltage.

See CeedDataReader.generate_movie() for details. E.g.:

with CeedDataReader('data.h5') as f:
    f.load_mcs_data()
    f.load_experiment(0)
    f.generate_movie(
        'video.mp4', lum=3, canvas_size_hint=(2, 1),
        speed=1., end=1,
        paint_funcs=[
            f.paint_background_image(
                f.experiment_cam_image,
                transform_matrix=f.view_controller.cam_transform),
            f.show_mea_outline(f.view_controller.mea_transform),
            f.paint_electrodes_data_callbacks(
                f.get_electrode_names(), draw_pos_hint=(1, 0),
                volt_axis=50)]
    )

This creates a movie of the first second of the experiment at three times the intensity, containing the background image, the MEA grid and the electrode data as well as the shapes.

class ceed.analysis.CeedDataReader(filename, **kwargs)

Bases: object

Loads the data from an experiment.

See analysis for details.

experiments_in_file: List[str] = []

After open_h5(), it is a list of the experiment names found in the file. The experiments listed can be opened with load_experiment().

num_images_in_file: int = 0

After open_h5(), it is the number of camera images found in the file. Images can be opened by number with get_image_from_file().

electrodes_data: Dict[str, numpy.ndarray] = {}

After load_mcs_data(), it is a mapping whose keys is an electrode name and whose values is a 1D array with the raw electrode data from MCS.

Use get_electrode_offset_scale() to convert the raw data to properly scaled volt.

The electrode data is typically sampled at a high sampling rate and contains multiple Ceed experiments. See electrode_intensity_alignment.

electrodes_metadata: Dict[str, Dict[str, Any]] = {}

Similar to electrodes_data, but instead the values is a dict of the electrode metadata such as sampling rate etc.

electrode_dig_data: Optional[numpy.ndarray] = None

After load_mcs_data(), if present in the file, it’s an 1D array containing the digital input data as sampled by the MCS system.

This data was used when aligning the Ceed data to the MCS electrode data in merge_data and it contains the alignment pattern sent by Ceed to MCS over the hardware link. There should be the same number of samples as in the electrodes_data for any electrode.

analog_data: Dict[str, numpy.ndarray] = {}

After load_mcs_data(), it is a mapping whose keys is an analog channel name and whose values is a 1D array with the raw analog data from MCS.

Use get_analog_offset_scale() to convert the raw data to properly scaled volt.

The analog data is typically sampled at a high sampling rate and contains multiple Ceed experiments. See electrode_intensity_alignment.

analog_metadata: Dict[str, Dict[str, Any]] = {}

Similar to analog_data, but instead the values is a dict of the analog metadata such as sampling rate etc.

electrode_intensity_alignment: Optional[numpy.ndarray] = None

After load_experiment() it is a 1D array, mapping the ceed frames (shapes_intensity_rendered) to the MCS electrodes_data by index. See electrode_intensity_alignment_gpu_rate for the indices, one per GPU frame.

If no alignment was computed for this file (e.g. if it’s not a “merged” file), this is None.

Ceed refreshes the projector at a low rate, e.g. 120Hz. MCS records data at a very high rate, e.g. 10kHz. Consequently, each Ceed frame is displayed during multiple electrode samples. So, starting from the first frame for an experiment, we can write the it starts at index k in electrodes_data, frame 2 at index m, frame 3 at index n, etc. Then e.g. frame one is displayed during samples [k, m), frame two at [m, n), etc.

electrode_intensity_alignment are these start indices, one for each Ceed frame and therefore its length is the number of Ceed frames displayed during the experiment.

Minor detail; if the experiment ran in QUAD4X or QUAD12X mode then each main frame is composed of sub-frames (e.g. 4 or 12 frames). The start index of each sub-frame was estimated by splitting the main frames into the given number of sub-frames. Because we only know the exact index of each main frame.

As explained in Frame rate and dropped frames, Ceed will sometimes drop a frame to compensate when a frame is displayed too long. Therefore, the length of electrode_intensity_alignment may be shorter than the the length of the values in shapes_intensity, because this contains only indices for frames that were rendered. So it should be the same length as the items in shapes_intensity_rendered.

If Ceed drops a frame, it means a previous frame was displayed for longer than one frame duration. Consequently, the distance between values in this array may not be regularly spaced. E.g. in the example above, m - n could be twice or three times as large as k - m. CeedDataReader.compute_long_and_skipped_frames() helps analyze to find these dropped and long frames.

property electrode_intensity_alignment_gpu_rate: Optional[numpy.ndarray]

Like electrode_intensity_alignment, except that indices corresponding to long frames are split into individual frames, rather than one sample per long frame.

This means that each frame index corresponds to a single GPU frame and is the same size as shapes_intensity_rendered_gpu_rate.

E.g. If Ceed projected 5 frames - 0, 1, 2, 3, and 4, but frame 1 was displayed for 2 frames, and Ceed dropped its frame 3 to compensate, then Ceed projected the following frames at the GPU rate: 0, 1, 1, 2, 4.

In this example, electrode_intensity_alignment would hold 4 values indicating the indices in MCS of frames 0, 1, 3, 4. That’s because from MCS’ pov, only 4 frames were emitted by Ceed because frames 2 and 3 were the same because the GPU didn’t render frame 3.

electrode_intensity_alignment_gpu_rate however will actually contain 5 values because we estimate where in the MCS data frame 2 would have occurred and add that index.

electrode_intensity_alignment_gpu_rate is None when electrode_intensity_alignment is None.

Warning

If there are overwhelmingly too many long frames (i.e. the approximate median frame was displayed longer compared to one expected GPU period) it is not accurate.

shapes_intensity: Dict[str, numpy.ndarray] = {}

After load_experiment(), it is a mapping whose keys are the names of the shapes drawn in ceed for this experiment and whose values is a 2D array containing the intensity value of the shape for each Ceed frame, including those dropped.

This is sampled at the projector frame rate, not the MEA sampling rate and should have the same size or larger as the shape of electrode_intensity_alignment.

Each shape is a 2D array. Each row corresponds to a single frame and has 4 columns indicating the red, green, blue, and alpha intensity of that shape for that frame. They are values between 0-1. Alpha is the transparency with 1 being completely opaque.

As explained in electrode_intensity_alignment, Ceed will drop frames to compensate for long frames. However, Ceed will still compute and record the shapes’ intensity for dropped frames. shapes_intensity contains the intensity for all frames, including the dropped ones and may therefore be larger than the length of electrode_intensity_alignment.

shapes_intensity_rendered contains only the frames that were rendered and is therefore the same size as electrode_intensity_alignment. It is generated by indexing shapes_intensity using rendered_frames. shapes_intensity_rendered_gpu_rate additionally, breaks the longer frames into individual frames so it’s generally the same size as shapes_intensity.

rendered_frames_long additionally indicates the displayed frames that were displayed for more than one frame duration, potentially causing Ceed to drop frames.

property shapes_intensity_rendered: Dict[str, numpy.ndarray]

After load_experiment(), it is a mapping whose keys are the names of the shapes drawn in ceed for this experiment and whose values is a 2D array containing the intensity value of the shape for each Ceed frame, not including the dropped frames. See also shapes_intensity_rendered_gpu_rate for the data spaced exactly one per GPU frame.

Unlike shapes_intensity, it only contains the frames that were displayed and is the same size as electrode_intensity_alignment. See those properties docs’ for details.

Long frames, frames displayed for more than one GPU frame period duration resulting in a subsequent dropped frame, is listed only once for each long frame (i.e. the array is not evenly sampled at the GPU refresh rate if there are long frames because the duration of each item may be longer than the GPU period).

property shapes_intensity_rendered_gpu_rate: Optional[Dict[str, numpy.ndarray]]

Like shapes_intensity_rendered, except that samples corresponding to long frames are split into individual samples, rather than one sample per long frame.

This means that each frame sample corresponds to a single GPU frame and is the same size as electrode_intensity_alignment_gpu_rate.

E.g. If Ceed projected 5 frames - 0, 1, 2, 3, and 4, but frame 1 was displayed for 2 frames, and Ceed dropped its frame 3 to compensate, then Ceed projected the following intensity frame values at the GPU rate: 0, 1, 1, 2, 4.

In this example, shapes_intensity shapes would hold 5 values corresponding to frames 0, 1, 2, 3, 4, shapes_intensity_rendered shapes would hold 4 values corresponding to frames 0, 1, 2, 4, and shapes_intensity_rendered_gpu_rate shapes would hold 5 values corresponding to frames 0, 1, 1, 2, 4. rendered_frames_long would be an array with values 1, 2, 1, 1.

shapes_intensity_rendered_gpu_rate is None when electrode_intensity_alignment is None.

Warning

If there are overwhelmingly too many long frames (i.e. the approximate median frame was displayed longer compared to one expected GPU period) it is not accurate.

rendered_frames: numpy.ndarray = None

After load_experiment(), it is a 1D logical array of the same size as the values in shapes_intensity and it indicates which frames of those frames were rendered.

shapes_intensity_rendered is internally generated by indexing shapes_intensity with rendered_frames.

If MCS was stopped recording data while a Ceed experiment was ongoing, those remaining frames are marked as not rendered because they are not in the MCS data, even though they may actually have been projected.

property rendered_frames_long: Optional[numpy.ndarray]

After load_experiment() it is a 1D array of the same size as shapes_intensity_rendered indicating the number of GPU periods for which the corresponding frame was rendered. If it’s larger than one it means the frame was long. See shapes_intensity and shapes_intensity_rendered_gpu_rate.

rendered_frames_long is None when electrode_intensity_alignment is None.

Warning

If there are overwhelmingly too many long frames (i.e. the approximate median frame was displayed longer compared to one expected GPU period) it is not accurate.

led_state: numpy.ndarray = None

After load_experiment(), it is the state of each of the 3 RGB projector LEDs.

The projector lets us set it them using LED_mode and this records it when set during the experiment.

It is a 2D array of Tx4:

  • where each row corresponds to each time the projector is set during the experiment,

  • the first column is the frame number when it was set as indexed into the values in shapes_intensity,

  • the remaining 3 columns is the state of the R, G, and B LED as set by the projector.

event_data: List[Tuple[int, int, str, list]] = []

After load_experiment(), it is a list of all the events logged during the experiment.

During an experiment, functions and stages can log arbitrary events to the data file. Each event contains some identifying info as well an arbitrary data payload. The identifying info is a 3-tuple of (frame, ceed_id, name):

frame: int

This is the GPU frame number at which the event was logged. For events logged after the last displayed frame (e.g. when the experiment ends), it’ll be the one above the largest frame number. The values can be between zero and the length of shapes_intensity_rendered_gpu_rate, inclusive.

ceed_id: int

The ceed_id of the object doing the logging. set_ceed_id() is automatically called on the root stage before an experiment is run. It sets all sub-stage’s and all their function’s ceed_id, each to a number that is unique among all of them (it’s only unique per stages/functions used in that experiment, not globally or even between experiments). You can lookup the ceed_id of a stage/function to locate it in the logs using experiment_stage.

name: str

The event type being logged. It is a unique string, specific to the event. See below for the list of currently built-in events.

The payload is a list of any of the native python data types (e.g. int, list, str etc.) and simple numpy arrays. It shouldn’t be too big otherwise frames may be dropped if it takes too long to process.

FunctionFactoryBase and StageFactoryBase each define a on_data_event dispatchable event that when dispatched with arguments will be automatically logged during the experiment. Following is the list of built in events dispatched automatically.

Function/stage events
start:

Dispatched by each stage and function when they are initially started. Its payload is a 3-item list of the current loop iteration (loop_count and loop_tree_count, nominally zero) and the global ceed time (with higher resolution than GPU frames, so any real number) when it started.

end:

Dispatched by each stage and function when they are ended. Its payload is a 3-item list of the final loop iteration number (loop_count and loop_tree_count) and the global ceed time when it ended. This event may not be logged if e.g. the user suddenly ends the stage so it never reached its end.

loop_start:

Dispatched by each stage and function when each loop iteration starts, including the first loop iteration. Its payload is a 3-item list of the current loop iteration number (loop_count and loop_tree_count) and the global ceed time when it started.

loop_end:

Dispatched by each stage and function when each loop iteration ends, including the last loop iteration. Its payload is a 3-item list of the loop iteration number that just ended (loop_count and loop_tree_count) and the global ceed time when it ended. This event may not be logged if e.g. the user suddenly ends the stage so the loop never reached its end.

Plugins can similarly log events by dispatching to the on_data_event. E.g. stage_factory.dispatch('on_data_event', stage.ceed_id, 'drug', .4, 'h2o') could be dispatched so that event drug is logged for the current Ceed frame labeled as the specific stage, including a payload of [.4, 'h2o']. The could e.g. mean that .4 ml of the drug was injected at this frame.

view_controller: ceed.view.controller.ViewControllerBase = None

After load_experiment(), it is the ViewControllerBase instance configured to the same settings as used during the loaded_experiment.

data_serializer: ceed.storage.controller.DataSerializerBase = None

After load_experiment(), it is the DataSerializerBase instance configured to the same settings as used during the loaded_experiment.

function_factory: ceed.function.FunctionFactoryBase = None

After load_experiment(), it is the FunctionFactoryBase instance configured to the same settings as used during the loaded_experiment.

stage_factory: ceed.stage.StageFactoryBase = None

After load_experiment(), it is the StageFactoryBase instance configured to the same settings as used during the loaded_experiment.

shape_factory: ceed.shape.CeedPaintCanvasBehavior = None

After load_experiment(), it is the CeedPaintCanvasBehavior instance configured to the same settings as used during the loaded_experiment.

experiment_stage_name: str = ''

After load_experiment(), it is the name of the stage among the stages in stage_factory that was used to run the currently loaded_experiment.

experiment_stage: ceed.stage.CeedStage = None

After load_experiment(), it is the CeedStage instance that is configured exactly like the stage used to run the experiment. All the properties, sub-stages, and functions match that original stage.

experiment_notes: str = ''

The experiment notes recorded for the currently loaded_experiment in the GUI.

experiment_start_time: float = 0.0

The time.time timestamp when the currently loaded_experiment started.

experiment_cam_image: Optional[ffpyplayer.pic.Image] = None

The camera image stored for the currently loaded_experiment, if there was a background image present in the GUI when it was started.

See also save_image().

loaded_experiment: Optional[str] = None

After load_experiment(), it is the experiment number of the currently loaded experiment.

The ceed h5 file contains data for multiple experiments. load_experiment() must be called to load a particular experiment; this stores the experiment number currently loaded.

ceed_version: str = ''

After open_h5(), it is the version of ceed that created the file.

external_function_plugin_package: str = ''

After open_h5(), it contains the external function plugin package containing any additional functions, if it was specified during the experiment.

See external_function_plugin_package.

external_stage_plugin_package: str = ''

After open_h5(), it contains the external stage plugin package containing any additional stages, if it was specified during the experiment.

See external_stage_plugin_package

app_logs: str = ''

After open_h5(), it contains the overall app logs recorded during the experiments.

app_notes: str = ''

After open_h5(), it contains the overall notes stored in the file, including those added during merge_data().

app_config: Dict[str, Any] = {}

After open_h5(), it is the last ceed application configuration objects as saved in the file.

These options are similarly stored in each experiment containing the configuration options used at the time the experiment was executed. These are stored in function_factory, stage_factory etc. and is specific to the experiment. app_config are the Ceed options as they were when the file was last closed/saved.

filename: str = ''

The full filename path of the Ceed H5 file opened.

close_h5()

Closes the data file if it has been opened.

This is implicitly called when using the with syntax shown in open_h5(), which is a safer way of closing the file because it is called even if there’s an exception.

open_h5()

Opens the data file in read only mode and loads the application config.

This is implicitly called when using the with syntax below, which is a safer way of opening the file because close_h5() is called even if there’s an exception.

To load a specific experiment data after the file is open, use load_experiment().

E.g.:

>>> reader = CeedDataReader(filename='data.h5')
>>> reader.open_h5()
>>> print(reader.experiments_in_file)
>>> reader.load_experiment(0)
>>> reader.close_h5()

Or in a safer way:

>>> with CeedDataReader(filename='data.h5') as reader:
...     print(reader.experiments_in_file)
...     reader.load_experiment(0)

This will call open_h5() when entring the with block and close_h5() when exiting, even if there’s an exception raised within the block.

dump_plugin_sources(plugin_type: str, target_root: Union[str, pathlib.Path])

Dumps the source code of all the registered function or stage plugins (depending on plugin_type) that was saved to the data file.

See ceed.function.register_external_functions(), ceed.function.register_all_functions(), ceed.stage.register_external_stages(), and ceed.stage.register_all_stages().

Parameters
  • plugin_type – either 'function' or 'stage' to indicate which plugin to dump.

  • target_root – the root directory where to dump the source code.

get_electrode_names() List[List[str]]

After load_experiment(), it returns a 2D list (list of lists) containing the 2D grid of electrode names used in the experiment as derived from the mea_num_cols and other mea options.

load_application_data()

Loads all the application configuration from the data file and stores it in the instance properties.

This is automatically called by open_h5().

load_experiment(experiment: Union[int, str])

Loads the data for a specific experiment and populates the instance properties with the data.

If a previous experiment was loaded, it replaces the properties with the new data. E.g.:

>>> with CeedDataReader(filename='data.h5') as reader:
...     print(reader.experiments_in_file)
...     reader.load_experiment(0)
...     print(reader.experiment_notes)
...     reader.load_experiment(2)
load_mcs_data()

Loads the MCS electrode data and stores them in the instance properties.

See e.g. electrodes_data.

It should be called only once after opening the file. E.g.:

>>> with CeedDataReader(filename='data.h5') as reader:
...     reader.load_mcs_data()
...     reader.load_experiment(0)
...     print(reader.electrodes_metadata)
...     print(reader.experiment_notes)
format_event_data(event: Optional[str] = None, ceed_obj: Optional[ceed.utils.CeedWithID] = None, ceed_name: Optional[str] = None, ceed_id: Optional[int] = None) list

Returns a flattened and optionally filtered version of the event_data.

Unlike event_data where each event item is a 4-tuple of (frame, ceed_id, name, payload) and payload is a list of event data. Each item in the returned list is a list whose first three items are [frame, obj, name, ...] and the remaining items is the payload. That is the payload items are appended to the list.

Notice how we return obj, rather than ceed_id. The obj is the stage or function object that emitted the event.

Parameters
  • event – If not None, the name of the event to such that only these events will be returned. E.g. "loop_start".

  • ceed_obj – If not None, it’s an object such that only events generated by this object is returned. This could be e.g. a stage or function.

  • ceed_name – If not None, it’s a name such that only events generated by an object of this name is returned. This could be e.g. the name of a stage or function.

  • ceed_id – If not None, it’s the ceed_id such that only events generated by an object with this ceed id is returned.

E.g. if event_data is e.g. [(300, 1, 'start', [0, 1, 0.33])], then without any filtering this method returns [[300, func_obj, 'start', 0, 1, 0.33]].

get_image_from_file(image_num: int) Tuple[ffpyplayer.pic.Image, str, float]

After opening the file num_images_in_file contains the number of images the user manually saved to the file. This loads a specific image and its metadata.

See also save_image().

Parameters

image_num – The image number to return. Should be less than num_images_in_file.

Returns

A 3-tuple of (image, notes, save_time), where image is the image, notes is any user notes saved with the image, and save_time is the time.time when the image was saved.

save_image(filename: str, img: ffpyplayer.pic.Image, codec: str = 'bmp')

Saves the given image to disk.

Parameters
  • filename – The full path where to save the image.

  • img – The image to save.

  • codec – The codec with which to save. The default is bmp and the extension should then also be .bmp.

get_electrode_offset_scale(electrode: str) Tuple[float, float]

After load_mcs_data(), returns the conversion factors used to convert the raw integer data in electrodes_data to properly scaled micro-volt.

Parameters

electrode – The electrode name as stored in electrodes_data

Returns

A 2-tuple of (offset, scale) ` that can be used to convert. E.g. micro-volt = (input - offset) * scale.

get_analog_offset_scale(channel: str) Tuple[float, float]

After load_mcs_data(), returns the conversion factors used to convert the raw integer data in analog_data to properly scaled micro-volt.

Parameters

channel – The channel name as stored in analog_data

Returns

A 2-tuple of (offset, scale) ` that can be used to convert. E.g. micro-volt = (input - offset) * scale.

estimate_skipped_frames()

After load_experiment(), returns an estimate of the skipped and long frames.

It returns the same value as compute_long_and_skipped_frames, but specific to the current experiment.

static compute_long_and_skipped_frames(n_sub_frames: int, rendered_frames: numpy.ndarray, ceed_mcs_alignment: numpy.ndarray) Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray, int]

Returns a estimate of the Ceed frames that were displayed longer than one frame and the frames that were dropped.

ceed_mcs_alignment can be shorter than rendered_frames because the MCS data could have been stopped recording while Ceed was still projecting.

Parameters
  • n_sub_frames – The number of sub-frames for each Ceed frame (e.g. quad mode can be 4 or 12).

  • rendered_frames – The logical array indexing the rendered frames. It is the same as rendered_frames.

  • ceed_mcs_alignment – The Ceed-MCS alignment array. It is the same as electrode_intensity_alignment.

Returns

A 5-tuple of (mcs_long_frames, mcs_frame_len, ceed_skipped, ceed_skipped_main, largest_bad). mcs_long_frames is the frame indices that were longer than one. mcs_frame_len is the corresponding length of each long frame. ceed_skipped is the indices of the frames that were skipped. ceed_skipped_main is the indices of the main frames that were skipped. largest_bad is the maximum delay in terms of frames between Ceed repeating a frame because the CPU was too slow, until Ceed realized it needs to drop a frame to compensate. I.e. this is the largest number of bad frames Ceed ever displayed before correcting for each event.

paint_background_image(img: ffpyplayer.pic.Image, scale=None, translation=None, rotation=None, transform_matrix=None)

Uses partial_func() to return a function that can be passed to paint_funcs of generate_movie() which will draw the image in the background.

This can be used e.g. to display the tissue image and display the experiment shapes on it.

Parameters
  • img – The image to show.

  • scale – Optionally a scaling factor by which to upscale the image.

  • translation – Optionally a 2-tuple of a x, y translation by which to translate the image.

  • rotation – Optionally a rotation angle in degrees by which to rotate the image.

  • transform_matrix

    Optionally a transformation matrix stored as an array by which to apply a affine transform to the image.

    You can get the transformation used for the current experiment from cam_transform, using view_controller.

Returns

The function to pass to generate_movie().

E.g.:

>>> with CeedDataReader('data.h5') as f:
... f.load_mcs_data()
... f.load_experiment(0)
... paint_funcs = [
...     f.paint_background_image(
...         f.experiment_cam_image,
...         transform_matrix=f.view_controller.cam_transform)
... ]
... f.generate_movie('video.mp4', paint_funcs=paint_funcs)
paint_electrodes_data_callbacks(electrode_names, spacing=2, draw_size=(0, 0), draw_size_hint=(1, 1), draw_pos=(0, 0), draw_pos_hint=(None, None), volt_scale=1e-06, time_axis_s=1, volt_axis=100)

Uses partial_func() to return a function that can be passed to paint_funcs of generate_movie() which will draw the electrodes_data.

This can be used e.g. to display the electrode data for all electrodes while the movie is displaying the intensity of each of the shapes.

Parameters
  • electrode_names – List of names of electrodes (as keys in electrodes_data) to draw.

  • spacing – The pixel spacing by which to space them to each other.

  • draw_size – A optional 2-tuple of the size of the overall grid in which to draw the electrodes.

  • draw_size_hint – Similar to draw_size, but instead of absolute size, it’s the scaling factor relative to the projector screen size on which the Ceed shapes are drawn. E.g. (1, 1) means it’ll be the same size as the shapes region, and (1, 2) means it’ll be the same horizontally, but twice as large vertically.

  • draw_pos – A optional 2-tuple of the bottom left corner pos of the overall grid in which to draw the electrodes.

  • draw_pos_hint – Similar to draw_pos, but instead of absolute pos, it’s the scaling factor relative to the projector screen size on which the Ceed shapes are drawn. E.g. (1, 0) means it’ll be drawn to the right of the shapes region but at the same vertical height.

  • volt_scale – The scale in which the volts are given. E.g. 1e-6 means that the data is given in micro-volts (which is what MCS defaults to).

  • time_axis_s – The sub-sampling rate of the x-axis in seconds. E.g. 1 means that we display 1 sample for every second.

  • volt_axis – The plus/minus range of the y-axis to display, in units of volt_scale. E.g. 100 means it’ll display +/- 100uv.

Returns

The function to pass to generate_movie().

E.g.:

>>> with CeedDataReader('data.h5') as f:
... f.load_mcs_data()
... f.load_experiment(0)
... paint_funcs = [
...     f.paint_electrodes_data_callbacks(
...     f.get_electrode_names(), draw_pos_hint=(1, 0),
...     volt_axis=50)
... ]
... f.generate_movie(
...     'video.mp4',
...     canvas_size_hint=(2, 1), paint_funcs=paint_funcs)
show_mea_outline(transform_matrix=None, color=(1, 0.8431372549019608, 0, 0.2))

Uses partial_func() to return a function that can be passed to paint_funcs of generate_movie() which will draw the MEA electrode grid in the movie.

This can be used e.g. to display the position of the electrodes relative to the tissue and relative to the experiment shapes.

Parameters
  • transform_matrix

    Optionally a transformation matrix stored as an array by which to apply a affine transform to the grid.

    You can get the transformation used for the current experiment from mea_transform, using view_controller.

  • color – A 4-tuple of the red, green, blue, and transparency value to use to draw the grid.

Returns

The function to pass to generate_movie().

E.g.:

>>> with CeedDataReader('data.h5') as f:
... f.load_mcs_data()
... f.load_experiment(0)
... paint_funcs = [
...     f.show_mea_outline(f.view_controller.mea_transform)
... ]
... f.generate_movie('video.mp4', paint_funcs=paint_funcs)
generate_movie(filename, out_fmt='yuv420p', codec='libx264', lib_opts={'crf': '0'}, video_fmt='mp4', start=None, end=None, canvas_size=(0, 0), canvas_size_hint=(1, 1), projector_pos=(0, 0), projector_pos_hint=(None, None), paint_funcs=(), stimulation_transparency=1.0, lum=1.0, speed=1.0, hidden_shapes=None)

Generates a movie from the Ceed and MCS data.

By default it just draws the Ceed shapes and their intensities for each frame. Additional things can be drawn in the movie using e.g. paint_background_image(), paint_electrodes_data_callbacks(), and show_mea_outline().

Parameters
  • filename – The video filename to create.

  • out_fmt – The pixel format to use for the movie. Defaults to yuv420p

  • codec – The codec to use for the movie. Defaults to x264.

  • lib_opts – Any options to pass to video encoding library.

  • video_fmt – The video format into which to save the video. Defaults to mp4.

  • start – The optional start time, in seconds in terms of the experiment, from which to start drawing the movie.

  • end – The optional end time, in seconds in terms of the experiment, at which to end drawing the movie.

  • canvas_size – The optional movie size to create. If not provided defaults to the projector screen size used during the experiment.

  • canvas_size_hint – Similar to canvas_size, but is relative to the projector screen size used during the experiment. e.g. (1, 2) means that it’ll be the same width the projector, but twice as tall (e.g. if we want to draw other stuff besides the shapes in the empty space).

  • projector_pos – The optional absolute bottom left position where to draw the shapes. Defaults to the bottom left corner.

  • projector_pos_hint – The optional relative bottom left position where to draw the shapes. E.g. (1, 0) means to draw at a horizontal offset the size of the projector, but at the zero vertical offset.

  • paint_funcs

    Additional functions that the drawing engine will call for each Ceed frame so it will be given the opportunity to draw other things.

    The function is called once before anything so it can set up. Then if it returns a non-None object, it returns a generator that we call for each frame using gen.send, where we send it the current frame number and it draws the data for that frame. See the sample methods for an example.

    The order determines the z-depth of the canvas. E.g. the last function will be painted on top of the previous functions.

  • stimulation_transparency – The transparency used for the shapes. 1 means completely opaque, zero means completely transparent. It determines whether we can see through the drawn shapes to see the underlying image.

  • lum – The luminosity, as a percentage with which to draw the shape intensities. E.g. lum of 0.5 means all the shapes will be drawn at %50 of the real intensity.

  • speed – The speed of the movie relative to the rela experiment. E.g. 1 means real time. 0.5 means it’ll be hals as fast as real-time. And 2 means it’ll be twice as fast.

  • hidden_shapes – Optional list of Ceed shape names that will not be displayed in the movie.

E.g.:

>>> with CeedDataReader('data.h5') as f:
... f.load_mcs_data()
... f.load_experiment(0)
... paint_funcs = [
...     f.paint_background_image(
...         f.experiment_cam_image,
...         transform_matrix=f.view_controller.cam_transform),
...     f.show_mea_outline(f.view_controller.mea_transform),
...     f.paint_electrodes_data_callbacks(
...     f.get_electrode_names(), draw_pos_hint=(1, 0),
...     volt_axis=50)
... ]
... f.generate_movie(
...     'video.mp4',
...     canvas_size_hint=(2, 1), paint_funcs=paint_funcs)

Note

The movie generation can be quite slow.

static populate_config(settings, shape_factory, function_factory, stage_factory)

Given the application settings recovered from e.g. a yaml file and the shape, function, and stage factories, it creates the shapes, functions, and stages from the settings and returns the root functions and stages.

static read_image_from_block(block, postfix='') ffpyplayer.pic.Image

Given a nix block that contains an Image, it reads and returns the image.

dump_electrode_data_matlab(prefix, chunks=1000000000.0)

Dumps all the electrode data in a format that can be loaded by matlab. Each electrode is stored in its own file.

Parameters
  • prefix – A path to a directory and filename prefix that will be used for the output files. The output files, one for each electrode, will have the electrode name and a .mat suffix.

  • chunks – If not None, the data will be split into chucks of the given size in bytes and these chucks will be saved in the file. If the data is too big we may not be able to load it all at once in memory, so saving it as chucks will work if the chucks are small enough. Defaults to 1GB.

dump_electrode_data_circus(filename, chunks=1000000000.0)

Dumps all the electrode data in a format that can be loaded by spyking circus.

Parameters
  • filename – The filename where to dump the data.

  • chunks – If not None, the data will be copied in chucks of the given size in bytes. If the data is too big we may not be able to load it all at once in memory, so saving it with chucks will work if the chucks are small enough. Defaults to 1GB.

exception ceed.analysis.EndOfDataException

Bases: Exception

Exception raised when the data read is after the end of the file.

class ceed.analysis.CallableGen(gen)

Bases: object

Converts a generator that accepts input into a callable.

It calls next on the generator when created. Them with each call it “sends” the argument when called to the generator.

E.g.:

>>> def echo():
...     while True:
...         value = yield
...         print(f'Got "{value}"')
>>> # use it like a normal generator
>>> generator = echo()
>>> next(generator)
>>> generator.send(1)
Got "1"
>>> generator.send(5)
Got "5"
>>> # or more conveniently make it into a callable
>>> callback_generator = CallableGen(echo())
>>> callback_generator(1)
Got "1"
>>> callback_generator(5)
Got "5"
close()

Closes the underlying generator.

ceed.analysis.callable_gen(gen)

Decorator that converts a generator into a CallableGen.

It returns a function that when called will return a function that itself instantiates the original decorated generator and passes it to CallableGen, that is returned.

Both of these functions accept positional and keyword arguments that are passed on to the generator when it’s created.

ceed.analysis.partial_func(func)

Decorator that returns a function, which when called returns another function that calls the original function.

Both of these functions accept positional and keyword arguments that are passed on to the original function when it’s called.

ceed.analysis.read_nix_prop(prop)

Reads the value from a nix property.

It is slightly different between versions, so this works across all nix versions.