blkio.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. from .baseio import BaseIO
  2. from neo.core import ImageSequence, Segment, Block
  3. import numpy as np
  4. import struct
  5. import os
  6. import math
  7. import quantities as pq
  8. class BlkIO(BaseIO):
  9. """
  10. Neo IO module for optical imaging data stored as BLK file
  11. *Usage*:
  12. >>> from neo import io
  13. >>> import quantities as pq
  14. >>> r = io.BlkIO("file_blk_1.BLK",units='V',sampling_rate=1.0*pq.Hz,
  15. ... spatial_scale=1.0*pq.Hz)
  16. >>> block = r.read_block()
  17. reading the header
  18. reading block
  19. returning block
  20. >>> block
  21. Block with 6 segments
  22. file_origin: 'file_blk_1.BLK'
  23. # segments (N=6)
  24. 0: Segment with 1 imagesequences description: 'stim nb:0' # analogsignals (N=0)
  25. 1: Segment with 1 imagesequences description: 'stim nb:1' # analogsignals (N=0)
  26. 2: Segment with 1 imagesequences description: 'stim nb:2' # analogsignals (N=0)
  27. 3: Segment with 1 imagesequences description: 'stim nb:3' # analogsignals (N=0)
  28. 4: Segment with 1 imagesequences description: 'stim nb:4' # analogsignals (N=0)
  29. 5: Segment with 1 imagesequences description: 'stim nb:5' # analogsignals (N=0)
  30. Many thanks to Thomas Deneux for the MATLAB code on which this was based.
  31. """
  32. name = 'BLK IO'
  33. description = "Neo IO module for optical imaging data stored as BLK file"
  34. _prefered_signal_goup_mode = 'group-by-same-units'
  35. is_readable = True
  36. is_wirtable = False
  37. supported_objects = [Block, Segment, ImageSequence]
  38. readble_objects = supported_objects
  39. support_lazy = False
  40. read_params = {}
  41. write_params = {}
  42. extensions = []
  43. mode = 'file'
  44. def __init__(self, file_name=None, units=None, sampling_rate=None, spatial_scale=None, **kwargs):
  45. BaseIO.__init__(self, file_name, **kwargs)
  46. self.units = units
  47. self.sampling_rate = sampling_rate
  48. self.spatial_scale = spatial_scale
  49. def read(self, lazy=False, **kwargs):
  50. """
  51. Return all data from the file as a list of Blocks
  52. """
  53. if lazy:
  54. raise ValueError('This IO module does not support lazy loading')
  55. return [self.read_block(lazy=lazy, units=self.units, sampling_rate=self.sampling_rate,
  56. spatial_scale=self.spatial_scale, **kwargs)]
  57. def read_block(self, lazy=False, **kargs):
  58. def read(name, type, nb, dictionary, file):
  59. if type == 'int32':
  60. # dictionary[name] = int.from_bytes(file.read(4), byteorder=sys.byteorder, signed=True)
  61. dictionary[name] = struct.unpack("i", file.read(4))[0]
  62. if type == 'float32':
  63. dictionary[name] = struct.unpack('f', file.read(4))[0]
  64. if type == 'uint8':
  65. l = []
  66. for i in range(nb):
  67. l.append(chr(struct.unpack('B', file.read(1))[0]))
  68. dictionary[name] = l
  69. if type == 'uint16':
  70. l = []
  71. for i in range(nb):
  72. l.append((struct.unpack('H', file.read(2)))[0])
  73. dictionary[name] = l
  74. if type == 'short':
  75. dictionary[name] = struct.unpack('h', file.read(2))[0]
  76. return dictionary
  77. def read_header(file_name):
  78. file = open(file_name, "rb")
  79. i = [
  80. ['file_size', 'int32', 1],
  81. ['checksum_header', 'int32', 1],
  82. ['check_data', 'int32', 1],
  83. ['lenheader', 'int32', 1],
  84. ['versionid', 'float32', 1],
  85. ['filetype', 'int32', 1],
  86. ['filesubtype', 'int32', 1],
  87. ['datatype', 'int32', 1],
  88. ['sizeof', 'int32', 1],
  89. ['framewidth', 'int32', 1],
  90. ['frameheight', 'int32', 1],
  91. ['nframesperstim', 'int32', 1],
  92. ['nstimuli', 'int32', 1],
  93. ['initialxbinfactor', 'int32', 1],
  94. ['initialybinfactor', 'int32', 1],
  95. ['xbinfactor', 'int32', 1],
  96. ['ybinfactor', 'int32', 1],
  97. ['username', 'uint8', 32],
  98. ['recordingdate', 'uint8', 16],
  99. ['x1roi', 'int32', 1],
  100. ['y1roi', 'int32', 1],
  101. ['x2roi', 'int32', 1],
  102. ['y2roi', 'int32', 1],
  103. ['stimoffs', 'int32', 1],
  104. ['stimsize', 'int32', 1],
  105. ['frameoffs', 'int32', 1],
  106. ['framesize', 'int32', 1],
  107. ['refoffs', 'int32', 1],
  108. ['refsize', 'int32', 1],
  109. ['refwidth', 'int32', 1],
  110. ['refheight', 'int32', 1],
  111. ['whichblocks', 'uint16', 16],
  112. ['whichframe', 'uint16', 16],
  113. ['loclip', 'int32', 1],
  114. ['hiclip', 'int32', 1],
  115. ['lopass', 'int32', 1],
  116. ['hipass', 'int32', 1],
  117. ['operationsperformed', 'uint8', 64],
  118. ['magnification', 'float32', 1],
  119. ['gain', 'uint16', 1],
  120. ['wavelength', 'uint16', 1],
  121. ['exposuretime', 'int32', 1],
  122. ['nrepetitions', 'int32', 1],
  123. ['acquisitiondelay', 'int32', 1],
  124. ['interstiminterval', 'int32', 1],
  125. ['creationdate', 'uint8', 16],
  126. ['datafilename', 'uint8', 64],
  127. ['orareserved', 'uint8', 256]
  128. ]
  129. dic = {}
  130. for x in i:
  131. dic = read(name=x[0], type=x[1], nb=x[2], dictionary=dic, file=file)
  132. if dic['filesubtype'] == 13:
  133. i = [
  134. ["includesrefframe", "int32", 1],
  135. ["temp", "uint8", 128],
  136. ["ntrials", "int32", 1],
  137. ["scalefactors", "int32", 1],
  138. ["cameragain", "short", 1],
  139. ["ampgain", "short", 1],
  140. ["samplingrate", "short", 1],
  141. ["average", "short", 1],
  142. ["exposuretime", "short", 1],
  143. ["samplingaverage", "short", 1],
  144. ["presentaverage", "short", 1],
  145. ["framesperstim", "short", 1],
  146. ["trialsperblock", "short", 1],
  147. ["sizeofanalogbufferinframes", "short", 1],
  148. ["cameratrials", "short", 1],
  149. ["filler", "uint8", 106],
  150. ["dyedaqreserved", "uint8", 106]
  151. ]
  152. for x in i:
  153. dic = read(name=x[0], type=x[1], nb=x[2], dictionary=dic, file=file)
  154. # nottested
  155. # p.listofstimuli=temp(1:max(find(temp~=0)))'; % up to first non-zero stimulus
  156. dic["listofstimuli"] = dic["temp"][0:np.argwhere(x != 0).max(0)]
  157. else:
  158. i = [
  159. ["includesrefframe", "int32", 1],
  160. ["listofstimuli", "uint8", 256],
  161. ["nvideoframesperdataframe", "int32", 1],
  162. ["ntrials", "int32", 1],
  163. ["scalefactor", "int32", 1],
  164. ["meanampgain", "float32", 1],
  165. ["meanampdc", "float32", 1],
  166. ["vdaqreserved", "uint8", 256]
  167. ]
  168. for x in i:
  169. dic = read(name=x[0], type=x[1], nb=x[2], dictionary=dic, file=file)
  170. i = [["user", "uint8", 256], ["comment", "uint8", 256], ["refscalefactor", "int32", 1]]
  171. for x in i:
  172. dic = read(name=x[0], type=x[1], nb=x[2], dictionary=dic, file=file)
  173. dic["actuallength"] = os.stat(file_name).st_size
  174. file.close()
  175. return dic
  176. # start of the reading process
  177. nblocks = 1
  178. print("reading the header")
  179. header = read_header(self.filename)
  180. nstim = header['nstimuli']
  181. ni = header['framewidth']
  182. nj = header['frameheight']
  183. nfr = header['nframesperstim']
  184. lenh = header['lenheader']
  185. framesize = header['framesize']
  186. filesize = header['file_size']
  187. dtype = header['datatype']
  188. gain = header['meanampgain']
  189. dc = header['meanampdc']
  190. scalefactor = header['scalefactor']
  191. # [["dtype","nbytes","datatype","type_out"],[...]]
  192. l = [
  193. [11, 1, "uchar", "uint8", "B"], [12, 2, "ushort", "uint16", "H"],
  194. [13, 4, "ulong", "uint32", "I"], [14, 4, "float", "single", "f"]
  195. ]
  196. for i in l:
  197. if dtype == i[0]:
  198. nbytes, datatype, type_out, struct_type = i[1], i[2], i[3], i[4]
  199. if framesize != ni * nj * nbytes:
  200. print("BAD HEADER!!! framesize does not match framewidth*frameheight*nbytes!")
  201. framesize = ni * nj * nbytes
  202. if (filesize - lenh) > (framesize * nfr * nstim):
  203. nfr2 = nfr + 1
  204. includesrefframe = True
  205. else:
  206. nfr2 = nfr
  207. includesrefframe = False
  208. nbin = nblocks
  209. conds = [i for i in range(1, nstim + 1)]
  210. ncond = len(conds)
  211. data = [[[np.zeros((ni, nj, nfr), type_out)] for x in range(ncond)] for i in range(nbin)]
  212. for k in range(1, nbin + 1):
  213. print("reading block")
  214. bin = np.arange(math.floor((k - 1 / nbin * nblocks) + 1),
  215. math.floor((k / nbin * nblocks) + 1))
  216. sbin = bin.size
  217. for j in range(1, sbin + 1):
  218. file = open(self.filename, 'rb')
  219. for i in range(1, ncond + 1):
  220. framestart = conds[i - 1] * nfr2 - nfr
  221. offset = framestart * ni * nj * nbytes + lenh
  222. file.seek(offset, 0)
  223. a = [(struct.unpack(struct_type, file.read(nbytes)))[0]
  224. for m in range(ni * nj * nfr)]
  225. a = np.reshape(np.array(a, dtype=type_out, order='F'),
  226. (ni * nj, nfr), order='F')
  227. a = np.reshape(a, (ni, nj, nfr), order='F')
  228. if includesrefframe:
  229. # not tested
  230. framestart = (conds[i] - 1) * nfr2
  231. offset = framestart * ni * nj * nbytes + lenh
  232. file.seek(offset)
  233. ref = [(struct.unpack(struct_type, file.read(nbytes)))[0]
  234. for m in range(ni * nj)]
  235. ref = np.array(ref, dtype=type_out)
  236. for y in range(len(ref)):
  237. ref[y] *= scalefactor
  238. ref = np.reshape(ref, (ni, nj))
  239. b = np.tile(ref, [1, 1, nfr])
  240. for y in range(len(a)):
  241. b.append([])
  242. for x in range(len(a[y])):
  243. b[y + 1].append([])
  244. for frame in range(len(a[y][x])):
  245. b[y + 1][x][frame] = (a[y][x][frame] / gain) - \
  246. (scalefactor * dc / gain)
  247. a = b
  248. if sbin == 1:
  249. data[k - 1][i - 1] = a
  250. else:
  251. # not tested
  252. for y in range(len(a)):
  253. for x in range(len(a[y])):
  254. a[y][x] /= sbin
  255. data[k - 1][i - 1] = data[k - 1][i - 1] + a / sbin
  256. file.close()
  257. # data format [block][stim][width][height][frame]]
  258. # data structure should be [block][stim][frame][width][height] in order to be easy to use with neo
  259. # each file is a block
  260. # each stim could be a segment
  261. # then an image sequence [frame][width][height]
  262. # image need to be rotated
  263. # changing order of data for compatibility
  264. # [block][stim][width][height][frame]]
  265. # to
  266. # [block][stim][frame][width][height]
  267. for block in range(len(data)):
  268. for stim in range(len(data[block])):
  269. a = []
  270. for frame in range(header['nframesperstim']):
  271. a.append([])
  272. for width in range(len(data[block][stim])):
  273. a[frame].append([])
  274. for height in range(len(data[block][stim][width])):
  275. a[frame][width].append(data[block][stim][width][height][frame])
  276. # rotation of data to be the same as thomas deneux screenshot
  277. a[frame] = np.rot90(np.fliplr(a[frame]))
  278. data[block][stim] = a
  279. block = Block(file_origin=self.filename)
  280. for stim in range(len(data[0])):
  281. image_sequence = ImageSequence(data[0][stim], units=self.units,
  282. sampling_rate=self.sampling_rate,
  283. spatial_scale=self.spatial_scale)
  284. segment = Segment(file_origin=self.filename, description=("stim nb:"+str(stim)))
  285. segment.imagesequences = [image_sequence]
  286. segment.block = block
  287. for key in header:
  288. block.annotations[key] = header[key]
  289. block.segments.append(segment)
  290. print("returning block")
  291. return block