phase.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. """Functions for randomising phase in array representations of images"""
  2. from PIL import Image, ImageEnhance
  3. import numpy as np
  4. import warnings
  5. def randomise(img, noise='uniform', noise_prop=1, contrast_adj=1):
  6. """Randomise the phase of a PIL image. Returns the altered PIL image.
  7. Parameters
  8. ----------
  9. img : PIL.Image
  10. The image to randomise the phase of. If a `np.array` is passed, will be converted to PIL image.
  11. noise : str
  12. The type of distribution to draw the noise from. Can be one of:
  13. 'uniform': uniform distribution, between -pi and pi
  14. 'permute': randomly shuffle the image's existing phase
  15. 'normal': normal distribution, with mean of 0, and sd of 1
  16. noise_prop : float
  17. A float from 0 to 1 specifying how much of the image should be noise (e.g. 0.3 will produce an image with phase of 30% noise, 70% original - i.e., 70% coherence).
  18. contrast_adj : float
  19. A float specifying a proportion of contrast adjustment. The contrast of `img` will be adjusted to this value before the fft is run, and the phase-altered output will then be reverted to the original image's contrast. Contrast artefacts can often be removed from phase-altered images by reducing this value from 1.
  20. """
  21. if type(img) is np.ndarray:
  22. if img.max() <= 1:
  23. warnings.warn('Expected PIL.Image but got np.array - will try to convert assuming max value is 1.')
  24. img = Image.fromarray(np.uint8(img * 255))
  25. else:
  26. warnings.warn('Expected PIL.Image but got np.array - will try to convert assuming max value is 255.')
  27. img = Image.fromarray(np.uint8(img))
  28. # adjust contrast
  29. if contrast_adj!=1:
  30. img_ie = ImageEnhance.Contrast(img)
  31. img = img_ie.enhance(contrast_adj)
  32. # fast fourier transform of the image as-is
  33. img_fft = np.fft.fftn(img)
  34. # get amplitude as distance from origin in complex plane
  35. amp = np.abs(img_fft)
  36. # get original image's phase
  37. ph = np.angle(img_fft)
  38. # get randomised phase values
  39. if noise == 'uniform':
  40. ph_noise = np.random.uniform(-np.pi, np.pi, img_fft.shape)
  41. elif noise == 'permute':
  42. ph_noise = np.random.permutation(ph)
  43. elif noise == 'normal':
  44. ph_noise = np.random.normal(0, 1, img_fft.shape)
  45. # get phase values with the desired proportion of noise
  46. ph_new = ph * (1-noise_prop) + ph_noise * noise_prop
  47. # inverse fourier transform using the new phases
  48. # (rounded to nearest integer to account for rounding errors)
  49. img_ph_rand = np.round(np.abs( np.fft.ifftn(amp * np.exp(1j * ph_new)) ))
  50. # convert to PIL image
  51. img_out = Image.fromarray(np.uint8(img_ph_rand))
  52. # revert contrast to original value
  53. if contrast_adj!=1:
  54. img_out_ie = ImageEnhance.Contrast(img_out)
  55. img_out = img_out_ie.enhance(1/contrast_adj)
  56. return(img_out)