You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

407 lines
15 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. import numpy as np
  2. import xarray as xr
  3. import copy
  4. from DataContainer.ReadData import read_hdf5_file
  5. from Analyser.FringeRemoval import FringeRemoval
  6. from ToolFunction.ToolFunction import get_scanAxis
  7. class ImageAnalyser():
  8. """A class for operate with and analyse images
  9. """
  10. def __init__(self) -> None:
  11. """Initialize the class
  12. """
  13. self._image_name = {
  14. 'atoms': 'atoms',
  15. 'background': 'background',
  16. 'dark': 'dark',
  17. 'OD':'OD',
  18. 'optimumBackground':'optimumBackground'
  19. }
  20. self._center = None
  21. self._span = None
  22. self._fraction = None
  23. self._fringeRemoval = FringeRemoval()
  24. self.fringeRemovalReferenceImages = None
  25. @property
  26. def image_name(self):
  27. """The getter of the names of three standard images for absorption images
  28. :return: The names of three standard images for absorption images
  29. :rtype: dict
  30. """
  31. return self._image_name
  32. @image_name.setter
  33. def image_name(self, value):
  34. """The setter of the names of three standard images for absorption images.
  35. It has to be a dict and have the follow format:
  36. {
  37. 'atoms': the name of the image with atoms,
  38. 'background': the name of the image without atoms,
  39. 'dark': the name of the image without light,
  40. 'OD': the name of the calculated OD,
  41. }
  42. :param value: The names of three standard images for absorption images.
  43. :type value: dict
  44. """
  45. self._image_name.update(value)
  46. @property
  47. def center(self):
  48. """The getter of the center of region of insterest (ROI)
  49. :return: The center of region of insterest (ROI)
  50. :rtype: tuple
  51. """
  52. return self._center
  53. @center.setter
  54. def center(self, value):
  55. """The setter of the center of region of insterest (ROI)
  56. :param value: The center of region of insterest (ROI)
  57. :type value: tuple
  58. """
  59. self._center = value
  60. @property
  61. def span(self):
  62. """The getter of the span of region of insterest (ROI)
  63. :return: The span of region of insterest (ROI)
  64. :rtype: tuple
  65. """
  66. return self._span
  67. @span.setter
  68. def span(self, value):
  69. """The setter of the span of region of insterest (ROI)
  70. :param value: The span of region of insterest (ROI)
  71. :type value: tuple
  72. """
  73. self._span = value
  74. @property
  75. def fraction(self):
  76. """The getter of the fraction used to remove the background in the region of insterest (ROI)
  77. :return: The fraction used to remove the background
  78. :rtype: tuple
  79. """
  80. return self._fraction
  81. @fraction.setter
  82. def fraction(self, value):
  83. """The setter of the fraction used to remove the background in the region of insterest (ROI)
  84. :param value: The fraction used to remove the background
  85. :type value: tuple
  86. """
  87. self._fraction = value
  88. def get_offset_from_corner(self, dataArray, x_fraction=None, y_fraction=None, fraction=None, xAxisName='x', yAxisName='y'):
  89. """Calculate the background value based on the four coners of the image.
  90. :param dataArray: The image
  91. :type dataArray: xarray DataArray
  92. :param x_fraction: The fraction of the pixels used x in axis, defaults to None
  93. :type x_fraction: float, optional
  94. :param y_fraction: The fraction of the pixels used y in axis, defaults to None
  95. :type y_fraction: float, optional
  96. :param fraction: The fraction of the pixels used to calculate the background, defaults to None
  97. :type fraction: tuple, optional
  98. :param xAxisName: The name of the x axis in dataArray, defaults to 'x'
  99. :type xAxisName: str, optional
  100. :param yAxisName: The name of the y axis in dataArray, defaults to 'y'
  101. :type yAxisName: str, optional
  102. :return: The value of the background
  103. :rtype: float
  104. """
  105. if fraction is None:
  106. if x_fraction is None:
  107. x_fraction = self._fraction[0]
  108. if y_fraction is None:
  109. y_fraction = self._fraction[1]
  110. else:
  111. x_fraction = fraction[0]
  112. y_fraction = fraction[1]
  113. x_number = dataArray[xAxisName].shape[0]
  114. y_number = dataArray[yAxisName].shape[0]
  115. mean = dataArray.isel(x=slice(0, int(x_number * x_fraction)), y=slice(0 , int(y_number * y_fraction))).mean(dim=[xAxisName, yAxisName])
  116. mean += dataArray.isel(x=slice(0, int(x_number * x_fraction)), y=slice(int(y_number - y_number * y_fraction) , int(y_number))).mean(dim=[xAxisName, yAxisName])
  117. mean += dataArray.isel(x=slice(int(x_number - x_number * x_fraction) , int(x_number)), y=slice(0 , int(y_number * y_fraction))).mean(dim=[xAxisName, yAxisName])
  118. mean += dataArray.isel(x=slice(int(x_number - x_number * x_fraction) , int(x_number)), y=slice(int(y_number - y_number * y_fraction) , int(y_number))).mean(dim=[xAxisName, yAxisName])
  119. return mean / 4
  120. def substract_offset(self, dataArray, **kwargs):
  121. """Remove the backgournd
  122. :param dataArray: The image
  123. :type dataArray: xarray DataArray
  124. :return: The image after removing background
  125. :rtype: xarray DataArray
  126. """
  127. res = dataArray - self.get_offset_from_corner(dataArray, **kwargs)
  128. res.attrs = copy.copy(dataArray.attrs)
  129. return res
  130. def crop_image(self, dataSet, center=None, span=None, fringeRemoval=False):
  131. """Crop the image according to the region of interest (ROI).
  132. :param dataSet: The images
  133. :type dataSet: xarray DataArray or DataSet
  134. :param center: The center of region of insterest (ROI), defaults to None
  135. :type center: tuple, optional
  136. :param span: the span of region of insterest (ROI), defaults to None
  137. :type span: tuple, optional
  138. :param fringeRemoval: If also crop the reference background images for finges removal function, defaults to False
  139. :type fringeRemoval: bool, optional
  140. :return: The croped images
  141. :rtype: xarray DataArray or DataSet
  142. """
  143. if center is None:
  144. center = self._center
  145. if span is None:
  146. span = self._span
  147. x_start = int(center[0] - span[0] / 2)
  148. x_end = int(center[0] + span[0] / 2)
  149. y_end = int(center[1] + span[1] / 2)
  150. y_start = int(center[1] - span[1] / 2)
  151. dataSet.attrs['x_start'] = x_start
  152. dataSet.attrs['x_end'] = x_end
  153. dataSet.attrs['y_end'] = y_end
  154. dataSet.attrs['y_start'] = y_start
  155. dataSet.attrs['x_center'] = center[0]
  156. dataSet.attrs['y_center'] = center[1]
  157. dataSet.attrs['x_span'] = span[0]
  158. dataSet.attrs['y_span'] = span[1]
  159. if isinstance(dataSet, type(xr.Dataset())):
  160. for key in list(dataSet.data_vars):
  161. dataSet[key].attrs['x_start'] = x_start
  162. dataSet[key].attrs['x_end'] = x_end
  163. dataSet[key].attrs['y_end'] = y_end
  164. dataSet[key].attrs['y_start'] = y_start
  165. dataSet[key].attrs['x_center'] = center[0]
  166. dataSet[key].attrs['y_center'] = center[1]
  167. dataSet[key].attrs['x_span'] = span[0]
  168. dataSet[key].attrs['y_span'] = span[1]
  169. if fringeRemoval:
  170. scanAxis = list(get_scanAxis(self.fringeRemovalReferenceImages))
  171. if not scanAxis[1] is None:
  172. self._fringeRemoval.referenceImages = self.fringeRemovalReferenceImages.isel(x=slice(x_start, x_end), y=slice(y_start, y_end)).stack(_imgIdx=scanAxis)
  173. else:
  174. self._fringeRemoval.referenceImages = self.fringeRemovalReferenceImages.isel(x=slice(x_start, x_end), y=slice(y_start, y_end))
  175. return dataSet.isel(x=slice(x_start, x_end), y=slice(y_start, y_end))
  176. def get_OD(self, imageAtom, imageBackground, imageDrak):
  177. """Calculate the OD image for absorption imaging.
  178. :param imageAtom: The image with atoms
  179. :type imageAtom: numpy array
  180. :param imageBackground: The image without atoms
  181. :type imageBackground: numpy array
  182. :param imageDrak: The image without light
  183. :type imageDrak: numpy array
  184. :return: The OD images
  185. :rtype: numpy array
  186. """
  187. numerator = np.atleast_1d(imageBackground - imageDrak)
  188. denominator = np.atleast_1d(imageAtom - imageDrak)
  189. numerator[numerator == 0] = 1
  190. denominator[denominator == 0] = 1
  191. imageOD = np.abs(np.divide(denominator, numerator))
  192. imageOD= -np.log(imageOD)
  193. if len(imageOD) == 1:
  194. return imageOD[0]
  195. else:
  196. return imageOD
  197. def get_OD_no_dark(self, imageAtom, imageBackground):
  198. """Calculate the OD image for absorption imaging without dark images.
  199. :param imageAtom: The image with atoms
  200. :type imageAtom: numpy array
  201. :param imageBackground: The image without atoms
  202. :type imageBackground: numpy array
  203. :param imageDrak: The image without light
  204. :type imageDrak: numpy array
  205. :return: The OD images
  206. :rtype: numpy array
  207. """
  208. numerator = np.atleast_1d(imageBackground)
  209. denominator = np.atleast_1d(imageAtom)
  210. numerator[numerator == 0] = 1
  211. denominator[denominator == 0] = 1
  212. imageOD = np.abs(np.divide(denominator, numerator))
  213. imageOD= -np.log(imageOD)
  214. if len(imageOD) == 1:
  215. return imageOD[0]
  216. else:
  217. return imageOD
  218. def get_Ncount(self, dataSet, dim=['x', 'y'], **kwargs):
  219. """Sum all the value in the image to give the Ncount.
  220. :param dataSet: The images
  221. :type dataSet: xarray DataArray or DataSet
  222. :param dim: The dimensions to take the sumation, defaults to ['x', 'y']
  223. :type dim: list, optional
  224. :return: The Ncount
  225. :rtype: xarray DataArray or DataSet
  226. """
  227. return dataSet.sum(dim=['x', 'y'], **kwargs)
  228. def get_absorption_images(self, dataSet, fringeRemoval=False, dask='allowed', keep_attrs=True, **kwargs):
  229. """Calculate the OD images for absorption imaging.
  230. :param dataSet: The data from absorption imaging.
  231. :type dataSet: xarray DataSet
  232. :param fringeRemoval: If use fringe removal function, defaults to False
  233. :type fringeRemoval: bool, optional
  234. :param dask: over write of the same argument in xarray.apply_ufunc, defaults to 'allowed'
  235. :type dask: str, optional
  236. :param keep_attrs: over write of the same argument in xarray.apply_ufunc, defaults to True
  237. :type keep_attrs: bool, optional
  238. :return: The data including the OD images.
  239. :rtype: xarray DataSet
  240. """
  241. kwargs.update(
  242. {
  243. 'dask': dask,
  244. 'keep_attrs': keep_attrs,
  245. }
  246. )
  247. if fringeRemoval:
  248. dataSetAtoms = dataSet[self._image_name['atoms']] - dataSet[self._image_name['dark']]
  249. scanAxis = list(get_scanAxis(dataSet))
  250. if not scanAxis[1] is None:
  251. OptimumRef = self._fringeRemoval.fringe_removal(dataSetAtoms.stack(_imgIdx=scanAxis))
  252. else:
  253. OptimumRef = self._fringeRemoval.fringe_removal(dataSetAtoms)
  254. dataSet = dataSet.assign(
  255. {
  256. self._image_name['optimumBackground']: OptimumRef
  257. }
  258. )
  259. dataSet = dataSet.assign(
  260. {
  261. self._image_name['OD']: xr.apply_ufunc(self.get_OD_no_dark, dataSetAtoms, dataSet[self._image_name['optimumBackground']], **kwargs)
  262. }
  263. )
  264. else:
  265. dataSet = dataSet.assign(
  266. {
  267. self._image_name['OD']: xr.apply_ufunc(self.get_OD, dataSet[self._image_name['atoms']], dataSet[self._image_name['background']], dataSet[self._image_name['dark']], **kwargs)
  268. }
  269. )
  270. # dataSet[self._image_name['OD']].attrs.update(dataSet.attrs)
  271. return dataSet
  272. def remove_background(self, dataSet, dask='allowed', keep_attrs=True, **kwargs):
  273. """Remove the background for the given image data
  274. :param dataSet: The data from absorption imaging.
  275. :type dataSet: xarray DataSet
  276. :param dask: over write of the same argument in xarray.apply_ufunc, defaults to 'allowed'
  277. :type dask: str, optional
  278. :param keep_attrs: over write of the same argument in xarray.apply_ufunc, defaults to True
  279. :type keep_attrs: bool, optional
  280. """
  281. kwargs.update(
  282. {
  283. 'dask': dask,
  284. 'keep_attrs': keep_attrs,
  285. }
  286. )
  287. xr.apply_ufunc(self.get_OD, dataSet[self._image_name['atoms']], dataSet[self._image_name['background']], dataSet[self._image_name['dark']], **kwargs)
  288. @property
  289. def fringeRemovalCenter(self):
  290. """The getter of the center of region of insterest (ROI)
  291. :return: The center of region of insterest (ROI)
  292. :rtype: tuple
  293. """
  294. return self._fringeRemoval.center
  295. @fringeRemovalCenter.setter
  296. def fringeRemovalCenter(self, value):
  297. """The setter of the center of region of insterest (ROI)
  298. :param value: The center of region of insterest (ROI)
  299. :type value: tuple
  300. """
  301. self._fringeRemoval.center = value
  302. @property
  303. def fringeRemovalSpan(self):
  304. """The getter of the span of region of insterest (ROI)
  305. :return: The span of region of insterest (ROI)
  306. :rtype: tuple
  307. """
  308. return self._fringeRemoval.span
  309. @fringeRemovalSpan.setter
  310. def fringeRemovalSpan(self, value):
  311. """The setter of the span of region of insterest (ROI)
  312. :param value: The span of region of insterest (ROI)
  313. :type value: tuple
  314. """
  315. self._fringeRemoval.span = value
  316. def load_fringe_removal_background_from_hdf5(self, img_dir, SequenceName, data, shotNum, group, crop=False, load=False, **kwargs):
  317. folderPath = img_dir + SequenceName + "/" + data
  318. filePath = folderPath + "/" + shotNum + "/*.h5"
  319. dataSet = read_hdf5_file(filePath, group, **kwargs)
  320. scanAxis = dataSet.scanAxis
  321. dataSet = dataSet[self._image_name['background']] - dataSet[self._image_name['dark']]
  322. dataSet.attrs['scanAxis'] = scanAxis
  323. if crop:
  324. dataSet = self.crop_image(dataSet)
  325. if load:
  326. self.fringeRemovalReferenceImages = dataSet.load()
  327. else:
  328. self.fringeRemovalReferenceImages = dataSet
  329. def load_fringe_removal_background_from_database():
  330. pass