importers.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. import datetime
  2. import pathlib as pl
  3. from tillvisionio.vws import VWSDataManager
  4. import pandas as pd
  5. import tifffile
  6. from view.python_core.misc import excel_datetime
  7. import typing
  8. import easygui
  9. import logging
  10. import pprint
  11. from abc import ABC, abstractmethod
  12. import xml.etree.ElementTree as ET
  13. from view.python_core.io import LIFReaderGio
  14. def calculate_dt_from_timing_ms(timing_ms: str) -> float:
  15. times = timing_ms.strip()
  16. times = [float(x) for x in times.split(' ')]
  17. # calculate frame rate as time of (last frame - first frame) / (frames-1)
  18. dt = (times[-1] - times[0]) / (len(times) - 1)
  19. return dt
  20. # a function injecting code into the automatic parsing of metadata from vws.log file
  21. def additional_cols_func(s):
  22. # time & analyze
  23. try:
  24. dt = calculate_dt_from_timing_ms(s["Timing_ms"])
  25. analyze = 1 # since there are at least two frames, and thus a time, I suppose it is worth analyzing
  26. except Exception as e:
  27. dt = -1
  28. analyze = 0
  29. return {"dt": dt, "Analyze": analyze}
  30. class BaseImporter(ABC):
  31. def __init__(self, default_values: typing.Mapping):
  32. super().__init__()
  33. self.default_values = default_values
  34. self.associated_extensions = None
  35. self.associate_file_type = None
  36. self.LE_loadExp = None
  37. self.movie_data_extensions = None
  38. def get_default_row(self):
  39. return pd.Series(self.default_values)
  40. def import_metadata(self, raw_data_files, measurement_filter):
  41. combined_df = pd.DataFrame()
  42. for fle_ind, fle in enumerate(raw_data_files):
  43. logging.getLogger("VIEW").info(f"Parsing metadata from {fle}")
  44. df = self.parse_metadata(fle, fle_ind, measurement_filter)
  45. combined_df = combined_df.append(df, ignore_index=True)
  46. return combined_df
  47. def get_filetype_info_string(self):
  48. return [f"*{x}" for x in self.associated_extensions] + [self.associate_file_type]
  49. def ask_for_files(self, default_dir, multiple: bool = True) -> dict:
  50. default_dir_str = str(pl.Path(default_dir) / "*")
  51. files_chosen = easygui.fileopenbox(
  52. title=f"Choose one or more files for LE_loadExp={self.LE_loadExp}",
  53. filetypes=self.get_filetype_info_string(),
  54. multiple=multiple,
  55. default=default_dir_str)
  56. if files_chosen is None:
  57. raise IOError("User Abort while choosing files.")
  58. else:
  59. assert files_chosen[0].startswith(str(default_dir)), \
  60. f"The data selected in not in the expected data directory of the current tree:\n" \
  61. f"{default_dir}. Please copy your data there and try again!"
  62. animal_tag_raw_data_mapping = self.get_animal_tag_raw_data_mapping(files_chosen)
  63. logging.getLogger("VIEW").info(
  64. f"Working on the following animal tags and their corresponding files:\n"
  65. f"{pprint.pformat(animal_tag_raw_data_mapping)}")
  66. return animal_tag_raw_data_mapping
  67. @abstractmethod
  68. def parse_metadata(self, fle: str, fle_ind: int,
  69. measurement_filter: typing.Callable[[pd.Series], bool]) -> pd.DataFrame:
  70. """
  71. Reads and returns the metadata from a metadata file
  72. :param str fle: path of a metadata file
  73. :param int fle_ind: integer representing the row order of the measurement associated with <fle>,
  74. if it is part of a series
  75. :param Callable measurement_filter: only used for Till Vision setups. See tillvisionio.VWSDataManager.get_all_metadata
  76. :rtype: pd.DataFrame
  77. :return: the columns of the DataFrame returned must be a subset of the metadata columns defined in `view/flags_and_metadata_definitions/metadata_definition.csv`
  78. """
  79. pass
  80. def get_animal_tag_raw_data_mapping(self, files_chosen: list) -> dict:
  81. """
  82. Returns a one-element dictionary with the animal tag as key and list of (revised) raw data files as value.
  83. Parameters
  84. ----------
  85. files_chosen : list
  86. list of lif files.
  87. Returns
  88. -------
  89. dict
  90. file names without path.
  91. """
  92. if len(files_chosen) == 0:
  93. return {}
  94. else:
  95. dict2return = {}
  96. for fle in files_chosen:
  97. fle_path = pl.Path(fle)
  98. dict2return[fle_path.name] = [fle]
  99. return dict2return
  100. @abstractmethod
  101. def get_path_relative_to_data_dir(self, fle):
  102. """
  103. Creates a string representing the path of the raw data file <fle> relative to the data directory represented
  104. by the flag "STG_Datapath" (Eg.: "01_DATA")
  105. :param fle: path of the raw data file as parsed from the metadata file
  106. :rtype: str
  107. """
  108. pass
  109. class TillImporter(BaseImporter, ABC):
  110. def __init__(self, default_values: typing.Mapping):
  111. super().__init__(default_values)
  112. self.associate_file_type = "VWS Log Files"
  113. self.associated_extensions = [".vws.log"]
  114. self.movie_data_extensions = [".pst", ".ps"]
  115. def get_animal_tag_raw_data_mapping(self, files_chosen: list) -> dict:
  116. if len(files_chosen) == 0:
  117. return {}
  118. else:
  119. dict2return = {}
  120. for fle in files_chosen:
  121. fle_path = pl.Path(fle)
  122. dict2return[fle_path.name.split(".")[0]] = [fle]
  123. return dict2return
  124. def get_path_relative_to_data_dir(self, fle):
  125. for extension in self.movie_data_extensions:
  126. if fle.endswith(extension):
  127. fle_path = pl.PureWindowsPath(fle)
  128. possible_dbb1 = str(pl.Path(fle_path.parts[-2]) / fle_path.stem)
  129. return 1, str(possible_dbb1)
  130. else:
  131. return 0, "wrong extension"
  132. def convert_vws_names_to_lst_names(self, vws_measurement_series, default_row):
  133. """
  134. Convert values from vws.log nomenclaure to internal measurement list nomenclature
  135. :param vws_measurement_series: pandas.Series
  136. :param default_row: pandas.Series with default values
  137. :return: pandas.series
  138. """
  139. logging.getLogger("VIEW").info(f'Parsing measurement with label {vws_measurement_series["Label"]}')
  140. lst_line = default_row.copy()
  141. lst_line['Measu'] = vws_measurement_series['index'] + 1
  142. lst_line['Label'] = vws_measurement_series['Label']
  143. expected_data_file = vws_measurement_series["Location"]
  144. if expected_data_file[-2:] == 'ps':
  145. # there is one version of the macro in tillVision that "eats" the last t of the file name
  146. logging.getLogger("VIEW").warning('adding a t to the .ps file name to make it .pst')
  147. expected_data_file += 't'
  148. analyze, dbb1_relative = self.get_path_relative_to_data_dir(expected_data_file)
  149. if analyze == 0:
  150. logging.getLogger("VIEW").warning(
  151. f"Data file {expected_data_file} not found! Setting analyze=0 for this measurement")
  152. lst_line['DBB1'] = dbb1_relative
  153. lst_line["Analyze"] = analyze * int(lst_line.get("Analyze", 1))
  154. lst_line['Cycle'] = vws_measurement_series["dt"]
  155. lst_line['Lambda'] = vws_measurement_series['MonochromatorWL_nm']
  156. lst_line['UTC'] = vws_measurement_series['UTCTime']
  157. return pd.DataFrame(lst_line).T
  158. def get_mtime(self, utc, first_utc):
  159. time_since_first_utc = pd.to_timedelta(utc - first_utc, unit="s")
  160. return str(time_since_first_utc).split(" days ")[1]
  161. class TillImporterOneWavelength(TillImporter):
  162. def __init__(self, default_values: typing.Mapping):
  163. super().__init__(default_values)
  164. self.LE_loadExp = 3
  165. # for till data, metadata is contained in vws.log file
  166. def parse_metadata(self, fle: str, fle_ind: int,
  167. measurement_filter: typing.Callable[[pd.Series], bool]) -> pd.DataFrame:
  168. vws_manager = VWSDataManager(fle)
  169. measurements = vws_manager.get_all_metadata(filter=measurement_filter,
  170. additional_cols_func=additional_cols_func)
  171. first_utc = vws_manager.get_earliest_utc()
  172. this_lst_frame = pd.DataFrame()
  173. if len(measurements) == 0:
  174. logging.getLogger("VIEW").warning(
  175. f"In {fle}: No usable measurements found for given 'measurement_filter' function")
  176. for measurement_index, measurement_row in measurements.iterrows():
  177. lst_line = self.convert_vws_names_to_lst_names(vws_measurement_series=measurement_row,
  178. default_row=self.get_default_row(),
  179. )
  180. lst_line["MTime"] = self.get_mtime(utc=lst_line["UTC"][0], first_utc=first_utc)
  181. this_lst_frame = this_lst_frame.append(lst_line, ignore_index=True)
  182. return this_lst_frame
  183. class TillImporterTwoWavelength(TillImporter):
  184. def __init__(self, default_values: typing.Mapping):
  185. super().__init__(default_values)
  186. self.LE_loadExp = 4
  187. def parse_metadata(self, fle: str, fle_ind: int,
  188. measurement_filter: typing.Callable[[pd.Series], bool]) -> pd.DataFrame:
  189. vws_manager = VWSDataManager(fle)
  190. measurements_wl340_df, measurements_wl380_df \
  191. = vws_manager.get_metadata_two_wavelengths(wavelengths=(340, 380), filter=measurement_filter,
  192. additional_cols_func=additional_cols_func)
  193. first_utc = vws_manager.get_earliest_utc()
  194. this_lst_frame = pd.DataFrame()
  195. for (ind1, measurement_wl340), (ind2, measurement_wl380) in zip(measurements_wl340_df.iterrows(),
  196. measurements_wl380_df.iterrows()):
  197. lst_line_wl340 = self.convert_vws_names_to_lst_names(measurement_wl340, self.get_default_row())
  198. lst_line_wl380 = self.convert_vws_names_to_lst_names(measurement_wl380, self.get_default_row())
  199. lst_line_wl340["dbb2"] = lst_line_wl380["DBB1"]
  200. lst_line_wl340["MTime"] = self.get_mtime(utc=lst_line_wl340["UTC"][0], first_utc=first_utc)
  201. lst_line_wl380["Analyze"] = 0
  202. lst_line_wl380["MTime"] = self.get_mtime(utc=lst_line_wl380["UTC"][0], first_utc=first_utc)
  203. this_lst_frame = this_lst_frame.append(lst_line_wl340, ignore_index=True)
  204. this_lst_frame = this_lst_frame.append(lst_line_wl380, ignore_index=True)
  205. return this_lst_frame
  206. class LifImporter(BaseImporter):
  207. """
  208. importer for Leica Confocal/2-Photon .lif files
  209. """
  210. def __init__(self, default_values: typing.Mapping):
  211. super().__init__(default_values)
  212. self.associate_file_type = "Leica .lif files" # short text describing raw data files
  213. self.associated_extensions = [".lif"] # possible extensions of files containing metadata
  214. self.movie_data_extensions = [".lif"] # possible extension of file containing data (calcium imaging movies)
  215. self.LE_loadExp = 21 # associated value of the flag LE_loadExp
  216. def get_path_relative_to_data_dir(self, fle):
  217. for movie_data_extension in self.movie_data_extensions:
  218. if fle.endswith(movie_data_extension):
  219. fle_path = pl.PureWindowsPath(fle)
  220. return 1, str(fle_path.stem)
  221. else:
  222. return 0, -1
  223. def convert_lif_metadata_to_lst_row(self, fle, measu, lif_metadata_single, default_row):
  224. """
  225. Convert values from lif metadata to .lst nomenclature
  226. for a particular measurement
  227. """
  228. lst_line = default_row.copy()
  229. lst_line["Label"] = lif_metadata_single["Label"]
  230. lst_line["Cycle"] = lif_metadata_single["Cycle"]
  231. lst_line["Lambda"] = lif_metadata_single["Lambda"]
  232. lst_line["PxSzX"] = lif_metadata_single["PxSzX"]
  233. lst_line["PxSzY"] = lif_metadata_single["PxSzX"] # y-size
  234. analyze, dbb1_relative = self.get_path_relative_to_data_dir(fle)
  235. lst_line["DBB1"] = dbb1_relative
  236. lst_line["Analyze"] = analyze
  237. lst_line["Measu"] = measu
  238. lst_line['SampFreq'] = lif_metadata_single["SampFreq"]
  239. lst_line['FrameSizeX'] = lif_metadata_single["FrameSizeX"]
  240. lst_line['FrameSizeY'] = lif_metadata_single["FrameSizeY"]
  241. lst_line['NumFrames'] = lif_metadata_single["NumFrames"]
  242. lst_line['Comment'] = lif_metadata_single["Comment"]
  243. lst_line["UTC"] = lif_metadata_single["UTC"]
  244. lst_line["MTime"] = lif_metadata_single["MTime"]
  245. return pd.DataFrame(lst_line).T
  246. def parse_metadata(self, fle: str, fle_ind: int,
  247. measurement_filter: typing.Callable[[pd.Series], bool] = True) -> pd.DataFrame:
  248. lif_reader = LIFReaderGio(fle)
  249. all_lif_metadata = lif_reader.load_all_metadata()
  250. this_lst_frame = pd.DataFrame()
  251. # iterate all measurements
  252. for fle_ind, lst_row in all_lif_metadata.iterrows():
  253. if lst_row["NumFrames"] > 1:
  254. lst_line = self.convert_lif_metadata_to_lst_row(
  255. fle, fle_ind, lst_row,
  256. default_row=self.get_default_row()
  257. )
  258. this_lst_frame = this_lst_frame.append(lst_line, ignore_index=True)
  259. return this_lst_frame
  260. class LSMImporter(BaseImporter):
  261. def __init__(self, default_values: typing.Mapping):
  262. super().__init__(default_values)
  263. self.associate_file_type = "Zeiss LSM files" # short text describing raw data files
  264. self.associated_extensions = [".lsm"] # possible extensions of files containing metadata
  265. self.movie_data_extensions = [".lsm"] # possible extension of file containing data (calcium imaging movies)
  266. self.LE_loadExp = 20 # associated value of the flag LE_loadExp
  267. def get_path_relative_to_data_dir(self, fle):
  268. for movie_data_extension in self.movie_data_extensions:
  269. if fle.endswith(movie_data_extension):
  270. fle_path = pl.PureWindowsPath(fle)
  271. return 1, str(pl.Path(fle_path.parts[-3]) / fle_path.parts[-2] / fle_path.stem)
  272. else:
  273. return 0, -1
  274. def convert_lsm_metadata_to_lst_row(self, measu, fle, lsm_metadata, default_row):
  275. """
  276. Convert values from lsm_metadata to .lst nomenclature
  277. :param lsm_metadata: dict, like the one returned by tifffile.TiffFile.lsm_metadata
  278. :param default_row: pandas.Series, with default values
  279. :return: pandas.Series
  280. """
  281. lst_line = default_row.copy()
  282. lst_line["Label"] = lsm_metadata["ScanInformation"]["Name"]
  283. # converting from seconds to milliseconds
  284. lst_line["Cycle"] = lsm_metadata["TimeIntervall"] * 1000
  285. lst_line["Lambda"] = lsm_metadata["ScanInformation"]["Tracks"][0]["IlluminationChannels"][0]["Wavelength"]
  286. lst_line['UTC'] = excel_datetime(lsm_metadata["ScanInformation"]["Sample0time"]).timestamp()
  287. # convert from meters to micrometers
  288. lst_line["PxSzX"] = lsm_metadata["VoxelSizeX"] / 1e-6
  289. lst_line["PxSzY"] = lsm_metadata["VoxelSizeY"] / 1e-6
  290. analyze, dbb1_relative = self.get_path_relative_to_data_dir(fle)
  291. lst_line["DBB1"] = dbb1_relative
  292. lst_line["Analyze"] = analyze
  293. lst_line["Measu"] = measu
  294. return pd.DataFrame(lst_line).T
  295. # for till data, a single raw data file is a .lsm file
  296. def parse_metadata(self, fle: str, fle_ind: int,
  297. measurement_filter: typing.Callable[[pd.Series], bool] = True) -> pd.DataFrame:
  298. lsm_metadata = tifffile.TiffFile(fle).lsm_metadata
  299. lst_row = self.convert_lsm_metadata_to_lst_row(measu=fle_ind + 1,
  300. fle=fle,
  301. lsm_metadata=lsm_metadata,
  302. default_row=self.get_default_row())
  303. return lst_row
  304. class P1DualWavelengthTIFSingleFileImporter(BaseImporter):
  305. # added Dec2021, to import single tiff file with dual wavelength as used in Trondheim
  306. # or also single wavelength (e.g. Ratio) tif files
  307. # init in view uses read_single_file_fura_tif(filename)
  308. def __init__(self, default_values: typing.Mapping):
  309. super().__init__(default_values)
  310. self.associate_file_type = "Dual Wavelength Tif files" # short text describing raw data files
  311. self.associated_extensions = [".tif"] # possible extensions of files containing metadata
  312. self.movie_data_extensions = [".tif"] # possible extension of file containing data (calcium imaging movies)
  313. self.LE_loadExp = 35 # associated value of the flag LE_loadExp
  314. def get_path_relative_to_data_dir(self, fle):
  315. for movie_data_extension in self.movie_data_extensions:
  316. if fle.endswith(movie_data_extension):
  317. fle_path = pl.PureWindowsPath(fle)
  318. return 1, str(pl.Path(fle_path.parts[-3]) / fle_path.parts[-2] / fle_path.stem)
  319. else:
  320. return 0, -1
  321. def convert_metadata_to_lst_row(self, measu, fle, meta_info, default_row):
  322. """
  323. Convert values from meta_info to .lst nomenclature
  324. :param meta_info['PsSzX']: dict, like the one returned by tifffile.TiffFile.lsm_metadata
  325. :param default_row: pandas.Series, with default values
  326. :return: pandas.Series
  327. """
  328. lst_line = default_row.copy()
  329. lst_line["Label"] = meta_info['Label']
  330. # converting from seconds to milliseconds
  331. lst_line["Cycle"] = meta_info['GDMfreq']
  332. lst_line["Lambda"] = meta_info['Lambda']
  333. lst_line['UTC'] = meta_info['UTCTime']
  334. # convert from meters to micrometers
  335. lst_line["PxSzX"] = meta_info['PsSzX']
  336. lst_line["PxSzY"] = meta_info['PsSzY']
  337. analyze, dbb1_relative = self.get_path_relative_to_data_dir(fle)
  338. lst_line["DBB1"] = meta_info['dbb1']
  339. lst_line["dbb2"] = meta_info['dbb2']
  340. lst_line["Analyze"] = analyze
  341. lst_line["Measu"] = measu
  342. #additional info
  343. lst_line["ExposureTime_ms"] = meta_info['ExposureTime_ms']
  344. lst_line["AcquisitionDate"] = meta_info['AcquisitionDate']
  345. lst_line["Binning"] = meta_info['Binning']
  346. lst_line["StartTime"] = meta_info['StartTime']
  347. return pd.DataFrame(lst_line).T
  348. # for till data, a single raw data file
  349. def parse_metadata(self, fle: str, fle_ind: int,
  350. measurement_filter: typing.Callable[[pd.Series], bool] = True) -> pd.DataFrame:
  351. # load metadata
  352. tif_file=pl.Path(fle)
  353. with tifffile.TiffFile(tif_file) as tif:
  354. metadata = tif.imagej_metadata
  355. # imagej_metadata does not work any more or never worked on stack - read metadata from first frame
  356. if metadata is None:
  357. metadata = tif.pages[0].description
  358. # extract XML tree from metadata into root
  359. root = ET.fromstring(metadata)
  360. # define namespace for OME data
  361. # this uses xTree OME syntax
  362. # https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element
  363. ns = {
  364. "d": "http://www.openmicroscopy.org/Schemas/OME/2013-06"
  365. }
  366. # now get all infos that we put into settings file
  367. meta_info = root.find("./d:Image/d:Pixels", ns).attrib
  368. # so far, this works with TillPhotonics .tif files for dual wavelengths (as saved in Trondheim group)
  369. # recognized by int(meta_info['SizeC']) == 2
  370. # saved sigle wavelength files have SizeC == 1
  371. # those settings that do noot exist there, are excluded for now
  372. # result is a dictionary, for example:
  373. # {'ID': 'Pixels:1-0',
  374. # 'DimensionOrder': 'XYTZC',
  375. # 'Type': 'uint16',
  376. # 'SizeX': '1392',
  377. # 'SizeY': '1040',
  378. # 'SizeZ': '1',
  379. # 'SizeC': '1',
  380. # 'SizeT': '160',
  381. # 'PhysicalSizeX': '6.45',
  382. # 'PhysicalSizeY': '6.45',
  383. # 'PhysicalSizeZ': '1000',
  384. # 'SignificantBits': '14'}
  385. # acquisition date as string, e.g. '2021-09-19T16:49:28'
  386. AcquisitionDate = root.find("./d:Image/d:AcquisitionDate", ns).text
  387. meta_info.update({'AcquisitionDate':AcquisitionDate})
  388. # columns in .settings that need to be filled here:
  389. # get the tif file, including the last directory
  390. this_filename = tif_file.parts
  391. dbb = this_filename[-2] +'/'+ this_filename[-1]
  392. meta_info.update({'dbb1':dbb})
  393. meta_info.update({'Label':this_filename[-1]})
  394. # PxSzX
  395. # replace the Andor name "PhysicalSizeX' with the Galizia name PsSzX
  396. meta_info['PsSzX'] = meta_info.pop('PhysicalSizeX')
  397. meta_info['PsSzY'] = meta_info.pop('PhysicalSizeY')
  398. # When was this measurement taken?
  399. # first get the time when the measurement was started: first frame
  400. first_frame = 1
  401. num_frames = int(meta_info['SizeT'])
  402. last_frame = num_frames * int(meta_info['SizeC'])
  403. root_text_first_frame = "./d:Image/d:Pixels/d:Plane["+str(first_frame)+"]"
  404. root_text_last_frame = "./d:Image/d:Pixels/d:Plane["+str(last_frame)+"]"
  405. time_frame1 = root.find(root_text_first_frame, ns).attrib["DeltaT"]
  406. time_frame_last = root.find(root_text_last_frame, ns).attrib["DeltaT"]
  407. # frame interval. Since this is dual wavelength,
  408. # take time from first to last, and divide by dimension T
  409. GDMfreq = (float(time_frame_last) - float(time_frame1)) / num_frames
  410. GDMfreq = round(GDMfreq*1000) # unit is ms, rounded
  411. meta_info.update({'GDMfreq':str(GDMfreq)})
  412. measurementtime = datetime.datetime.fromisoformat(AcquisitionDate)
  413. # now add the time of the first frame, since measurement start time ie equal for all measurements in one loop
  414. measurementtime_delta = datetime.timedelta(seconds=float(time_frame1))
  415. measurementtime = measurementtime + measurementtime_delta
  416. # StartTime, e.g. 10:05:04
  417. StartTime = measurementtime.strftime('%H:%M:%S')
  418. meta_info.update({'StartTime':StartTime})
  419. # UTC, e.g. 1623229504.482
  420. UTC = measurementtime.timestamp()
  421. meta_info.update({'UTCTime':UTC})
  422. # from here, information that is not available in .tif for saved ratios
  423. if int(meta_info['SizeC']) == 2:
  424. meta_info.update({'dbb2':dbb}) # copy filename also into column dbb2, since it is dual wavelength
  425. # binning info, e.g. '1x1'
  426. Binning = root.find("./d:Image/d:Pixels/d:Channel/d:DetectorSettings", ns).attrib["Binning"]
  427. meta_info.update({'Binning':Binning})
  428. # this format is for two-wavelength recording,
  429. # so I take exposure time for frame 3 and 4
  430. # just in case the very first one would be strange
  431. ExposureTime_ms = float(root.find("./d:Image/d:Pixels/d:Plane[3]", ns).attrib["ExposureTime"])
  432. ExposureTime_ms_340 = int(1000*ExposureTime_ms) # value in Andor is in seconds
  433. ExposureTime_ms = float(root.find("./d:Image/d:Pixels/d:Plane[4]", ns).attrib["ExposureTime"])
  434. ExposureTime_ms_380 = int(1000*ExposureTime_ms) # value in Andor is in seconds
  435. ExposureTimeStr = str(ExposureTime_ms_340)+'/'+str(ExposureTime_ms_380)
  436. meta_info.update({'ExposureTime_ms':ExposureTimeStr})
  437. meta_info.update({'Lambda':"340/380"}) #most likely this is FURA
  438. else: #single wavelength, ie. SizeC==1
  439. meta_info.update({'dbb2':'none'}) # copy filename also into column dbb2, since it is dual wavelength
  440. meta_info.update({'ExposureTime_ms':'unknown'})
  441. meta_info.update({'Binning':'unknown'})
  442. meta_info.update({'Lambda':"ratio of 340/380"}) #most likely this is ready made ratio
  443. ##example for meta_info now:
  444. # {'ID': 'Pixels:1-0',
  445. # 'DimensionOrder': 'XYCTZ',
  446. # 'Type': 'uint16',
  447. # 'SizeX': '336',
  448. # 'SizeY': '256',
  449. # 'SizeZ': '1',
  450. # 'SizeC': '2',
  451. # 'SizeT': '100',
  452. # 'PhysicalSizeZ': '1000',
  453. # 'SignificantBits': '16',
  454. # 'AcquisitionDate': '2019-08-14T14:44:29',
  455. # 'Binning': '4x4',
  456. # 'GDMfreq': '34',
  457. # 'ExposureTime_ms': '13',
  458. # 'dbb': '190815_h2_El/A_3.tif',
  459. # 'Label': 'A_3.tif',
  460. # 'PsSzX': '1.3',
  461. # 'PsSzY': '1.3',
  462. # 'StartTime': '14:44:29',
  463. # 'UTCTime': 1565786669.06601}
  464. lst_row = self.convert_metadata_to_lst_row(measu=fle_ind + 1,
  465. fle=fle,
  466. meta_info=meta_info,
  467. default_row=self.get_default_row())
  468. return lst_row
  469. def get_animal_tag_raw_data_mapping(self, files_chosen: list) -> dict:
  470. if len(files_chosen) == 0:
  471. return {}
  472. else:
  473. parents = [pl.Path(fle).parent for fle in files_chosen]
  474. assert all(x == parents[0] for x in parents), f"Tif files specified for constructing measurement " \
  475. f"list file do no belong to the same directory: " \
  476. f"{files_chosen}"
  477. return {parents[0].parent.name: files_chosen}
  478. def get_importer_class(LE_loadExp):
  479. if LE_loadExp == 3:
  480. return TillImporterOneWavelength
  481. elif LE_loadExp == 4:
  482. return TillImporterTwoWavelength
  483. elif LE_loadExp == 20:
  484. return LSMImporter
  485. elif LE_loadExp == 21:
  486. return LifImporter
  487. elif LE_loadExp == 33:
  488. # single wavelength TIFF
  489. return P1DualWavelengthTIFSingleFileImporter
  490. # works also for ratio files, not yet tested for other single file tif formats
  491. elif LE_loadExp == 35:
  492. return P1DualWavelengthTIFSingleFileImporter
  493. else:
  494. raise NotImplementedError
  495. def get_setup_extension(LE_loadExp):
  496. """
  497. returns the file extension of raw data file of the setup specified by <LE_loadExp>
  498. :param int LE_loadExp: value of the flag of the same name
  499. :rtype: list
  500. """
  501. importer_class = get_importer_class(LE_loadExp)
  502. return importer_class({}).movie_data_extension