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 withload_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 withget_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 theelectrodes_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 MCSelectrodes_data
by index. Seeelectrode_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
inelectrodes_data
, frame 2 at indexm
, frame 3 at indexn
, 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 inshapes_intensity
, because this contains only indices for frames that were rendered. So it should be the same length as the items inshapes_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 ask - 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 whenelectrode_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 ofelectrode_intensity_alignment
.shapes_intensity_rendered
contains only the frames that were rendered and is therefore the same size aselectrode_intensity_alignment
. It is generated by indexingshapes_intensity
usingrendered_frames
.shapes_intensity_rendered_gpu_rate
additionally, breaks the longer frames into individual frames so it’s generally the same size asshapes_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 alsoshapes_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 aselectrode_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, andshapes_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 whenelectrode_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 inshapes_intensity
and it indicates which frames of those frames were rendered.shapes_intensity_rendered
is internally generated by indexingshapes_intensity
withrendered_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 asshapes_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. Seeshapes_intensity
andshapes_intensity_rendered_gpu_rate
.rendered_frames_long
is None whenelectrode_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’sceed_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 theceed_id
of a stage/function to locate it in the logs usingexperiment_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
andStageFactoryBase
each define aon_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
andloop_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
andloop_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
andloop_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
andloop_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 eventdrug
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 theViewControllerBase
instance configured to the same settings as used during theloaded_experiment
.
- data_serializer: ceed.storage.controller.DataSerializerBase = None
After
load_experiment()
, it is theDataSerializerBase
instance configured to the same settings as used during theloaded_experiment
.
- function_factory: ceed.function.FunctionFactoryBase = None
After
load_experiment()
, it is theFunctionFactoryBase
instance configured to the same settings as used during theloaded_experiment
.
- stage_factory: ceed.stage.StageFactoryBase = None
After
load_experiment()
, it is theStageFactoryBase
instance configured to the same settings as used during theloaded_experiment
.
- shape_factory: ceed.shape.CeedPaintCanvasBehavior = None
After
load_experiment()
, it is theCeedPaintCanvasBehavior
instance configured to the same settings as used during theloaded_experiment
.
- experiment_stage_name: str = ''
After
load_experiment()
, it is the name of the stage among the stages instage_factory
that was used to run the currentlyloaded_experiment
.
- experiment_stage: ceed.stage.CeedStage = None
After
load_experiment()
, it is theCeedStage
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 currentlyloaded_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.
- 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.
- 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.
- 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 duringmerge_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 inopen_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 becauseclose_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 thewith
block andclose_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()
, andceed.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 themea_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)
andpayload
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 thanceed_id
. Theobj
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)
, whereimage
is the image,notes
is any user notes saved with the image, andsave_time
is thetime.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 inelectrodes_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 inanalog_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 thanrendered_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 topaint_funcs
ofgenerate_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
, usingview_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 topaint_funcs
ofgenerate_movie()
which will draw theelectrodes_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 topaint_funcs
ofgenerate_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
, usingview_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()
, andshow_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.