__init__.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. from view.idl_translation_core.ViewLoadData import create_raw_data666
  2. from .metadata_related import MetadataDefinition, parse_p1_metadata_from_measurement_list_row
  3. from .filters import apply_filter
  4. from view.python_core.bleach_corr import get_bleach_compensator
  5. from view.python_core.background import get_background_frames
  6. from view.python_core.areas import get_area_for_p1, get_area_for_bleach_correction
  7. from view.python_core.measurement_list.importers import LSMImporter
  8. from view.python_core.foto import calc_foto1
  9. from view.python_core.io import load_pst, read_lsm, read_tif_2Dor3D, read_single_file_fura_tif, read_lif
  10. from view.python_core.paths import get_existing_raw_data_filename
  11. from view.python_core.stimuli import PulsedStimuliiHandler
  12. from view.python_core.calc_methods import get_calc_method
  13. import pathlib as pl
  14. import pandas as pd
  15. import copy
  16. import numpy as np
  17. from scipy.ndimage import gaussian_filter
  18. import logging
  19. import gc
  20. from abc import ABC, abstractmethod
  21. class P1SingleWavelengthAbstract(ABC):
  22. @abstractmethod
  23. def get_extensions(self):
  24. """
  25. list of allowed file extensions. E.g.: [".tif"]
  26. :rtype: list
  27. """
  28. pass
  29. @abstractmethod
  30. def read_data(self, filename: str):
  31. """
  32. read and return data in <filename>. Data is expected to be a numpy.ndarray of format XYT
  33. :param str filename: absolute path of raw data file on file system
  34. :rtype: numpy.ndarray
  35. """
  36. pass
  37. def __init__(self):
  38. super().__init__()
  39. self.metadata = None # pandas.Series or dict
  40. self.extra_metadata = None # dict
  41. self.raw1 = None # numpy.ndarray, format XYT
  42. self.raw2 = None # numpy.ndarray, format XYT
  43. self.foto1 = None # numpy.ndarray, format XY
  44. self.foto2 = None # numpy.ndarray, format XY
  45. self.sig1 = None # numpy.ndarray, format XYT
  46. self.area_mask = None # numpy.ndarray, format XY
  47. # an object of PulsedStimuliiHandler or some other subclass of BaseStimuliiHandler
  48. self.pulsed_stimuli_handler = None
  49. def __del__(self):
  50. to_del = [
  51. "metadata", "extra_metadata", "raw1", "raw2", "foto1", "foto2", "sig1",
  52. "pulsed_stimuli_handler", "area_mask"
  53. ]
  54. for obj in to_del:
  55. if hasattr(self, obj):
  56. eval(f"self.{obj}")
  57. gc.collect()
  58. def initialize_raw_data_size(self, raw_data_size):
  59. """
  60. Add metadata about the dimensions of raw data
  61. :param sequence raw_data_size: size of raw data
  62. """
  63. self.metadata.format_x, self.metadata.format_y, self.metadata.frames = raw_data_size
  64. def initialize_raw_data(self, raw_data):
  65. """
  66. Load raw data and initialize associated metadata
  67. :param list raw_data: list of raw data
  68. """
  69. self.initialize_raw_data_size(raw_data[0].shape)
  70. self.raw1 = raw_data[0]
  71. # calculate foto1
  72. self.foto1 = calc_foto1(self.raw1)
  73. # Loading Air not implemented
  74. def get_frame_size(self):
  75. """
  76. Return the frame size of this measurement as a 2 member tuple (XY)
  77. :rtype: tuple
  78. """
  79. return self.metadata.format_x, self.metadata.format_y
  80. def correct_raw_data(self, raw_data, p1_metadata, flags):
  81. # if the flag replace_init_frames is 2, replace the first two frames with the third
  82. frames2replace = flags["Data_ReplaceInitFrames"]
  83. if frames2replace < raw_data.shape[2]:
  84. temp = raw_data.swapaxes(0, 2) # convert TYX, otherwise the frame replacement takes more commands
  85. temp[:frames2replace, :, :] = temp[frames2replace, :, :]
  86. raw_data = temp.swapaxes(0, 2) # convert back to XYT
  87. else:
  88. logging.getLogger("VIEW").warning(
  89. f"value of the flag Data_ReplaceInitFrames ({flags['Data_ReplaceInitFrames']}) "
  90. f"is too large for the number of frames loaded ({raw_data.shape[2]}). Not Replacing any frames!")
  91. # apply median filter first, then mean filter, depending on flags
  92. raw_data = apply_filter(matrix_in=raw_data, view_flags=flags, filter_type="median")
  93. raw_data = apply_filter(matrix_in=raw_data, view_flags=flags, filter_type="mean")
  94. # apply light scattering compensation using unsharp masking
  95. # see Pg 376 of Galizia & Vetter(2004).
  96. # "Optical Methods for Analyzing Odor-Evoked Activity in the Insect Brain."
  97. # https://doi.org/10.1201/9781420039429.ch13
  98. smoothfactor = flags["LE_ScatteredLightFactor"]
  99. if smoothfactor > 0:
  100. corrected_raw_data = np.empty_like(raw_data)
  101. smoothradius_um = flags["LE_ScatteredLightRadius"]
  102. smX = smoothradius_um / p1_metadata.pixelsizex
  103. smY = smoothradius_um / p1_metadata.pixelsizey
  104. smoothradius = (smX + smY) / 2.0
  105. if smX != smY:
  106. logging.getLogger("VIEW").warning('unequal pixel size not implemented yet - averaging x and y value')
  107. for frame_ind in range(raw_data.shape[2]):
  108. current_frame = raw_data[:, :, frame_ind]
  109. # calculate correction
  110. correction = current_frame - gaussian_filter(current_frame, smoothradius, mode='nearest')
  111. # smooth correction so as to not enhance noise
  112. smoothed_correction = gaussian_filter(correction, smoothradius, mode='nearest')
  113. # apply correction
  114. corrected_raw_data[:, :, frame_ind] = current_frame + smoothfactor * smoothed_correction
  115. else:
  116. corrected_raw_data = raw_data
  117. # apply bleach correction depending on flags
  118. bleach_compensator = get_bleach_compensator(flags=flags, p1_metadata=p1_metadata, movie_size=raw_data.shape)
  119. area_mask_for_p1 = get_area_for_p1(frame_size=raw_data.shape[:2], flags=flags)
  120. area_mask_for_bleach_correction = get_area_for_bleach_correction(
  121. area_mask_for_p1, LE_BleachCutBorder=flags['LE_BleachCutBorder'],
  122. LE_BleachExcludeArea=flags["LE_BleachExcludeArea"])
  123. bleach_corrected_raw_data, bleach_fit_params = bleach_compensator.apply(
  124. stack_xyt=corrected_raw_data, area_mask=area_mask_for_bleach_correction)
  125. return area_mask_for_p1, bleach_corrected_raw_data, bleach_fit_params
  126. def load_correct_raw_data(self, p1_metadata, flags):
  127. """
  128. Reads data, applies median filters, applies mean filters, applies scatter light correction,
  129. applies bleach correction and return the resulting data. It also loads area from an area file
  130. if present, uses it for bleach correction and returns it
  131. :param pd.Series p1_metadata: metadata
  132. :param FlagsManager flags:
  133. :return: filename, area_mask_for_p1, bleach_corrected_raw_data, bleach_fit_params
  134. filename: absolute path of the file containing the raw data
  135. area_mask_for_p1: area mask read if an area file was found, else an numpy array of ones
  136. bleach_corrected_raw_data: processed raw data, as described above
  137. bleach_fit_params: params used for bleach fitting
  138. """
  139. # read raw1
  140. try:
  141. logging.getLogger("VIEW").info(f"Reading raw data")
  142. filename, raw_data = self.read_data_with_defaulting(metadata=p1_metadata, flags=flags)
  143. except FileNotFoundError as fnfe:
  144. raise IOError(
  145. f"Problem loading raw data from dbb1. Please check the measurement row selected in the "
  146. f"measurement list file. Original Error:\n {str(fnfe)}")
  147. area_mask_for_p1, bleach_corrected_raw_data, bleach_fit_params = self.correct_raw_data(
  148. raw_data=raw_data, p1_metadata=p1_metadata, flags=flags
  149. )
  150. return filename, area_mask_for_p1, [bleach_corrected_raw_data], bleach_fit_params
  151. def load_from_metadata(self, p1_metadata: pd.Series, flags, extra_metadata=None):
  152. # initialize give values of metadata and extra_metadata
  153. self.metadata = p1_metadata
  154. if extra_metadata is not None:
  155. self.extra_metadata = extra_metadata
  156. # initialize background frames.
  157. # Needed for movement and bleach correction, in addition to signal calculation
  158. self.metadata.background_frames = get_background_frames(p1_metadata=p1_metadata, flags=flags)
  159. # LE_ShrinkFacktor not implemented
  160. # load and correct raw data
  161. # self.area_mask is calculated inside self.load_correct_raw_data because it needs frame_size,
  162. # which can only be known after loading data
  163. filename, self.area_mask, bleach_corrected_raw_data, bleach_fit_params \
  164. = self.load_correct_raw_data(p1_metadata=p1_metadata, flags=flags)
  165. self.metadata.bleachpar = bleach_fit_params
  166. # initialize raw data
  167. self.initialize_raw_data(raw_data=bleach_corrected_raw_data)
  168. # saving the complete filename of raw data for reference
  169. self.metadata["full_raw_data_path_str"] = filename
  170. self.pulsed_stimuli_handler = self.metadata["pulsed_stimuli_handler"]
  171. data_sampling_period = pd.to_timedelta(self.metadata['trial_ticks'], unit='ms')
  172. self.pulsed_stimuli_handler.initialize_stimulus_offset(flags["mv_correctStimulusOnset"], data_sampling_period)
  173. def load_without_metadata(self, filenames, flags, sampling_rate=5):
  174. """
  175. load raw data in p1 structure directly, without other metadata
  176. :param sequence filenames: raw data file names, compatible with the flag `LE_loadExp`
  177. :param FlagManager flags:
  178. :param sampling_rate: in Hz, number of frames measured per second
  179. """
  180. p1_metadata, extra_metadata = self.get_p1_metadata_from_filenames(filenames=filenames)
  181. assert sampling_rate > 0, f"Invalid sampling rate specified ({sampling_rate})"
  182. p1_metadata['trial_ticks'] = 1000.0 / sampling_rate
  183. p1_metadata["frequency"] = sampling_rate
  184. self.load_from_metadata(p1_metadata=p1_metadata, flags=flags)
  185. def get_default_extension(self):
  186. return ".tif"
  187. def read_data_with_defaulting(self, metadata: pd.Series, flags):
  188. """
  189. Tries to find and read data with the extension given by <get_extension>. If file was not found, tries to find
  190. and read a '.tif.' file in the same indicated path.
  191. :param pandas.Series metadata:
  192. :param FlagsManager flags:
  193. :rtype: tuple
  194. :return: (filename, raw_data)
  195. filename: str, path of the file found
  196. raw_data: np.ndarray
  197. """
  198. current_extensions = self.get_extensions()
  199. default_extension = self.get_default_extension()
  200. try:
  201. filename = get_existing_raw_data_filename(flags=flags, dbb=metadata.dbb1, extensions=current_extensions)
  202. if pl.Path(filename).suffix in current_extensions:
  203. logging.getLogger("VIEW").info(f"(read_data_with_defaulting 1) Reading raw data from {filename}")
  204. if '.lif' in current_extensions:
  205. #a .lif file containse several measurement, read_data for measu only
  206. return filename, self.read_data(filename, flags.flags['STG_Measu'])
  207. else:
  208. return filename, self.read_data(filename)
  209. else:
  210. raise FileNotFoundError()
  211. except FileNotFoundError as fnfe:
  212. try:
  213. filename = get_existing_raw_data_filename(
  214. flags=flags, dbb=metadata.dbb1, extensions=[default_extension])
  215. if pl.Path(filename).suffix == default_extension:
  216. logging.getLogger("VIEW").info(f"(read_data_with_defaulting 2) Reading raw data from {filename}")
  217. data, _ = read_tif_2Dor3D(filename)
  218. return filename, data
  219. else:
  220. raise FileNotFoundError()
  221. except FileNotFoundError as fnfe:
  222. raise FileNotFoundError(f"{repr(fnfe)}.\n "
  223. f"Looked for data with extension '{current_extensions}' and "
  224. f"'{default_extension}'")
  225. def copy(self):
  226. new_p1 = self.__class__()
  227. new_p1.metadata = self.metadata.copy()
  228. new_p1.extra_metadata = self.extra_metadata.copy()
  229. new_p1.raw1 = self.raw1
  230. new_p1.foto1 = self.foto1
  231. new_p1.sig1 = self.sig1
  232. new_p1.pulsed_stimuli_handler = self.pulsed_stimuli_handler
  233. new_p1.raw2 = self.raw2
  234. new_p1.foto2 = self.foto2
  235. return new_p1
  236. def calculate_signals(self, flags):
  237. raw_data = self.get_raw_data()
  238. assert self.area_mask is not None
  239. assert self.metadata.background_frames is not None
  240. calc_method = get_calc_method(flags)
  241. self.sig1 = calc_method(
  242. raw_data=raw_data, background_frames=self.metadata.background_frames, area_mask=self.area_mask)
  243. def get_raw_data(self):
  244. assert self.raw1 is not None, "Cannot access raw data as they have not yet been loaded. Please" \
  245. "load some raw data using the methods 'load_from_metadata' or " \
  246. "'load_without_metadata'"
  247. return [self.raw1]
  248. def get_p1_metadata_from_filenames(self, filenames):
  249. """
  250. Create a p1_metadata object only using raw data filenames in <filenames>. Use defaults or guesses
  251. for metadata not directly deducible from raw data filenames
  252. :param list filenames: list of raw data file names
  253. :return: p1_metadata, extra_metadata
  254. p1_metadata: pandas.Series object,
  255. extra_metadata: dict
  256. (see view.python_core.p1_class.metadata_related.parse_p1_metadata_from_measurement_list_row)
  257. """
  258. p1_metadata, extra_metadata = Default_P1_Getter().get_p1_metadata_without_measurement_list()
  259. p1_metadata.dbb1 = filenames[0]
  260. label = pl.Path(filenames[0]).name.split(".")[0]
  261. p1_metadata.ex_name = label
  262. return p1_metadata, extra_metadata
  263. class P1SingleWavelengthTIF(P1SingleWavelengthAbstract):
  264. def __init__(self):
  265. super().__init__()
  266. def get_extensions(self):
  267. """
  268. list of allowed file extensions. E.g.: [".tif"]
  269. :rtype: list
  270. """
  271. return [".tif"]
  272. def read_data(self, filename: str):
  273. """
  274. read and return data in <filename>. Data is expected to be a numpy.ndarray of format XYT
  275. :param str filename: absolute path of raw data file on file system
  276. :rtype: numpy.ndarray
  277. """
  278. data, _ = read_tif_2Dor3D(filename)
  279. return data
  280. class P1SingleWavelengthLIF(P1SingleWavelengthAbstract):
  281. """
  282. Load Leica .lif file data
  283. """
  284. def __init__(self):
  285. super().__init__()
  286. def get_extensions(self):
  287. """
  288. list of allowed file extensions. E.g.: [".tif"]
  289. :rtype: list
  290. """
  291. return [".lif"]
  292. def read_data(self, filename: str, measu: int):
  293. """
  294. read and return data in <filename>, image <measu>.
  295. Data is expected to be a .lif file
  296. :param str filename: absolute path of lif data file on file system
  297. : param int measu: leica image number in the lif file
  298. :rtype: numpy.ndarray
  299. """
  300. data = read_lif(filename, measu)
  301. return data
  302. class P1SingleWavelengthTill(P1SingleWavelengthAbstract):
  303. def __init__(self):
  304. super().__init__()
  305. def get_extensions(self):
  306. """
  307. list of allowed file extensions. E.g.: [".tif"]
  308. :rtype: list
  309. """
  310. return [".pst", ".ps"]
  311. def read_data(self, filename):
  312. """
  313. read and return data in <filename>. Data is expected to be a numpy.ndarray of format XYT
  314. :param str filename: absolute path of raw data file on file system
  315. :rtype: numpy.ndarray
  316. """
  317. return load_pst(filename)
  318. class P1SingleWavelengthLSM(P1SingleWavelengthAbstract):
  319. def __init__(self):
  320. super().__init__()
  321. def get_extensions(self):
  322. """
  323. list of allowed file extensions. E.g.: [".tif"]
  324. :rtype: list
  325. """
  326. return [".lsm"]
  327. def read_data(self, filename):
  328. """
  329. read and return data in <filename>. Data is expected to be a numpy.ndarray of format XYT
  330. :param str filename: absolute path of raw data file on file system
  331. :rtype: numpy.ndarray
  332. """
  333. return read_lsm(filename)
  334. def get_p1_metadata_from_filenames(self, filenames):
  335. """
  336. Create a p1_metadata object only using raw data filenames in <filenames>. Use defaults or guesses
  337. for metadata not directly deducible from raw data filenames
  338. :param list filenames: list of raw data file names
  339. :return: p1_metadata, extra_metadata
  340. p1_metadata: pandas.Series object,
  341. extra_metadata: dict
  342. (see view.python_core.p1_class.metadata_related.parse_p1_metadata_from_measurement_list_row)
  343. """
  344. lsm_importer = LSMImporter(default_values=MetadataDefinition().get_default_row())
  345. # selection of the first row is required as this function returns a one-row DataFrame
  346. row = lsm_importer.parse_metadata(fle=filenames[0], fle_ind=-2).iloc[0]
  347. # revise index names to be lower case
  348. # row.rename(index={x: x.lower() for x in row.index.values}, inplace=True)
  349. p1_metadata, extra_metadata = parse_p1_metadata_from_measurement_list_row(row)
  350. p1_metadata.dbb1 = filenames[0]
  351. label = pl.Path(filenames[0]).name.split(".")[0]
  352. p1_metadata.ex_name = label
  353. return p1_metadata, extra_metadata
  354. class P1DualWavelengthAbstract(P1SingleWavelengthAbstract, ABC):
  355. @abstractmethod
  356. def get_extensions(self):
  357. """
  358. list of allowed file extensions. E.g.: [".tif"]
  359. :rtype: list
  360. """
  361. pass
  362. @abstractmethod
  363. def read_data(self, filename: str):
  364. """
  365. read and return data in <filename>. Data is expected to be a numpy.ndarray of format XYT
  366. :param str filename: absolute path of raw data file on file system
  367. :rtype: numpy.ndarray
  368. """
  369. pass
  370. @abstractmethod
  371. def get_p1_metadata_from_filenames(self, filenames):
  372. """
  373. Create a p1_metadata object only using raw data filenames in <filenames>. Use defaults or guesses
  374. for metadata not directly deducible from raw data filenames
  375. :param list filenames: list of raw data file names
  376. :return: p1_metadata, extra_metadata
  377. p1_metadata: pandas.Series object,
  378. extra_metadata: dict
  379. (see view.python_core.p1_class.metadata_related.parse_p1_metadata_from_measurement_list_row)
  380. """
  381. pass
  382. def __init__(self):
  383. super().__init__()
  384. def initialize_raw_data(self, raw_data):
  385. """
  386. Load raw data and initialize associated metadata
  387. :param list raw_data: list of raw data
  388. """
  389. self.raw1, self.raw2 = raw_data
  390. self.initialize_raw_data_size(self.raw1.shape)
  391. # calculate foto1
  392. self.foto1 = calc_foto1(self.raw1)
  393. # calculate foto2
  394. self.foto2 = calc_foto1(self.raw2)
  395. # Loading Air not implemented
  396. def get_raw_data(self):
  397. assert self.raw1 is not None and self.raw2 is not None, \
  398. "Cannot calculate signals as raw data has bot yet been loaded. Please" \
  399. "load some raw data using the methods 'load_from_metadata' or " \
  400. "'load_without_metadata'"
  401. return [self.raw1, self.raw2]
  402. class P1DualWavelengthTIFTwoFiles(P1DualWavelengthAbstract):
  403. def __init__(self):
  404. super().__init__()
  405. def get_extensions(self):
  406. """
  407. list of allowed file extensions. E.g.: [".tif"]
  408. :rtype: list
  409. """
  410. return [".tif"]
  411. def read_data(self, filename: str):
  412. """
  413. read and return data in <filename>. Data is expected to be a numpy.ndarray of format XYT
  414. :param str filename: absolute path of raw data file on file system
  415. :rtype: numpy.ndarray
  416. """
  417. data, _ = read_tif_2Dor3D(filename)
  418. return data
  419. def load_correct_raw_data(self, p1_metadata, flags):
  420. """
  421. Reads data, applies median filters, applies mean filters, applies scatter light correction,
  422. applies bleach correction and return the resulting data. It also loads area from an area file
  423. if present, uses it for bleach correction and returns it
  424. :param pd.Series p1_metadata: metadata
  425. :param FlagsManager flags:
  426. :return: filename, area_mask_for_p1, bleach_corrected_raw_data, bleach_fit_params
  427. filename: absolute path of the file containing the raw data
  428. area_mask_for_p1: area mask read if an area file was found, else an numpy array of ones
  429. bleach_corrected_raw_data: processed raw data, as described above
  430. bleach_fit_params: params used for bleach fitting
  431. """
  432. # read and correct raw1
  433. filename1, area_mask, [bleach_corrected_raw_data1], bleach_fit_params1 \
  434. = super().load_correct_raw_data(p1_metadata=p1_metadata, flags=flags)
  435. # read raw2
  436. metadata_copy = copy.copy(self.metadata)
  437. metadata_copy.dbb1 = metadata_copy.dbb2
  438. filename2, area_mask, [bleach_corrected_raw_data2], bleach_fit_params2 \
  439. = super().load_correct_raw_data(p1_metadata=metadata_copy, flags=flags)
  440. # make sure the shapes of data belonging to the two wavelength match
  441. assert bleach_corrected_raw_data1.shape == bleach_corrected_raw_data2.shape, \
  442. f"Shapes of the movie data of the two wavelengths do not match: " \
  443. f"{bleach_corrected_raw_data1.shape}, {bleach_corrected_raw_data2.shape}"
  444. return \
  445. filename1, area_mask, [bleach_corrected_raw_data1, bleach_corrected_raw_data2], \
  446. [bleach_fit_params1, bleach_fit_params2]
  447. def get_p1_metadata_from_filenames(self, filenames):
  448. """
  449. Create a p1_metadata object only using raw data filenames in <filenames>. Use defaults or guesses
  450. for metadata not directly deducible from raw data filenames
  451. :param list filenames: list of raw data file names
  452. :return: p1_metadata, extra_metadata
  453. p1_metadata: pandas.Series object,
  454. extra_metadata: dict
  455. (see view.python_core.p1_class.metadata_related.parse_p1_metadata_from_measurement_list_row)
  456. """
  457. dbb1_filename, dbb2_filename = filenames
  458. p1_metadata, extra_metadata = Default_P1_Getter().get_p1_metadata_without_measurement_list()
  459. p1_metadata.dbb1 = dbb1_filename
  460. p1_metadata.dbb2 = dbb2_filename
  461. dbb1_stem = pl.Path(dbb1_filename).name.split(".")[0]
  462. dbb2_stem = pl.Path(dbb2_filename).name.split(".")[0]
  463. label = f"{dbb1_stem}_{dbb2_stem}"
  464. p1_metadata.ex_name = label
  465. return p1_metadata, extra_metadata
  466. class P1DualWavelengthTill(P1DualWavelengthTIFTwoFiles):
  467. def __init__(self):
  468. super().__init__()
  469. def get_extensions(self):
  470. """
  471. list of allowed file extensions. E.g.: [".tif"]
  472. :rtype: list
  473. """
  474. return [".pst", ".ps"]
  475. def read_data(self, filename):
  476. """
  477. read and return data in <filename>. Data is expected to be a numpy.ndarray of format XYT
  478. :param str filename: absolute path of raw data file on file system
  479. :rtype: numpy.ndarray
  480. """
  481. return load_pst(filename)
  482. class P1SingleWavelength666(P1SingleWavelengthAbstract):
  483. def __init__(self, peaksignal=10):
  484. super().__init__()
  485. self.peaksignal = peaksignal
  486. def read_data_with_defaulting(self, metadata: pd.Series, flags):
  487. """
  488. Creates fake data 666 and returns it with a fake filename
  489. :param pandas.Series metadata:
  490. :param FlagsManager flags:
  491. :rtype: tuple
  492. :return: (filename, raw_data)
  493. filename: str, path of the file found
  494. raw_data: np.ndarray
  495. """
  496. raw_data666 = create_raw_data666(p1_metadata=metadata, peaksignal=self.peaksignal)
  497. return str(pl.Path(flags["STG_Datapath"]) / f"{metadata.dbb1}.fake"), raw_data666
  498. def load_without_metadata(self, filenames, flags, sampling_rate=5):
  499. """
  500. load raw data in p1 structure directly, without other metadata
  501. :param sequence filenames: raw data file names, compatible with the flag `LE_loadExp`
  502. :param FlagManager flags:
  503. :param sampling_rate: unused, hardcoded below
  504. """
  505. # for all other cases, it is better to not override the current method and instead override
  506. # get_p1_metadata_from_filenames. However, I am overriding the current method as an exception as
  507. # I need to make sure that stimulus information gets added, without which "loading", i.e., creating synthetic
  508. # data would fail.
  509. label = pl.Path(filenames[0]).name.split(".")[0]
  510. fake_measurement_list_row = MetadataDefinition().get_default_row()
  511. fake_measurement_list_row.update(
  512. {
  513. "DBB1": label,
  514. "Label": label,
  515. # need to add stimulus information here as it is later needed when loading data, i.e., in this
  516. # case, creating synthetic data
  517. "StimON": 25,
  518. "StimOFF": 35,
  519. "Stim2ON": 65,
  520. "Stim2OFF": 75
  521. }
  522. )
  523. p1_metadata, extra_metadata = parse_p1_metadata_from_measurement_list_row(fake_measurement_list_row)
  524. self.load_from_metadata(p1_metadata=p1_metadata, flags=flags, extra_metadata=extra_metadata)
  525. def read_data(self, filename: str):
  526. raise NotImplementedError
  527. def get_extensions(self):
  528. raise NotImplementedError
  529. class P1SingleWavelength676(P1SingleWavelength666):
  530. def __init__(self, peaksignal):
  531. super().__init__(peaksignal)
  532. def read_data_with_defaulting(self, metadata: pd.Series, flags):
  533. """
  534. Creates fake data 666, restrict it to 10x10x10 and returns it with a fake filename
  535. :param pandas.Series metadata:
  536. :param FlagsManager flags:
  537. :rtype: tuple
  538. :return: (filename, raw_data)
  539. filename: str, path of the file found
  540. raw_data: np.ndarray
  541. """
  542. raw_data666 = create_raw_data666(p1_metadata=metadata, peaksignal=self.peaksignal)
  543. return str(pl.Path(flags["STG_Datapath"]) / f"{metadata.dbb1}.fake"), raw_data666[:50, :50, :50]
  544. def read_data(self, filename: str):
  545. raise NotImplementedError
  546. def get_extensions(self):
  547. raise NotImplementedError
  548. class P1DualWavelengthTIFSingleFile(P1DualWavelengthAbstract):
  549. #created Dec 2021 for Till Trondheim Data: 340 & 380 in one file
  550. def __init__(self):
  551. super().__init__()
  552. def get_extensions(self):
  553. """
  554. list of allowed file extensions. E.g.: [".tif"]
  555. :rtype: list
  556. """
  557. return [".tif"]
  558. def read_data(self, filename: str):
  559. """
  560. read and return data in <filename>.
  561. :param str filename: absolute path of raw data file on file system
  562. :rtype: tuple
  563. :returns: data_340, data_380
  564. data_340: 340nm data as an numpy.ndarray, format XYT
  565. data_380: 340nm data as an numpy.ndarray, format XYT
  566. """
  567. return read_single_file_fura_tif(filename)
  568. def load_correct_raw_data(self, p1_metadata, flags):
  569. """
  570. Reads data, applies median filters, applies mean filters, applies scatter light correction,
  571. applies bleach correction and return the resulting data. It also loads area from an area file
  572. if present, uses it for bleach correction and returns it
  573. :param pd.Series p1_metadata: metadata
  574. :param FlagsManager flags:
  575. :return: filename, area_mask_for_p1, bleach_corrected_raw_data, bleach_fit_params
  576. filename: absolute path of the file containing the raw data
  577. area_mask_for_p1: area mask read if an area file was found, else an numpy array of ones
  578. bleach_corrected_raw_data: processed raw data, as described above
  579. bleach_fit_params: params used for bleach fitting
  580. """
  581. # read raw data
  582. try:
  583. logging.getLogger("VIEW").info(f"Reading raw data from ")
  584. filename, raw_data = self.read_data_with_defaulting(metadata=p1_metadata, flags=flags)
  585. except FileNotFoundError as fnfe:
  586. raise IOError(
  587. f"Problem loading raw data from dbb1. Please check the measurement row selected in the "
  588. f"measurement list file. Original Error:\n {str(fnfe)}")
  589. area_mask_for_p1, bleach_corrected_raw_data_340, bleach_fit_params_340 = self.correct_raw_data(
  590. raw_data=raw_data[0], p1_metadata=p1_metadata, flags=flags
  591. )
  592. area_mask_for_p1, bleach_corrected_raw_data_380, bleach_fit_params_380 = self.correct_raw_data(
  593. raw_data=raw_data[1], p1_metadata=p1_metadata, flags=flags
  594. )
  595. return \
  596. filename, area_mask_for_p1, \
  597. [bleach_corrected_raw_data_340, bleach_corrected_raw_data_380], \
  598. [bleach_fit_params_340, bleach_fit_params_380]
  599. def get_p1_metadata_from_filenames(self, filenames):
  600. """
  601. Create a p1_metadata object only using raw data filenames in <filenames>. Use defaults or guesses
  602. for metadata not directly deducible from raw data filenames
  603. :param list filenames: list of raw data file names
  604. :return: p1_metadata, extra_metadata
  605. p1_metadata: pandas.Series object,
  606. extra_metadata: dict
  607. (see view.python_core.p1_class.metadata_related.parse_p1_metadata_from_measurement_list_row)
  608. """
  609. p1_metadata, extra_metadata = Default_P1_Getter().get_p1_metadata_without_measurement_list()
  610. p1_metadata.dbb1 = filenames[0]
  611. label = pl.Path(filenames[0]).name.split(".")[0]
  612. p1_metadata.ex_name = label
  613. p1_metadata.lambda_nm = "380, 340"
  614. return p1_metadata, extra_metadata
  615. def get_empty_p1(LE_loadExp, odor_conc=None):
  616. if LE_loadExp == 3:
  617. empty_obj = P1SingleWavelengthTill()
  618. elif LE_loadExp == 4:
  619. empty_obj = P1DualWavelengthTill()
  620. elif LE_loadExp == 20:
  621. empty_obj = P1SingleWavelengthLSM()
  622. elif LE_loadExp == 21:
  623. empty_obj = P1SingleWavelengthLIF()
  624. elif LE_loadExp == 33:
  625. empty_obj = P1SingleWavelengthTIF()
  626. elif LE_loadExp == 34:
  627. empty_obj = P1DualWavelengthTIFTwoFiles()
  628. elif LE_loadExp == 35:
  629. empty_obj = P1DualWavelengthTIFSingleFile()
  630. elif LE_loadExp == 665: # synthetic data set, response negative, fixed response magnitude
  631. empty_obj = P1SingleWavelength666(peaksignal=-10)
  632. elif LE_loadExp == 666: # synthetic data set, response magnitude taken from list
  633. empty_obj = P1SingleWavelength666(peaksignal=odor_conc)
  634. elif LE_loadExp == 667: # synthetic data set, response positive, fixed response magnitude
  635. empty_obj = P1SingleWavelength666(peaksignal=10)
  636. elif LE_loadExp == 676: # synthetic data set 666, clipped
  637. empty_obj = P1SingleWavelength676(peaksignal=10)
  638. else:
  639. raise NotImplementedError
  640. return empty_obj
  641. def get_p1(p1_metadata, flags, extra_metadata):
  642. empty_obj = get_empty_p1(flags["LE_loadExp"], p1_metadata.get("odor_nr", None))
  643. empty_obj.load_from_metadata(p1_metadata, flags, extra_metadata)
  644. return empty_obj
  645. class Default_P1_Getter():
  646. def __init__(self):
  647. super().__init__()
  648. self._metadata_def = MetadataDefinition()
  649. def get_p1_metadata_without_measurement_list(self):
  650. """
  651. returns an pandas.Series object with all default values.
  652. :return: metadata of p1 object with all defaults
  653. :rtype: pandas.Series
  654. """
  655. default_row = self._metadata_def.get_default_row()
  656. default_p1_metadata, default_extra_metadata = parse_p1_metadata_from_measurement_list_row(default_row)
  657. default_p1_metadata.pulsed_stimuli_handler = PulsedStimuliiHandler()
  658. return default_p1_metadata, default_extra_metadata
  659. def get_fake_p1_from_raw(self, raw1, raw2=None):
  660. """
  661. returns a p1 object with default values and values related to raw data
  662. """
  663. fake_p1 = P1SingleWavelengthTIF()
  664. fake_p1.metadata, fake_p1.extra_metadata = self.get_p1_metadata_without_measurement_list()
  665. fake_p1.metadata.format_x, fake_p1.metadata.format_y, fake_p1.metadata.frames = raw1.shape
  666. fake_p1.raw1 = raw1
  667. fake_p1.pulsed_stimuli_handler = fake_p1.metadata["pulsed_stimuli_handler"]
  668. fake_p1.foto1 = calc_foto1(raw1)
  669. if raw2 is not None and raw1.shape == raw2.shape:
  670. fake_p1.raw2 = raw2
  671. fake_p1.foto2 = calc_foto1(raw2)
  672. return fake_p1