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.

286 lines
10 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. import numpy as np
  2. import xarray as xr
  3. import copy
  4. class ImageAnalyser():
  5. """A class for operate with and analyse images
  6. """
  7. def __init__(self) -> None:
  8. """Initialize the class
  9. """
  10. self._image_name = {
  11. 'atoms': 'atoms',
  12. 'background': 'background',
  13. 'dark': 'dark',
  14. 'OD':'OD',
  15. }
  16. self._center = None
  17. self._span = None
  18. self._fraction = None
  19. @property
  20. def image_name(self):
  21. """The getter of the names of three standard images for absorption images
  22. :return: The names of three standard images for absorption images
  23. :rtype: dict
  24. """
  25. return self._image_name
  26. @image_name.setter
  27. def image_name(self, value):
  28. """The setter of the names of three standard images for absorption images.
  29. It has to be a dict and have the follow format:
  30. {
  31. 'atoms': the name of the image with atoms,
  32. 'background': the name of the image without atoms,
  33. 'dark': the name of the image without light,
  34. 'OD': the name of the calculated OD,
  35. }
  36. :param value: The names of three standard images for absorption images.
  37. :type value: dict
  38. """
  39. self._image_name.update(value)
  40. @property
  41. def center(self):
  42. """The getter of the center of region of insterest (ROI)
  43. :return: The center of region of insterest (ROI)
  44. :rtype: tuple
  45. """
  46. return self._center
  47. @center.setter
  48. def center(self, value):
  49. """The setter of the center of region of insterest (ROI)
  50. :param value: The center of region of insterest (ROI)
  51. :type value: tuple
  52. """
  53. self._center = value
  54. @property
  55. def span(self):
  56. """The getter of the span of region of insterest (ROI)
  57. :return: The span of region of insterest (ROI)
  58. :rtype: tuple
  59. """
  60. return self._span
  61. @span.setter
  62. def span(self, value):
  63. """The setter of the span of region of insterest (ROI)
  64. :param value: The span of region of insterest (ROI)
  65. :type value: tuple
  66. """
  67. self._span = value
  68. @property
  69. def fraction(self):
  70. """The getter of the fraction used to remove the background in the region of insterest (ROI)
  71. :return: The fraction used to remove the background
  72. :rtype: tuple
  73. """
  74. return self._fraction
  75. @fraction.setter
  76. def fraction(self, value):
  77. """The setter of the fraction used to remove the background in the region of insterest (ROI)
  78. :param value: The fraction used to remove the background
  79. :type value: tuple
  80. """
  81. self._fraction = value
  82. def get_offset_from_corner(self, dataArray, x_fraction=None, y_fraction=None, fraction=None, xAxisName='x', yAxisName='y'):
  83. """Calculate the background value based on the four coners of the image.
  84. :param dataArray: The image
  85. :type dataArray: xarray DataArray
  86. :param x_fraction: The fraction of the pixels used x in axis, defaults to None
  87. :type x_fraction: float, optional
  88. :param y_fraction: The fraction of the pixels used y in axis, defaults to None
  89. :type y_fraction: float, optional
  90. :param fraction: The fraction of the pixels used to calculate the background, defaults to None
  91. :type fraction: tuple, optional
  92. :param xAxisName: The name of the x axis in dataArray, defaults to 'x'
  93. :type xAxisName: str, optional
  94. :param yAxisName: The name of the y axis in dataArray, defaults to 'y'
  95. :type yAxisName: str, optional
  96. :return: The value of the background
  97. :rtype: float
  98. """
  99. if fraction is None:
  100. if x_fraction is None:
  101. x_fraction = self._fraction[0]
  102. if y_fraction is None:
  103. y_fraction = self._fraction[1]
  104. else:
  105. x_fraction = fraction[0]
  106. y_fraction = fraction[1]
  107. x_number = dataArray[xAxisName].shape[0]
  108. y_number = dataArray[yAxisName].shape[0]
  109. mean = dataArray.isel(x=slice(0, int(x_number * x_fraction)), y=slice(0 , int(y_number * y_fraction))).mean(dim=[xAxisName, yAxisName])
  110. 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])
  111. 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])
  112. 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])
  113. return mean / 4
  114. def substract_offset(self, dataArray, **kwargs):
  115. """Remove the backgournd
  116. :param dataArray: The image
  117. :type dataArray: xarray DataArray
  118. :return: The image after removing background
  119. :rtype: xarray DataArray
  120. """
  121. res = dataArray - self.get_offset_from_corner(dataArray, **kwargs)
  122. res.attrs = copy.copy(dataArray.attrs)
  123. return res
  124. def crop_image(self, dataSet, center=None, span=None):
  125. """Crop the image according to the region of interest (ROI).
  126. :param dataSet: The images
  127. :type dataSet: xarray DataArray or DataSet
  128. :param center: The center of region of insterest (ROI), defaults to None
  129. :type center: tuple, optional
  130. :param span: the span of region of insterest (ROI), defaults to None
  131. :type span: tuple, optional
  132. :return: The croped images
  133. :rtype: xarray DataArray or DataSet
  134. """
  135. if center is None:
  136. center = self._center
  137. if span is None:
  138. span = self._span
  139. x_start = int(center[0] - span[0] / 2)
  140. x_end = int(center[0] + span[0] / 2)
  141. y_end = int(center[1] + span[1] / 2)
  142. y_start = int(center[1] - span[1] / 2)
  143. dataSet.attrs['x_start'] = x_start
  144. dataSet.attrs['x_end'] = x_end
  145. dataSet.attrs['y_end'] = y_end
  146. dataSet.attrs['y_start'] = y_start
  147. dataSet.attrs['x_center'] = center[0]
  148. dataSet.attrs['y_center'] = center[1]
  149. dataSet.attrs['x_span'] = span[0]
  150. dataSet.attrs['y_span'] = span[1]
  151. if isinstance(dataSet, type(xr.Dataset())):
  152. for key in list(dataSet.data_vars):
  153. dataSet[key].attrs['x_start'] = x_start
  154. dataSet[key].attrs['x_end'] = x_end
  155. dataSet[key].attrs['y_end'] = y_end
  156. dataSet[key].attrs['y_start'] = y_start
  157. dataSet[key].attrs['x_center'] = center[0]
  158. dataSet[key].attrs['y_center'] = center[1]
  159. dataSet[key].attrs['x_span'] = span[0]
  160. dataSet[key].attrs['y_span'] = span[1]
  161. return dataSet.isel(x=slice(x_start, x_end), y=slice(y_start, y_end))
  162. def get_OD(self, imageAtom, imageBackground, imageDrak):
  163. """Calculate the OD image for absorption imaging.
  164. :param imageAtom: The image with atoms
  165. :type imageAtom: numpy array
  166. :param imageBackground: The image without atoms
  167. :type imageBackground: numpy array
  168. :param imageDrak: The image without light
  169. :type imageDrak: numpy array
  170. :return: The OD images
  171. :rtype: numpy array
  172. """
  173. numerator = np.atleast_1d(imageBackground - imageDrak)
  174. denominator = np.atleast_1d(imageAtom - imageDrak)
  175. numerator[numerator == 0] = 1
  176. denominator[denominator == 0] = 1
  177. imageOD = np.abs(np.divide(denominator, numerator))
  178. imageOD= -np.log(imageOD)
  179. if len(imageOD) == 1:
  180. return imageOD[0]
  181. else:
  182. return imageOD
  183. def get_Ncount(self, dataSet, dim=['x', 'y'], **kwargs):
  184. """Sum all the value in the image to give the Ncount.
  185. :param dataSet: The images
  186. :type dataSet: xarray DataArray or DataSet
  187. :param dim: The dimensions to take the sumation, defaults to ['x', 'y']
  188. :type dim: list, optional
  189. :return: The Ncount
  190. :rtype: xarray DataArray or DataSet
  191. """
  192. return dataSet.sum(dim=['x', 'y'], **kwargs)
  193. def get_absorption_images(self, dataSet, dask='allowed', keep_attrs=True, **kwargs):
  194. """Calculate the OD images for absorption imaging.
  195. :param dataSet: The data from absorption imaging.
  196. :type dataSet: xarray DataSet
  197. :param dask: over write of the same argument in xarray.apply_ufunc, defaults to 'allowed'
  198. :type dask: str, optional
  199. :param keep_attrs: over write of the same argument in xarray.apply_ufunc, defaults to True
  200. :type keep_attrs: bool, optional
  201. :return: The data including the OD images.
  202. :rtype: xarray DataSet
  203. """
  204. kwargs.update(
  205. {
  206. 'dask': dask,
  207. 'keep_attrs': keep_attrs,
  208. }
  209. )
  210. dataSet = dataSet.assign(
  211. {
  212. 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)
  213. }
  214. )
  215. # dataSet[self._image_name['OD']].attrs.update(dataSet.attrs)
  216. return dataSet
  217. def remove_background(self, dataSet, dask='allowed', keep_attrs=True, **kwargs):
  218. """Remove the background for the given image data
  219. :param dataSet: The data from absorption imaging.
  220. :type dataSet: xarray DataSet
  221. :param dask: over write of the same argument in xarray.apply_ufunc, defaults to 'allowed'
  222. :type dask: str, optional
  223. :param keep_attrs: over write of the same argument in xarray.apply_ufunc, defaults to True
  224. :type keep_attrs: bool, optional
  225. """
  226. kwargs.update(
  227. {
  228. 'dask': dask,
  229. 'keep_attrs': keep_attrs,
  230. }
  231. )
  232. xr.apply_ufunc(self.get_OD, dataSet[self._image_name['atoms']], dataSet[self._image_name['background']], dataSet[self._image_name['dark']], **kwargs)