123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- #!/usr/bin/env python
- '''
- Wiener deconvolution.
- Sample shows how DFT can be used to perform Weiner deconvolution [1]
- of an image with user-defined point spread function (PSF)
- Usage:
- deconvolution.py [--circle]
- [--angle <degrees>]
- [--d <diameter>]
- [--snr <signal/noise ratio in db>]
- [<input image>]
- Use sliders to adjust PSF paramitiers.
- Keys:
- SPACE - switch btw linear/circular PSF
- ESC - exit
- Examples:
- deconvolution.py --angle 135 --d 22 licenseplate_motion.jpg
- (image source: http://www.topazlabs.com/infocus/_images/licenseplate_compare.jpg)
- deconvolution.py --angle 86 --d 31 text_motion.jpg
- deconvolution.py --circle --d 19 text_defocus.jpg
- (image source: compact digital photo camera, no artificial distortion)
- [1] http://en.wikipedia.org/wiki/Wiener_deconvolution
- '''
- # Python 2/3 compatibility
- from __future__ import print_function
- import numpy as np
- import cv2 as cv
- # local module
- from common import nothing
- def blur_edge(img, d=31):
- h, w = img.shape[:2]
- img_pad = cv.copyMakeBorder(img, d, d, d, d, cv.BORDER_WRAP)
- img_blur = cv.GaussianBlur(img_pad, (2*d+1, 2*d+1), -1)[d:-d,d:-d]
- y, x = np.indices((h, w))
- dist = np.dstack([x, w-x-1, y, h-y-1]).min(-1)
- w = np.minimum(np.float32(dist)/d, 1.0)
- return img*w + img_blur*(1-w)
- def motion_kernel(angle, d, sz=65):
- kern = np.ones((1, d), np.float32)
- c, s = np.cos(angle), np.sin(angle)
- A = np.float32([[c, -s, 0], [s, c, 0]])
- sz2 = sz // 2
- A[:,2] = (sz2, sz2) - np.dot(A[:,:2], ((d-1)*0.5, 0))
- kern = cv.warpAffine(kern, A, (sz, sz), flags=cv.INTER_CUBIC)
- return kern
- def defocus_kernel(d, sz=65):
- kern = np.zeros((sz, sz), np.uint8)
- cv.circle(kern, (sz, sz), d, 255, -1, cv.LINE_AA, shift=1)
- kern = np.float32(kern) / 255.0
- return kern
- def main():
- import sys, getopt
- opts, args = getopt.getopt(sys.argv[1:], '', ['circle', 'angle=', 'd=', 'snr='])
- opts = dict(opts)
- try:
- fn = args[0]
- except:
- fn = 'licenseplate_motion.jpg'
- win = 'deconvolution'
- img = cv.imread(cv.samples.findFile(fn), cv.IMREAD_GRAYSCALE)
- if img is None:
- print('Failed to load file:', fn)
- sys.exit(1)
- img = np.float32(img)/255.0
- cv.imshow('input', img)
- img = blur_edge(img)
- IMG = cv.dft(img, flags=cv.DFT_COMPLEX_OUTPUT)
- defocus = '--circle' in opts
- def update(_):
- ang = np.deg2rad( cv.getTrackbarPos('angle', win) )
- d = cv.getTrackbarPos('d', win)
- noise = 10**(-0.1*cv.getTrackbarPos('SNR (db)', win))
- if defocus:
- psf = defocus_kernel(d)
- else:
- psf = motion_kernel(ang, d)
- cv.imshow('psf', psf)
- psf /= psf.sum()
- psf_pad = np.zeros_like(img)
- kh, kw = psf.shape
- psf_pad[:kh, :kw] = psf
- PSF = cv.dft(psf_pad, flags=cv.DFT_COMPLEX_OUTPUT, nonzeroRows = kh)
- PSF2 = (PSF**2).sum(-1)
- iPSF = PSF / (PSF2 + noise)[...,np.newaxis]
- RES = cv.mulSpectrums(IMG, iPSF, 0)
- res = cv.idft(RES, flags=cv.DFT_SCALE | cv.DFT_REAL_OUTPUT )
- res = np.roll(res, -kh//2, 0)
- res = np.roll(res, -kw//2, 1)
- cv.imshow(win, res)
- cv.namedWindow(win)
- cv.namedWindow('psf', 0)
- cv.createTrackbar('angle', win, int(opts.get('--angle', 135)), 180, update)
- cv.createTrackbar('d', win, int(opts.get('--d', 22)), 50, update)
- cv.createTrackbar('SNR (db)', win, int(opts.get('--snr', 25)), 50, update)
- update(None)
- while True:
- ch = cv.waitKey()
- if ch == 27:
- break
- if ch == ord(' '):
- defocus = not defocus
- update(None)
- print('Done')
- if __name__ == '__main__':
- print(__doc__)
- main()
- cv.destroyAllWindows()
|