26
26
* numpy
27
27
* scipy
28
28
* Python Image Library (PIL) or Pillow
29
- * matplotlib
30
- * imageio
29
+ * matplotlib (plotAverageAmpSpec only)
30
+ * imageio (plotAverageAmpSpec only)
31
31
32
32
"""
33
33
38
38
from numpy import pi
39
39
from numpy .fft import fft2 , ifft2 , fftshift , ifftshift
40
40
import scipy .ndimage
41
- import matplotlib .pyplot as plt
42
41
try :
43
- import Image # will work on most installations
42
+ import Image # via PIL
44
43
except ImportError :
45
- from PIL import Image # if installed via pillow
44
+ from PIL import Image # via pillow
46
45
47
46
48
47
##### UTILITY FUNCTION DEFINITIONS #####
@@ -86,14 +85,10 @@ def imread(image, output_dtype=np.float64, pad_depth_channel=True,
86
85
# Handle alpha channel
87
86
if im .ndim == 3 and (im .shape [2 ] in [2 ,4 ]) and alpha_action :
88
87
if alpha_action == 'remove' :
89
- with warnings .catch_warnings ():
90
- warnings .simplefilter ('always' )
91
- warnings .warn ('Removing alpha channel' )
88
+ warnings .warn ('Removing alpha channel' )
92
89
im = im [..., :- 1 ]
93
90
elif alpha_action == 'mask' :
94
- with warnings .catch_warnings ():
95
- warnings .simplefilter ('always' )
96
- warnings .warn ('Masking by alpha channel' )
91
+ warnings .warn ('Masking by alpha channel' )
97
92
orig_dtype = im .dtype
98
93
im = im .astype (np .float64 )
99
94
im , mask = np .split (im , [- 1 ], axis = - 1 )
@@ -267,11 +262,13 @@ def applyPhaseScram(image, coherence=0.0, rndphi=None, mask=None, nSegs=1,
267
262
apply, with 0 being fully scrambled (default) and 1 being not scrambled
268
263
at all.
269
264
rndphi : array, optional
270
- Array of random phases.
265
+ 2D array of random phases - allows using a custom phase spectrum.
266
+ Should be same size as image segments (or whole image if nSegs == 1).
271
267
mask : array, optional
272
268
Mask of weights in range 0-1 that can be applied to random phase array
273
269
(e.g. to scramble only certain parts of the spectrum). Mask should be
274
- for an unshifted spectrum.
270
+ for an unshifted spectrum, and same size as image segments (or whole
271
+ image if nSegs == 1),
275
272
nSegs : int, optional
276
273
Number of segments to split image into. If nSegs is 1 (default),
277
274
scrambling is performed across entire image (i.e. global scrambling).
@@ -293,34 +290,34 @@ def applyPhaseScram(image, coherence=0.0, rndphi=None, mask=None, nSegs=1,
293
290
--------
294
291
Phase scramble image with 0% coherence
295
292
296
- >>> scram1 = applyPhaseScram('/some/image.png')
293
+ >>> im = imageio.imread('imageio:camera.png')
294
+ >>> scram1 = applyPhaseScram(im)
297
295
298
296
Scramble with 40% phase coherence
299
297
300
- >>> scram2 = applyPhaseScram('/some/image.png' , coherence = .4)
298
+ >>> scram2 = applyPhaseScram(im , coherence = .4)
301
299
302
300
Use own random phase array
303
301
304
302
>>> import numpy as np
305
- >>> myrndphi = np.angle(np.fft.fft2(np.random.rand(im_height, im_width )))
306
- >>> scram3 = applyPhaseScram('/some/image.png' , rndphi = myrndphi)
303
+ >>> myrndphi = np.angle(np.fft.fft2(np.random.rand(*im.shape )))
304
+ >>> scram3 = applyPhaseScram(im , rndphi = myrndphi)
307
305
308
306
Weight rndphi by mask. Here we weight by an inverted horizontal-pass
309
307
filter to scramble vertical orientations but preserve horizontals.
310
308
311
309
>>> from imageprocessing import FourierFilter
312
- >>> impath = '/some/image.png'
313
- >>> filterer = FourierFilter(impath)
310
+ >>> filterer = FourierFilter(im)
314
311
>>> filt = filterer.makeFilter(
315
312
... mode='ori', filtertype='gaussian', invert=True,
316
313
... filter_kwargs = {'mu':np.radians(0),
317
314
... 'sigma':fwhm2sigma(np.radians(45))}
318
315
... )
319
- >>> scram4 = applyPhaseScram(impath , mask = filt)
316
+ >>> scram4 = applyPhaseScram(im , mask = filt)
320
317
321
318
Locally scrambled image within windows of an 8x8 grid
322
319
323
- >>> local_scram = applyPhaseScram('/some/image.png' , nSegs = 8)
320
+ >>> local_scram = applyPhaseScram(im , nSegs = 8)
324
321
325
322
"""
326
323
# Read in image
@@ -329,8 +326,9 @@ def applyPhaseScram(image, coherence=0.0, rndphi=None, mask=None, nSegs=1,
329
326
330
327
# Work out segments
331
328
if L % nSegs or W % nSegs :
332
- raise ValueError ('Image dimensions ({0}, {1}) must be divisible by '
333
- 'nSegs ({2})' .format (L , W , nSegs ))
329
+ raise ValueError (
330
+ f'Image dimensions ({ L } ,{ W } ) must be divisible by nSegs ({ nSegs } )'
331
+ )
334
332
segL = L // nSegs
335
333
segW = W // nSegs
336
334
@@ -366,11 +364,11 @@ def applyPhaseScram(image, coherence=0.0, rndphi=None, mask=None, nSegs=1,
366
364
ampF = np .abs (F )
367
365
phiF = np .angle (F )
368
366
# Calculate new phase spectrum
369
- newphi = phiF + rndphi
367
+ phiF += rndphi
370
368
# Combine original amplitude spectrum with new phase spectrum
371
- newF = ampF * np .exp (newphi * 1j )
369
+ F [:] = ampF * np .exp (phiF * 1j )
372
370
# Inverse transform, assign into scram
373
- scram [y1 :y2 , x1 :x2 , i ] = ifft2 (newF ).real
371
+ scram [y1 :y2 , x1 :x2 , i ] = ifft2 (F ).real
374
372
375
373
# Postproc and return
376
374
return postproc_im (scram , ** kwargs )
@@ -642,16 +640,17 @@ def plotAverageAmpSpec(indir, ext='png', nSegs=1, dpi=96, cmap='jet'):
642
640
dpi : int, optional
643
641
Resolution to save plots at (default = 96).
644
642
cmap : any valid matplotlib cmap instance
645
- Colourmap for filled contour plot
643
+ Colourmap for filled contour plot.
646
644
"""
647
645
# Local imports just for this function
648
646
import glob , imageio
647
+ import matplotlib .pyplot as plt
649
648
650
649
# Ensure . character not included in extension
651
650
ext = ext .strip ('.' )
652
651
653
652
# Glob for input files
654
- infiles = sorted (glob .glob (os .path .join (indir , '*.%s' % ext )))
653
+ infiles = sorted (glob .glob (os .path .join (indir , f '*.{ ext } ' )))
655
654
if len (infiles ) == 0 :
656
655
raise IOError ('No images found! Check directory and extension' )
657
656
@@ -662,8 +661,9 @@ def plotAverageAmpSpec(indir, ext='png', nSegs=1, dpi=96, cmap='jet'):
662
661
663
662
# Work out if we can segment image evenly, and dims of windows if we can
664
663
if L % nSegs or W % nSegs :
665
- raise IOError ('Image dimensions ({0}, {1}) must be divisible by '
666
- 'nSegs ({2})' .format (L , W , nSegs ))
664
+ raise ValueError (
665
+ f'Image dimensions ({ L } ,{ W } ) must be divisible by nSegs ({ nSegs } )'
666
+ )
667
667
segL = L // nSegs
668
668
segW = W // nSegs
669
669
@@ -688,7 +688,7 @@ def plotAverageAmpSpec(indir, ext='png', nSegs=1, dpi=96, cmap='jet'):
688
688
ampF = np .abs (fftshift (fft2 (win )))
689
689
# Log scale, assign relevant window of spectrum (we use ampF+1
690
690
# to avoid any -ve values from log scaling values < 1)
691
- spectra [i , y :y + segL , x :x + segW ] = np .log (ampF + 1 )
691
+ spectra [i , y :y + segL , x :x + segW ] = np .log1p (ampF )
692
692
spectra [i ] /= spectra [i ].max () # scale full array to range 0:1
693
693
694
694
# Create average spectrum
@@ -700,7 +700,7 @@ def plotAverageAmpSpec(indir, ext='png', nSegs=1, dpi=96, cmap='jet'):
700
700
os .makedirs (outdir , exist_ok = True )
701
701
702
702
# Main numpy array
703
- savename = os .path .join (outdir , 'win{}_array.npy' . format ( nSegs ) )
703
+ savename = os .path .join (outdir , f 'win{ nSegs } _array.npy' )
704
704
np .save (savename , av_spectrum )
705
705
706
706
# Filled contour figure
@@ -713,9 +713,9 @@ def plotAverageAmpSpec(indir, ext='png', nSegs=1, dpi=96, cmap='jet'):
713
713
cf = ax .contourf (av_spectrum , origin = 'upper' )
714
714
cf .set_cmap (cmap )
715
715
cf .set_clim ([0 ,1 ])
716
- savename = os .path .join (outdir , 'win%s_filled_contour .png' % ( nSegs ) )
716
+ savename = os .path .join (outdir , f 'win{ nSegs } _filled_contour .png' )
717
717
fig .savefig (savename , dpi = dpi )
718
- print ('Saved %s' % savename )
718
+ print ('Saved ' + savename )
719
719
plt .close (fig )
720
720
721
721
# Line contour figure
@@ -726,9 +726,9 @@ def plotAverageAmpSpec(indir, ext='png', nSegs=1, dpi=96, cmap='jet'):
726
726
# spectrum in range 0:1
727
727
ax .contour (av_spectrum , [0.45 , 0.55 ], colors = 'k' , linewidths = 2 ,
728
728
origin = 'upper' )
729
- savename = os .path .join (outdir , 'win%s_line_contour .png' % ( nSegs ) )
729
+ savename = os .path .join (outdir , f 'win{ nSegs } _line_contour .png' )
730
730
fig .savefig (savename , dpi = dpi )
731
- print ('Saved %s' % savename )
731
+ print ('Saved ' + savename )
732
732
plt .close (fig )
733
733
734
734
@@ -758,19 +758,21 @@ class SoftWindowImage():
758
758
--------
759
759
Apply a rectangular soft window
760
760
761
+ >>> im = imageio.imread('imageio:camera.png')
761
762
>>> windower = SoftWindowImage('rect')
762
- >>> winIm = windower.maskImage('./some/image.png' )
763
+ >>> winIm = windower.maskImage(im )
763
764
764
765
The mask is created when the first image is processed, and stored within
765
766
the class. The mask can be re-used for subsequent images of the same size
766
767
767
- >>> winIm2 = windower.maskImage('./some/other_image.png')
768
+ >>> im2 = imageio.imread('imageio:wikkie.png')
769
+ >>> winIm2 = windower.maskImage(im2)
768
770
769
771
Create an elliptical mask with a fwhm of 0.8, and apply to image setting
770
772
background to be white
771
773
772
774
>>> windower = SoftWindowImage('ellipse', mask_fwhm=0.8)
773
- >>> winIm3 = windower.maskImage('./some/image.png' , bglum=255)
775
+ >>> winIm3 = windower.maskImage(im , bglum=255)
774
776
"""
775
777
def __init__ (self , mask_shape , mask_fwhm = 0.9 ):
776
778
if mask_shape not in ['ellipse' , 'rect' ]:
@@ -779,7 +781,6 @@ def __init__(self, mask_shape, mask_fwhm=0.9):
779
781
self .mask_fwhm = mask_fwhm
780
782
self .mask = None # placeholder for mask
781
783
782
-
783
784
def _createMask (self , imsize ):
784
785
"""
785
786
Create soft-window mask, assigns into class. Should get called
@@ -831,7 +832,6 @@ def _createMask(self, imsize):
831
832
# Assign to class
832
833
self .mask = mask
833
834
834
-
835
835
def maskImage (self , image , bglum = 'mean' , ** kwargs ):
836
836
"""
837
837
Create and apply mask to image.
0 commit comments