lytro.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2018, imageio contributors
  3. # imageio is distributed under the terms of the (new) BSD License.
  4. #
  5. """Read LFR files (Lytro Illum).
  6. Backend: internal
  7. Plugin to read Lytro Illum .lfr and .raw files as produced
  8. by the Lytro Illum light field camera. It is actually a collection
  9. of plugins, each supporting slightly different keyword arguments
  10. Parameters
  11. ----------
  12. meta_only : bool
  13. Whether to only read the metadata.
  14. include_thumbnail : bool
  15. (only for lytro-lfr and lytro-lfp)
  16. Whether to include an image thumbnail in the metadata.
  17. """
  18. #
  19. #
  20. # This code is based on work by
  21. # David Uhlig and his lfr_reader
  22. # (https://www.iiit.kit.edu/uhlig.php)
  23. # Donald Dansereau and his Matlab LF Toolbox
  24. # (http://dgd.vision/Tools/LFToolbox/)
  25. # and Behnam Esfahbod and his Python LFP-Reader
  26. # (https://github.com/behnam/python-lfp-reader/)
  27. import os
  28. import json
  29. import struct
  30. import logging
  31. import numpy as np
  32. from ..core import Format
  33. from ..v2 import imread
  34. logger = logging.getLogger(__name__)
  35. # Sensor size of Lytro Illum resp. Lytro F01 light field camera sensor
  36. LYTRO_ILLUM_IMAGE_SIZE = (5368, 7728)
  37. LYTRO_F01_IMAGE_SIZE = (3280, 3280)
  38. # Parameter of lfr file format
  39. HEADER_LENGTH = 12
  40. SIZE_LENGTH = 4 # = 16 - header_length
  41. SHA1_LENGTH = 45 # = len("sha1-") + (160 / 4)
  42. PADDING_LENGTH = 35 # = (4*16) - header_length - size_length - sha1_length
  43. DATA_CHUNKS_ILLUM = 11
  44. DATA_CHUNKS_F01 = 3
  45. class LytroFormat(Format):
  46. """Base class for Lytro format.
  47. The subclasses LytroLfrFormat, LytroLfpFormat, LytroIllumRawFormat and
  48. LytroF01RawFormat implement the Lytro-LFR, Lytro-LFP and Lytro-RAW format
  49. for the Illum and original F01 camera respectively.
  50. Writing is not supported.
  51. """
  52. # Only single images are supported.
  53. _modes = "i"
  54. def _can_write(self, request):
  55. # Writing of Lytro files is not supported
  56. return False
  57. # -- writer
  58. class Writer(Format.Writer):
  59. def _open(self, flags=0):
  60. self._fp = self.request.get_file()
  61. def _close(self):
  62. # Close the reader.
  63. # Note that the request object will close self._fp
  64. pass
  65. def _append_data(self, im, meta):
  66. # Process the given data and meta data.
  67. raise RuntimeError("The lytro format cannot write image data.")
  68. def _set_meta_data(self, meta):
  69. # Process the given meta data (global for all images)
  70. # It is not mandatory to support this.
  71. raise RuntimeError("The lytro format cannot write meta data.")
  72. class LytroIllumRawFormat(LytroFormat):
  73. """This is the Lytro Illum RAW format.
  74. The raw format is a 10bit image format as used by the Lytro Illum
  75. light field camera. The format will read the specified raw file and will
  76. try to load a .txt or .json file with the associated meta data.
  77. This format does not support writing.
  78. Parameters for reading
  79. ----------------------
  80. meta_only : bool
  81. Whether to only read the metadata.
  82. """
  83. def _can_read(self, request):
  84. # Check if mode and extensions are supported by the format
  85. if request.extension in (".raw",):
  86. return True
  87. @staticmethod
  88. def rearrange_bits(array):
  89. # Do bit rearrangement for the 10-bit lytro raw format
  90. # Normalize output to 1.0 as float64
  91. t0 = array[0::5]
  92. t1 = array[1::5]
  93. t2 = array[2::5]
  94. t3 = array[3::5]
  95. lsb = array[4::5]
  96. t0 = np.left_shift(t0, 2) + np.bitwise_and(lsb, 3)
  97. t1 = np.left_shift(t1, 2) + np.right_shift(np.bitwise_and(lsb, 12), 2)
  98. t2 = np.left_shift(t2, 2) + np.right_shift(np.bitwise_and(lsb, 48), 4)
  99. t3 = np.left_shift(t3, 2) + np.right_shift(np.bitwise_and(lsb, 192), 6)
  100. image = np.zeros(LYTRO_ILLUM_IMAGE_SIZE, dtype=np.uint16)
  101. image[:, 0::4] = t0.reshape(
  102. (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
  103. )
  104. image[:, 1::4] = t1.reshape(
  105. (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
  106. )
  107. image[:, 2::4] = t2.reshape(
  108. (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
  109. )
  110. image[:, 3::4] = t3.reshape(
  111. (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
  112. )
  113. # Normalize data to 1.0 as 64-bit float.
  114. # Division is by 1023 as the Lytro Illum saves 10-bit raw data.
  115. return np.divide(image, 1023.0).astype(np.float64)
  116. # -- reader
  117. class Reader(Format.Reader):
  118. def _open(self, meta_only=False):
  119. self._file = self.request.get_file()
  120. self._data = None
  121. self._meta_only = meta_only
  122. def _close(self):
  123. # Close the reader.
  124. # Note that the request object will close self._file
  125. del self._data
  126. def _get_length(self):
  127. # Return the number of images.
  128. return 1
  129. def _get_data(self, index):
  130. # Return the data and meta data for the given index
  131. if index not in [0, "None"]:
  132. raise IndexError("Lytro file contains only one dataset")
  133. if not self._meta_only:
  134. # Read all bytes
  135. if self._data is None:
  136. self._data = self._file.read()
  137. # Read bytes from string and convert to uint16
  138. raw = np.frombuffer(self._data, dtype=np.uint8).astype(np.uint16)
  139. # Rearrange bits
  140. img = LytroIllumRawFormat.rearrange_bits(raw)
  141. else:
  142. # Return empty image
  143. img = np.array([])
  144. # Return image and meta data
  145. return img, self._get_meta_data(index=0)
  146. def _get_meta_data(self, index):
  147. # Get the meta data for the given index. If index is None, it
  148. # should return the global meta data.
  149. if index not in [0, None]:
  150. raise IndexError("Lytro meta data file contains only one dataset")
  151. # Try to read meta data from meta data file corresponding
  152. # to the raw data file, extension in [.txt, .TXT, .json, .JSON]
  153. filename_base = os.path.splitext(self.request.get_local_filename())[0]
  154. meta_data = None
  155. for ext in [".txt", ".TXT", ".json", ".JSON"]:
  156. if os.path.isfile(filename_base + ext):
  157. meta_data = json.load(open(filename_base + ext))
  158. if meta_data is not None:
  159. return meta_data
  160. else:
  161. logger.warning("No metadata file found for provided raw file.")
  162. return {}
  163. class LytroLfrFormat(LytroFormat):
  164. """This is the Lytro Illum LFR format.
  165. The lfr is a image and meta data container format as used by the
  166. Lytro Illum light field camera.
  167. The format will read the specified lfr file.
  168. This format does not support writing.
  169. Parameters for reading
  170. ----------------------
  171. meta_only : bool
  172. Whether to only read the metadata.
  173. include_thumbnail : bool
  174. Whether to include an image thumbnail in the metadata.
  175. """
  176. def _can_read(self, request):
  177. # Check if mode and extensions are supported by the format
  178. if request.extension in (".lfr",):
  179. return True
  180. # -- reader
  181. class Reader(Format.Reader):
  182. def _open(self, meta_only=False, include_thumbnail=True):
  183. self._file = self.request.get_file()
  184. self._data = None
  185. self._chunks = {}
  186. self.metadata = {}
  187. self._content = None
  188. self._meta_only = meta_only
  189. self._include_thumbnail = include_thumbnail
  190. self._find_header()
  191. self._find_chunks()
  192. self._find_meta()
  193. try:
  194. # Get sha1 dict and check if it is in dictionary of data chunks
  195. chunk_dict = self._content["frames"][0]["frame"]
  196. if (
  197. chunk_dict["metadataRef"] in self._chunks
  198. and chunk_dict["imageRef"] in self._chunks
  199. and chunk_dict["privateMetadataRef"] in self._chunks
  200. ):
  201. if not self._meta_only:
  202. # Read raw image data byte buffer
  203. data_pos, size = self._chunks[chunk_dict["imageRef"]]
  204. self._file.seek(data_pos, 0)
  205. self.raw_image_data = self._file.read(size)
  206. # Read meta data
  207. data_pos, size = self._chunks[chunk_dict["metadataRef"]]
  208. self._file.seek(data_pos, 0)
  209. metadata = self._file.read(size)
  210. # Add metadata to meta data dict
  211. self.metadata["metadata"] = json.loads(metadata.decode("ASCII"))
  212. # Read private metadata
  213. data_pos, size = self._chunks[chunk_dict["privateMetadataRef"]]
  214. self._file.seek(data_pos, 0)
  215. serial_numbers = self._file.read(size)
  216. self.serial_numbers = json.loads(serial_numbers.decode("ASCII"))
  217. # Add private metadata to meta data dict
  218. self.metadata["privateMetadata"] = self.serial_numbers
  219. # Read image preview thumbnail
  220. if self._include_thumbnail:
  221. chunk_dict = self._content["thumbnails"][0]
  222. if chunk_dict["imageRef"] in self._chunks:
  223. # Read thumbnail image from thumbnail chunk
  224. data_pos, size = self._chunks[chunk_dict["imageRef"]]
  225. self._file.seek(data_pos, 0)
  226. # Read binary data, read image as jpeg
  227. thumbnail_data = self._file.read(size)
  228. thumbnail_img = imread(thumbnail_data, format="jpeg")
  229. thumbnail_height = chunk_dict["height"]
  230. thumbnail_width = chunk_dict["width"]
  231. # Add thumbnail to metadata
  232. self.metadata["thumbnail"] = {
  233. "image": thumbnail_img,
  234. "height": thumbnail_height,
  235. "width": thumbnail_width,
  236. }
  237. except KeyError:
  238. raise RuntimeError("The specified file is not a valid LFR file.")
  239. def _close(self):
  240. # Close the reader.
  241. # Note that the request object will close self._file
  242. del self._data
  243. def _get_length(self):
  244. # Return the number of images. Can be np.inf
  245. return 1
  246. def _find_header(self):
  247. """
  248. Checks if file has correct header and skip it.
  249. """
  250. file_header = b"\x89LFP\x0d\x0a\x1a\x0a\x00\x00\x00\x01"
  251. # Read and check header of file
  252. header = self._file.read(HEADER_LENGTH)
  253. if header != file_header:
  254. raise RuntimeError("The LFR file header is invalid.")
  255. # Read first bytes to skip header
  256. self._file.read(SIZE_LENGTH)
  257. def _find_chunks(self):
  258. """
  259. Gets start position and size of data chunks in file.
  260. """
  261. chunk_header = b"\x89LFC\x0d\x0a\x1a\x0a\x00\x00\x00\x00"
  262. for i in range(0, DATA_CHUNKS_ILLUM):
  263. data_pos, size, sha1 = self._get_chunk(chunk_header)
  264. self._chunks[sha1] = (data_pos, size)
  265. def _find_meta(self):
  266. """
  267. Gets a data chunk that contains information over content
  268. of other data chunks.
  269. """
  270. meta_header = b"\x89LFM\x0d\x0a\x1a\x0a\x00\x00\x00\x00"
  271. data_pos, size, sha1 = self._get_chunk(meta_header)
  272. # Get content
  273. self._file.seek(data_pos, 0)
  274. data = self._file.read(size)
  275. self._content = json.loads(data.decode("ASCII"))
  276. def _get_chunk(self, header):
  277. """
  278. Checks if chunk has correct header and skips it.
  279. Finds start position and length of next chunk and reads
  280. sha1-string that identifies the following data chunk.
  281. Parameters
  282. ----------
  283. header : bytes
  284. Byte string that identifies start of chunk.
  285. Returns
  286. -------
  287. data_pos : int
  288. Start position of data chunk in file.
  289. size : int
  290. Size of data chunk.
  291. sha1 : str
  292. Sha1 value of chunk.
  293. """
  294. # Read and check header of chunk
  295. header_chunk = self._file.read(HEADER_LENGTH)
  296. if header_chunk != header:
  297. raise RuntimeError("The LFR chunk header is invalid.")
  298. data_pos = None
  299. sha1 = None
  300. # Read size
  301. size = struct.unpack(">i", self._file.read(SIZE_LENGTH))[0]
  302. if size > 0:
  303. # Read sha1
  304. sha1 = str(self._file.read(SHA1_LENGTH).decode("ASCII"))
  305. # Skip fixed null chars
  306. self._file.read(PADDING_LENGTH)
  307. # Find start of data and skip data
  308. data_pos = self._file.tell()
  309. self._file.seek(size, 1)
  310. # Skip extra null chars
  311. ch = self._file.read(1)
  312. while ch == b"\0":
  313. ch = self._file.read(1)
  314. self._file.seek(-1, 1)
  315. return data_pos, size, sha1
  316. def _get_data(self, index):
  317. # Return the data and meta data for the given index
  318. if index not in [0, None]:
  319. raise IndexError("Lytro lfr file contains only one dataset")
  320. if not self._meta_only:
  321. # Read bytes from string and convert to uint16
  322. raw = np.frombuffer(self.raw_image_data, dtype=np.uint8).astype(
  323. np.uint16
  324. )
  325. im = LytroIllumRawFormat.rearrange_bits(raw)
  326. else:
  327. im = np.array([])
  328. # Return array and dummy meta data
  329. return im, self.metadata
  330. def _get_meta_data(self, index):
  331. # Get the meta data for the given index. If index is None,
  332. # it returns the global meta data.
  333. if index not in [0, None]:
  334. raise IndexError("Lytro meta data file contains only one dataset")
  335. return self.metadata
  336. class LytroF01RawFormat(LytroFormat):
  337. """This is the Lytro RAW format for the original F01 Lytro camera.
  338. The raw format is a 12bit image format as used by the Lytro F01
  339. light field camera. The format will read the specified raw file and will
  340. try to load a .txt or .json file with the associated meta data.
  341. This format does not support writing.
  342. Parameters for reading
  343. ----------------------
  344. meta_only : bool
  345. Whether to only read the metadata.
  346. """
  347. def _can_read(self, request):
  348. # Check if mode and extensions are supported by the format
  349. if request.extension in (".raw",):
  350. return True
  351. @staticmethod
  352. def rearrange_bits(array):
  353. # Do bit rearrangement for the 12-bit lytro raw format
  354. # Normalize output to 1.0 as float64
  355. t0 = array[0::3]
  356. t1 = array[1::3]
  357. t2 = array[2::3]
  358. a0 = np.left_shift(t0, 4) + np.right_shift(np.bitwise_and(t1, 240), 4)
  359. a1 = np.left_shift(np.bitwise_and(t1, 15), 8) + t2
  360. image = np.zeros(LYTRO_F01_IMAGE_SIZE, dtype=np.uint16)
  361. image[:, 0::2] = a0.reshape(
  362. (LYTRO_F01_IMAGE_SIZE[0], LYTRO_F01_IMAGE_SIZE[1] // 2)
  363. )
  364. image[:, 1::2] = a1.reshape(
  365. (LYTRO_F01_IMAGE_SIZE[0], LYTRO_F01_IMAGE_SIZE[1] // 2)
  366. )
  367. # Normalize data to 1.0 as 64-bit float.
  368. # Division is by 4095 as the Lytro F01 saves 12-bit raw data.
  369. return np.divide(image, 4095.0).astype(np.float64)
  370. # -- reader
  371. class Reader(Format.Reader):
  372. def _open(self, meta_only=False):
  373. self._file = self.request.get_file()
  374. self._data = None
  375. self._meta_only = meta_only
  376. def _close(self):
  377. # Close the reader.
  378. # Note that the request object will close self._file
  379. del self._data
  380. def _get_length(self):
  381. # Return the number of images.
  382. return 1
  383. def _get_data(self, index):
  384. # Return the data and meta data for the given index
  385. if index not in [0, "None"]:
  386. raise IndexError("Lytro file contains only one dataset")
  387. if not self._meta_only:
  388. # Read all bytes
  389. if self._data is None:
  390. self._data = self._file.read()
  391. # Read bytes from string and convert to uint16
  392. raw = np.frombuffer(self._data, dtype=np.uint8).astype(np.uint16)
  393. # Rearrange bits
  394. img = LytroF01RawFormat.rearrange_bits(raw)
  395. else:
  396. img = np.array([])
  397. # Return image and meta data
  398. return img, self._get_meta_data(index=0)
  399. def _get_meta_data(self, index):
  400. # Get the meta data for the given index. If index is None, it
  401. # should return the global meta data.
  402. if index not in [0, None]:
  403. raise IndexError("Lytro meta data file contains only one dataset")
  404. # Try to read meta data from meta data file corresponding
  405. # to the raw data file, extension in [.txt, .TXT, .json, .JSON]
  406. filename_base = os.path.splitext(self.request.get_local_filename())[0]
  407. meta_data = None
  408. for ext in [".txt", ".TXT", ".json", ".JSON"]:
  409. if os.path.isfile(filename_base + ext):
  410. meta_data = json.load(open(filename_base + ext))
  411. if meta_data is not None:
  412. return meta_data
  413. else:
  414. logger.warning("No metadata file found for provided raw file.")
  415. return {}
  416. class LytroLfpFormat(LytroFormat):
  417. """This is the Lytro Illum LFP format.
  418. The lfp is a image and meta data container format as used by the
  419. Lytro F01 light field camera.
  420. The format will read the specified lfp file.
  421. This format does not support writing.
  422. Parameters for reading
  423. ----------------------
  424. meta_only : bool
  425. Whether to only read the metadata.
  426. include_thumbnail : bool
  427. Whether to include an image thumbnail in the metadata.
  428. """
  429. def _can_read(self, request):
  430. # Check if mode and extensions are supported by the format
  431. if request.extension in (".lfp",):
  432. return True
  433. # -- reader
  434. class Reader(Format.Reader):
  435. def _open(self, meta_only=False):
  436. self._file = self.request.get_file()
  437. self._data = None
  438. self._chunks = {}
  439. self.metadata = {}
  440. self._content = None
  441. self._meta_only = meta_only
  442. self._find_header()
  443. self._find_meta()
  444. self._find_chunks()
  445. try:
  446. # Get sha1 dict and check if it is in dictionary of data chunks
  447. chunk_dict = self._content["picture"]["frameArray"][0]["frame"]
  448. if (
  449. chunk_dict["metadataRef"] in self._chunks
  450. and chunk_dict["imageRef"] in self._chunks
  451. and chunk_dict["privateMetadataRef"] in self._chunks
  452. ):
  453. if not self._meta_only:
  454. # Read raw image data byte buffer
  455. data_pos, size = self._chunks[chunk_dict["imageRef"]]
  456. self._file.seek(data_pos, 0)
  457. self.raw_image_data = self._file.read(size)
  458. # Read meta data
  459. data_pos, size = self._chunks[chunk_dict["metadataRef"]]
  460. self._file.seek(data_pos, 0)
  461. metadata = self._file.read(size)
  462. # Add metadata to meta data dict
  463. self.metadata["metadata"] = json.loads(metadata.decode("ASCII"))
  464. # Read private metadata
  465. data_pos, size = self._chunks[chunk_dict["privateMetadataRef"]]
  466. self._file.seek(data_pos, 0)
  467. serial_numbers = self._file.read(size)
  468. self.serial_numbers = json.loads(serial_numbers.decode("ASCII"))
  469. # Add private metadata to meta data dict
  470. self.metadata["privateMetadata"] = self.serial_numbers
  471. except KeyError:
  472. raise RuntimeError("The specified file is not a valid LFP file.")
  473. def _close(self):
  474. # Close the reader.
  475. # Note that the request object will close self._file
  476. del self._data
  477. def _get_length(self):
  478. # Return the number of images. Can be np.inf
  479. return 1
  480. def _find_header(self):
  481. """
  482. Checks if file has correct header and skip it.
  483. """
  484. file_header = b"\x89LFP\x0d\x0a\x1a\x0a\x00\x00\x00\x01"
  485. # Read and check header of file
  486. header = self._file.read(HEADER_LENGTH)
  487. if header != file_header:
  488. raise RuntimeError("The LFP file header is invalid.")
  489. # Read first bytes to skip header
  490. self._file.read(SIZE_LENGTH)
  491. def _find_chunks(self):
  492. """
  493. Gets start position and size of data chunks in file.
  494. """
  495. chunk_header = b"\x89LFC\x0d\x0a\x1a\x0a\x00\x00\x00\x00"
  496. for i in range(0, DATA_CHUNKS_F01):
  497. data_pos, size, sha1 = self._get_chunk(chunk_header)
  498. self._chunks[sha1] = (data_pos, size)
  499. def _find_meta(self):
  500. """
  501. Gets a data chunk that contains information over content
  502. of other data chunks.
  503. """
  504. meta_header = b"\x89LFM\x0d\x0a\x1a\x0a\x00\x00\x00\x00"
  505. data_pos, size, sha1 = self._get_chunk(meta_header)
  506. # Get content
  507. self._file.seek(data_pos, 0)
  508. data = self._file.read(size)
  509. self._content = json.loads(data.decode("ASCII"))
  510. data = self._file.read(5) # Skip 5
  511. def _get_chunk(self, header):
  512. """
  513. Checks if chunk has correct header and skips it.
  514. Finds start position and length of next chunk and reads
  515. sha1-string that identifies the following data chunk.
  516. Parameters
  517. ----------
  518. header : bytes
  519. Byte string that identifies start of chunk.
  520. Returns
  521. -------
  522. data_pos : int
  523. Start position of data chunk in file.
  524. size : int
  525. Size of data chunk.
  526. sha1 : str
  527. Sha1 value of chunk.
  528. """
  529. # Read and check header of chunk
  530. header_chunk = self._file.read(HEADER_LENGTH)
  531. if header_chunk != header:
  532. raise RuntimeError("The LFP chunk header is invalid.")
  533. data_pos = None
  534. sha1 = None
  535. # Read size
  536. size = struct.unpack(">i", self._file.read(SIZE_LENGTH))[0]
  537. if size > 0:
  538. # Read sha1
  539. sha1 = str(self._file.read(SHA1_LENGTH).decode("ASCII"))
  540. # Skip fixed null chars
  541. self._file.read(PADDING_LENGTH)
  542. # Find start of data and skip data
  543. data_pos = self._file.tell()
  544. self._file.seek(size, 1)
  545. # Skip extra null chars
  546. ch = self._file.read(1)
  547. while ch == b"\0":
  548. ch = self._file.read(1)
  549. self._file.seek(-1, 1)
  550. return data_pos, size, sha1
  551. def _get_data(self, index):
  552. # Return the data and meta data for the given index
  553. if index not in [0, None]:
  554. raise IndexError("Lytro lfp file contains only one dataset")
  555. if not self._meta_only:
  556. # Read bytes from string and convert to uint16
  557. raw = np.frombuffer(self.raw_image_data, dtype=np.uint8).astype(
  558. np.uint16
  559. )
  560. im = LytroF01RawFormat.rearrange_bits(raw)
  561. else:
  562. im = np.array([])
  563. # Return array and dummy meta data
  564. return im, self.metadata
  565. def _get_meta_data(self, index):
  566. # Get the meta data for the given index. If index is None,
  567. # it returns the global meta data.
  568. if index not in [0, None]:
  569. raise IndexError("Lytro meta data file contains only one dataset")
  570. return self.metadata