deconvolution.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #!/usr/bin/env python
  2. '''
  3. Wiener deconvolution.
  4. Sample shows how DFT can be used to perform Weiner deconvolution [1]
  5. of an image with user-defined point spread function (PSF)
  6. Usage:
  7. deconvolution.py [--circle]
  8. [--angle <degrees>]
  9. [--d <diameter>]
  10. [--snr <signal/noise ratio in db>]
  11. [<input image>]
  12. Use sliders to adjust PSF paramitiers.
  13. Keys:
  14. SPACE - switch btw linear/circular PSF
  15. ESC - exit
  16. Examples:
  17. deconvolution.py --angle 135 --d 22 licenseplate_motion.jpg
  18. (image source: http://www.topazlabs.com/infocus/_images/licenseplate_compare.jpg)
  19. deconvolution.py --angle 86 --d 31 text_motion.jpg
  20. deconvolution.py --circle --d 19 text_defocus.jpg
  21. (image source: compact digital photo camera, no artificial distortion)
  22. [1] http://en.wikipedia.org/wiki/Wiener_deconvolution
  23. '''
  24. # Python 2/3 compatibility
  25. from __future__ import print_function
  26. import numpy as np
  27. import cv2 as cv
  28. # local module
  29. from common import nothing
  30. def blur_edge(img, d=31):
  31. h, w = img.shape[:2]
  32. img_pad = cv.copyMakeBorder(img, d, d, d, d, cv.BORDER_WRAP)
  33. img_blur = cv.GaussianBlur(img_pad, (2*d+1, 2*d+1), -1)[d:-d,d:-d]
  34. y, x = np.indices((h, w))
  35. dist = np.dstack([x, w-x-1, y, h-y-1]).min(-1)
  36. w = np.minimum(np.float32(dist)/d, 1.0)
  37. return img*w + img_blur*(1-w)
  38. def motion_kernel(angle, d, sz=65):
  39. kern = np.ones((1, d), np.float32)
  40. c, s = np.cos(angle), np.sin(angle)
  41. A = np.float32([[c, -s, 0], [s, c, 0]])
  42. sz2 = sz // 2
  43. A[:,2] = (sz2, sz2) - np.dot(A[:,:2], ((d-1)*0.5, 0))
  44. kern = cv.warpAffine(kern, A, (sz, sz), flags=cv.INTER_CUBIC)
  45. return kern
  46. def defocus_kernel(d, sz=65):
  47. kern = np.zeros((sz, sz), np.uint8)
  48. cv.circle(kern, (sz, sz), d, 255, -1, cv.LINE_AA, shift=1)
  49. kern = np.float32(kern) / 255.0
  50. return kern
  51. def main():
  52. import sys, getopt
  53. opts, args = getopt.getopt(sys.argv[1:], '', ['circle', 'angle=', 'd=', 'snr='])
  54. opts = dict(opts)
  55. try:
  56. fn = args[0]
  57. except:
  58. fn = 'licenseplate_motion.jpg'
  59. win = 'deconvolution'
  60. img = cv.imread(cv.samples.findFile(fn), cv.IMREAD_GRAYSCALE)
  61. if img is None:
  62. print('Failed to load file:', fn)
  63. sys.exit(1)
  64. img = np.float32(img)/255.0
  65. cv.imshow('input', img)
  66. img = blur_edge(img)
  67. IMG = cv.dft(img, flags=cv.DFT_COMPLEX_OUTPUT)
  68. defocus = '--circle' in opts
  69. def update(_):
  70. ang = np.deg2rad( cv.getTrackbarPos('angle', win) )
  71. d = cv.getTrackbarPos('d', win)
  72. noise = 10**(-0.1*cv.getTrackbarPos('SNR (db)', win))
  73. if defocus:
  74. psf = defocus_kernel(d)
  75. else:
  76. psf = motion_kernel(ang, d)
  77. cv.imshow('psf', psf)
  78. psf /= psf.sum()
  79. psf_pad = np.zeros_like(img)
  80. kh, kw = psf.shape
  81. psf_pad[:kh, :kw] = psf
  82. PSF = cv.dft(psf_pad, flags=cv.DFT_COMPLEX_OUTPUT, nonzeroRows = kh)
  83. PSF2 = (PSF**2).sum(-1)
  84. iPSF = PSF / (PSF2 + noise)[...,np.newaxis]
  85. RES = cv.mulSpectrums(IMG, iPSF, 0)
  86. res = cv.idft(RES, flags=cv.DFT_SCALE | cv.DFT_REAL_OUTPUT )
  87. res = np.roll(res, -kh//2, 0)
  88. res = np.roll(res, -kw//2, 1)
  89. cv.imshow(win, res)
  90. cv.namedWindow(win)
  91. cv.namedWindow('psf', 0)
  92. cv.createTrackbar('angle', win, int(opts.get('--angle', 135)), 180, update)
  93. cv.createTrackbar('d', win, int(opts.get('--d', 22)), 50, update)
  94. cv.createTrackbar('SNR (db)', win, int(opts.get('--snr', 25)), 50, update)
  95. update(None)
  96. while True:
  97. ch = cv.waitKey()
  98. if ch == 27:
  99. break
  100. if ch == ord(' '):
  101. defocus = not defocus
  102. update(None)
  103. print('Done')
  104. if __name__ == '__main__':
  105. print(__doc__)
  106. main()
  107. cv.destroyAllWindows()