Skip to content

Commit 5fe556a

Browse files
authored
Merge pull request #1326 from mahlzahn/add_brw_v4.x_support
Add support for Biocam brw v4.x files, closes #1324
2 parents 106eefe + ef9a5a3 commit 5fe556a

File tree

3 files changed

+94
-49
lines changed

3 files changed

+94
-49
lines changed

neo/rawio/biocamrawio.py

+88-44
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
See:
55
https://www.3brain.com/products/single-well/biocam-x
66
7-
Author : Alessio Buccino
7+
Authors: Alessio Buccino, Robert Wolff
88
"""
99

1010
from .baserawio import (BaseRawIO, _signal_channel_dtype, _signal_stream_dtype,
1111
_spike_channel_dtype, _event_channel_dtype)
1212

1313
import numpy as np
14+
import json
1415

1516

1617

@@ -118,51 +119,88 @@ def open_biocam_file_header(filename):
118119
import h5py
119120

120121
rf = h5py.File(filename, 'r')
121-
# Read recording variables
122-
rec_vars = rf.require_group('3BRecInfo/3BRecVars/')
123-
bit_depth = rec_vars['BitDepth'][0]
124-
max_uv = rec_vars['MaxVolt'][0]
125-
min_uv = rec_vars['MinVolt'][0]
126-
n_frames = rec_vars['NRecFrames'][0]
127-
sampling_rate = rec_vars['SamplingRate'][0]
128-
signal_inv = rec_vars['SignalInversion'][0]
129-
130-
# Get the actual number of channels used in the recording
131-
file_format = rf['3BData'].attrs.get('Version', None)
132-
format_100 = False
133-
if file_format == 100:
134-
n_channels = len(rf['3BData/Raw'][0])
135-
format_100 = True
136-
elif file_format in (101, 102) or file_format is None:
137-
n_channels = int(rf['3BData/Raw'].shape[0] / n_frames)
138-
else:
139-
raise Exception('Unknown data file format.')
140-
141-
# # get channels
142-
channels = rf['3BRecInfo/3BMeaStreams/Raw/Chs'][:]
143-
144-
# determine correct function to read data
145-
if format_100:
146-
if signal_inv == 1:
147-
read_function = readHDF5t_100
148-
elif signal_inv == 1:
149-
read_function = readHDF5t_100_i
122+
123+
if '3BRecInfo' in rf.keys(): # brw v3.x
124+
# Read recording variables
125+
rec_vars = rf.require_group('3BRecInfo/3BRecVars/')
126+
bit_depth = rec_vars['BitDepth'][0]
127+
max_uv = rec_vars['MaxVolt'][0]
128+
min_uv = rec_vars['MinVolt'][0]
129+
num_frames = rec_vars['NRecFrames'][0]
130+
sampling_rate = rec_vars['SamplingRate'][0]
131+
signal_inv = rec_vars['SignalInversion'][0]
132+
133+
# Get the actual number of channels used in the recording
134+
file_format = rf['3BData'].attrs.get('Version', None)
135+
format_100 = False
136+
if file_format == 100:
137+
num_channels = len(rf['3BData/Raw'][0])
138+
format_100 = True
139+
elif file_format in (101, 102) or file_format is None:
140+
num_channels = int(rf['3BData/Raw'].shape[0] / num_frames)
150141
else:
151-
raise Exception("Unknown signal inversion")
152-
else:
153-
if signal_inv == 1:
154-
read_function = readHDF5t_101
155-
elif signal_inv == 1:
156-
read_function = readHDF5t_101_i
142+
raise Exception('Unknown data file format.')
143+
144+
# # get channels
145+
channels = rf['3BRecInfo/3BMeaStreams/Raw/Chs'][:]
146+
147+
# determine correct function to read data
148+
if format_100:
149+
if signal_inv == 1:
150+
read_function = readHDF5t_100
151+
elif signal_inv == 1:
152+
read_function = readHDF5t_100_i
153+
else:
154+
raise Exception("Unknown signal inversion")
157155
else:
158-
raise Exception("Unknown signal inversion")
159-
160-
gain = (max_uv - min_uv) / (2 ** bit_depth)
161-
offset = min_uv
162-
163-
return dict(file_handle=rf, num_frames=n_frames, sampling_rate=sampling_rate, num_channels=n_channels,
164-
channels=channels, file_format=file_format, signal_inv=signal_inv,
165-
read_function=read_function, gain=gain, offset=offset)
156+
if signal_inv == 1:
157+
read_function = readHDF5t_101
158+
elif signal_inv == 1:
159+
read_function = readHDF5t_101_i
160+
else:
161+
raise Exception("Unknown signal inversion")
162+
163+
gain = (max_uv - min_uv) / (2 ** bit_depth)
164+
offset = min_uv
165+
166+
return dict(file_handle=rf, num_frames=num_frames, sampling_rate=sampling_rate,
167+
num_channels=num_channels, channels=channels, file_format=file_format,
168+
signal_inv=signal_inv, read_function=read_function, gain=gain, offset=offset)
169+
else: # brw v4.x
170+
# Read recording variables
171+
experiment_settings = json.JSONDecoder().decode(rf['ExperimentSettings'][0].decode())
172+
max_uv = experiment_settings['ValueConverter']['MaxAnalogValue']
173+
min_uv = experiment_settings['ValueConverter']['MinAnalogValue']
174+
max_digital = experiment_settings['ValueConverter']['MaxDigitalValue']
175+
min_digital = experiment_settings['ValueConverter']['MinDigitalValue']
176+
scale_factor = experiment_settings['ValueConverter']['ScaleFactor']
177+
sampling_rate = experiment_settings['TimeConverter']['FrameRate']
178+
179+
for key in rf:
180+
if key[:5] == 'Well_':
181+
num_channels = len(rf[key]['StoredChIdxs'])
182+
if len(rf[key]['Raw']) % num_channels:
183+
raise RuntimeError(
184+
f"Length of raw data array is not multiple of channel number in {key}")
185+
num_frames = len(rf[key]['Raw']) // num_channels
186+
break
187+
try:
188+
num_channels_x = num_channels_y = int(np.sqrt(num_channels))
189+
except NameError:
190+
raise RuntimeError("No Well found in the file")
191+
if num_channels_x * num_channels_y != num_channels:
192+
raise RuntimeError(
193+
f'Cannot determine structure of the MEA plate with {num_channels} channels')
194+
channels = 1 + np.concatenate(np.transpose(np.meshgrid(
195+
range(num_channels_x), range(num_channels_y))))
196+
197+
gain = scale_factor * (max_uv - min_uv) / (max_digital - min_digital)
198+
offset = min_uv
199+
read_function = readHDF5t_brw4
200+
201+
return dict(file_handle=rf, num_frames=num_frames, sampling_rate=sampling_rate,
202+
num_channels=num_channels, channels=channels, read_function=read_function,
203+
gain=gain, offset=offset)
166204

167205

168206
def readHDF5t_100(rf, t0, t1, nch):
@@ -179,3 +217,9 @@ def readHDF5t_101(rf, t0, t1, nch):
179217

180218
def readHDF5t_101_i(rf, t0, t1, nch):
181219
return 4096 - rf['3BData/Raw'][nch * t0:nch * t1].reshape((t1 - t0, nch), order='C')
220+
221+
222+
def readHDF5t_brw4(rf, t0, t1, nch):
223+
for key in rf:
224+
if key[:5] == 'Well_':
225+
return rf[key]['Raw'][nch * t0:nch * t1].reshape((t1 - t0, nch), order='C')

neo/test/iotest/test_biocam.py renamed to neo/test/iotest/test_biocamio.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class TestBiocamIO(BaseTestIO, unittest.TestCase, ):
1414
'biocam'
1515
]
1616
entities_to_test = [
17-
'biocam/biocam_hw3.0_fw1.6.brw'
17+
'biocam/biocam_hw3.0_fw1.6.brw',
18+
'biocam/biocam_hw3.0_fw1.7.0.12_raw.brw',
1819
]
1920

2021

neo/test/rawiotest/test_biocam.py renamed to neo/test/rawiotest/test_biocamrawio.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
class TestBiocamRawIO(BaseTestRawIO, unittest.TestCase, ):
1212
rawioclass = BiocamRawIO
1313

14-
entities_to_download = [
15-
'biocam/biocam_hw3.0_fw1.6.brw'
16-
]
17-
1814
entities_to_download = [
1915
'biocam',
2016
]
17+
entities_to_test = [
18+
'biocam/biocam_hw3.0_fw1.6.brw',
19+
'biocam/biocam_hw3.0_fw1.7.0.12_raw.brw',
20+
]
2121

2222

2323
if __name__ == "__main__":

0 commit comments

Comments
 (0)