In [20]:
import numpy as np
import scipy as sp
import scipy.stats as stats
from skimage import img_as_float, io
# import matplotlib.pylab as plt
import sys, os
import psyutils as pu
from psyutils.image import show_im
import pandas as pd

# use ipython magic function to make sure psyutils is reloaded (for debugging):
%load_ext autoreload
%autoreload 2

# plots inline:
%pylab inline
# %config InlineBackend.figure_format = 'svg'


The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload
Populating the interactive namespace from numpy and matplotlib

Subpackages and functions


In [2]:
show_im(pu.misc.fixation_cross())


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.67
the SD of the image is 0.47
the rms contrast (SD / mean) is 0.7

In [33]:
# pix per deg:
pu.misc.pix_per_deg(60, (1920, 1200), (48.4, 30.2), average_wh=False)


Out[33]:
array([ 43.70417851,  42.47461678])

In [36]:
pu.misc.pix_per_deg(50, (1920, 1200), (48.4, 30.2))


Out[36]:
36.437770689218027

Filters

psyutils allows the creation of a range of filters, which are probability distributions defined on various axes. These can be multipled by images in the frequency domain to created filtered versions of the images (see the pu.image.make_filtered_noise and pu.image.filter_image functions).


In [4]:
im_size = 256
im = np.random.uniform(size=(im_size, im_size))
show_im(im)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.5
the SD of the image is 0.29
the rms contrast (SD / mean) is 0.58

Lowpass


In [5]:
filt = pu.image.make_filter_lowpass(im_size, cutoff=8)
show_im(filt)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.0
the SD of the image is 0.06
the rms contrast (SD / mean) is 17.77

In [6]:
blah = np.fft.fftshift(filt)
show_im(blah)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.0
the SD of the image is 0.06
the rms contrast (SD / mean) is 17.77

In [7]:
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.05 to max 0.05
the mean of the image is -0.0
the SD of the image is 0.01
the rms contrast (SD / mean) is -1.21367279442e+16

Highpass


In [8]:
filt = pu.image.make_filter_highpass(im_size, cutoff=64)
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.74 to max 0.81
the mean of the image is -0.0
the SD of the image is 0.26
the rms contrast (SD / mean) is -9.22607275073e+17

Log exponential


In [9]:
filt = pu.image.make_filter_log_exp(im_size, peak=8, width=0.5)
show_im(filt)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.0
the SD of the image is 0.06
the rms contrast (SD / mean) is 12.6

In [10]:
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.05 to max 0.06
the mean of the image is -0.0
the SD of the image is 0.02
the rms contrast (SD / mean) is -1.3462079724e+17

Gaussian


In [11]:
filt = pu.image.make_filter_gaussian(im_size, peak=8, width=2)
show_im(filt)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.0
the SD of the image is 0.05
the rms contrast (SD / mean) is 13.58

In [12]:
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.04 to max 0.05
the mean of the image is 0.0
the SD of the image is 0.01
the rms contrast (SD / mean) is 2.21101919143e+16

Log Gaussian


In [13]:
filt = pu.image.make_filter_log_gauss(im_size, peak=8, width=.5)
show_im(filt)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.01
the SD of the image is 0.08
the rms contrast (SD / mean) is 6.71

In [14]:
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.08 to max 0.07
the mean of the image is -0.0
the SD of the image is 0.02
the rms contrast (SD / mean) is -1.65101781129e+16

Log cosine


In [15]:
filt = pu.image.make_filter_log_cosine(im_size, peak=8)
show_im(filt)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.0
the SD of the image is 0.06
the rms contrast (SD / mean) is 12.19

In [16]:
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.05 to max 0.06
the mean of the image is 0.0
the SD of the image is 0.02
the rms contrast (SD / mean) is 1.71361578521e+16

$\alpha /f$ filters

where $\alpha$ is the negative log-log slope of power with frequency. $\alpha = 1$ gives a $1/f$ filter.


In [17]:
filt = pu.image.make_filter_alpha_over_f(im_size, 1)
show_im(filt)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.41
the mean of the image is 0.01
the SD of the image is 0.02
the rms contrast (SD / mean) is 1.39

In [18]:
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.02 to max 0.03
the mean of the image is 0.0
the SD of the image is 0.01
the rms contrast (SD / mean) is 3.29709948236e+15

Gaussian filter on orientation


In [19]:
filt = pu.image.make_filter_orientation_gaussian(im_size, peak=135, width=10)
show_im(filt)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.18
the SD of the image is 0.32
the rms contrast (SD / mean) is 1.8

In [20]:
im2 = pu.image.filter_image(im, filt)
show_im(im2)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from -0.37 to max 0.37
the mean of the image is -0.0
the SD of the image is 0.1
the rms contrast (SD / mean) is -1.80570925974e+18

Sloan letters


In [21]:
sloans = pu.im_data.sloan_letters()
show_im(sloans["H"])


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.48
the SD of the image is 0.5
the rms contrast (SD / mean) is 1.04

Placing rectangles in others; cutting out patches


In [36]:
rect_a = sloans["H"]
rect_b = np.ones((512, 512))
new_rect = pu.image.put_rect_in_rect(rect_a, rect_b, midpoints=200)
show_im(new_rect)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (512, 512)
image has range from 0.0 to max 1.0
the mean of the image is 0.87
the SD of the image is 0.34
the rms contrast (SD / mean) is 0.39

In [59]:
rect_a = np.zeros((2, 2))
rect_b = np.ones((4, 4))
new = pu.image.put_rect_in_rect(rect_a, rect_b, midpoints=(3, 2))
show_im(new)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (4, 4)
image has range from 0.0 to max 1.0
the mean of the image is 0.75
the SD of the image is 0.43
the rms contrast (SD / mean) is 0.58

In [73]:
im = np.array([[1., 1., 1., 1.],
               [1., 1., 0., 0.],
               [1., 1., 0., 0.],
               [1., 1., 1., 1.]])
res = pu.image.cutout_patch(im, size=(2, 2), midpoints=(3, 2))
res


Out[73]:
array([[ 0.,  0.],
       [ 0.,  0.]])

Windowing functions

Psyutils contains a few windowing functions for drawing spatio-temporal windows around stimuli. In general, these functions run from one to zero, so you should multiply a zero-mean stimulus by the window image to mask it.


In [2]:
# 1D cosine window:
win = pu.image.cos_win_1d(200, ramp=45)
plot(win), plt.box('off')


Out[2]:
([<matplotlib.lines.Line2D at 0x1097fe0f0>], None)

In [3]:
# 2D cosine window:
win = pu.image.cos_win_2d(128, ramp=0.3)
show_im(win)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (128, 128)
image has range from 0.0 to max 1.0
the mean of the image is 0.62
the SD of the image is 0.42
the rms contrast (SD / mean) is 0.69

In [4]:
# Specify using pixels (note the initial zero is included):
win = pu.image.cos_win_2d(16, ramp=4, ramp_type='pixels')
show_im(win)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (16, 16)
image has range from 0.0 to max 1.0
the mean of the image is 0.47
the SD of the image is 0.42
the rms contrast (SD / mean) is 0.88

In [5]:
win[7, :]


Out[5]:
array([ 0.        ,  0.39937548,  0.73675438,  0.94740807,  1.        ,
        1.        ,  1.        ,  1.        ,  1.        ,  1.        ,
        1.        ,  1.        ,  0.94740807,  0.73675438,  0.39937548,  0.        ])

Saving images

I've written a wrapper function to save images as either 8- or 16-bit. Depending on the dimensions of the input, these will be saved as either greyscale images (MxN), RGB images (MxNx3) or RGBA images (MxNx4). Note that as of scikit-image version 0.10.1 there is a bug in saving to 16 bit, where the dimensions are all messed up. Should be fixed in a future version (see issue 1101 on their github).

The input array will be converted to float using skimage's img_to_float function.

Test 1: input greyscale float


In [7]:
im = np.random.uniform(size=(256, 256))
im[:128, :128] = 1

show_im(im)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256)
image has range from 0.0 to max 1.0
the mean of the image is 0.63
the SD of the image is 0.33
the rms contrast (SD / mean) is 0.53

In [8]:
# save as uint8, check reload:
fname = 'test_1_8_bit.png'
pu.image.save_im(fname, im, bitdepth=8)
test = io.imread(fname)
show_im(test)


image is of type <class 'numpy.ndarray'>
image has data type uint8
image has dimensions (256, 256)
image has range from 0 to max 255
the mean of the image is 159.4
the SD of the image is 84.4
the rms contrast (SD / mean) is 0.53
/Users/tomwallis/miniconda3/envs/py34/lib/python3.4/site-packages/skimage/util/dtype.py:107: UserWarning: Possible precision loss when converting from float64 to uint8
  "%s to %s" % (dtypeobj_in, dtypeobj))

In [9]:
# save as uint16, check reload:
fname = 'test_1_16_bit.png'
pu.image.save_im(fname, im, bitdepth=16)
test = io.imread(fname)
show_im(test)


image is of type <class 'numpy.ndarray'>
image has data type uint16
image has dimensions (256, 256)
image has range from 0 to max 65535
the mean of the image is 40966.48
the SD of the image is 21691.66
the rms contrast (SD / mean) is 0.53
/Users/tomwallis/miniconda3/envs/py34/lib/python3.4/site-packages/skimage/util/dtype.py:107: UserWarning: Possible precision loss when converting from float64 to uint16
  "%s to %s" % (dtypeobj_in, dtypeobj))

Test 2: input RGBA


In [10]:
im = np.random.uniform(size=(256, 256, 4))
im[..., 3] = pu.image.cos_win_2d(256, ramp=32, ramp_type='pixels')  # fill cosine window into alpha level.
im[:128, :128, :3] = 1
show_im(im)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256, 4)
image has range from 0.0 to max 1.0
the mean of the image is 0.63
the SD of the image is 0.36
the rms contrast (SD / mean) is 0.57

In [11]:
# save as uint8, check reload:
fname = 'test_2_8_bit.png'
pu.image.save_im(fname, im, bitdepth=8)
test = io.imread(fname)
show_im(test)


image is of type <class 'numpy.ndarray'>
image has data type uint8
image has dimensions (256, 256, 4)
image has range from 0 to max 255
the mean of the image is 160.68
the SD of the image is 91.01
the rms contrast (SD / mean) is 0.57

In [12]:
# save as uint16, check reload:
fname = 'test_2_16_bit.png'
pu.image.save_im(fname, im, bitdepth=16)
test = io.imread(fname)
show_im(test)


image is of type <class 'numpy.ndarray'>
image has data type uint16
image has dimensions (256, 256, 4)
image has range from 0 to max 65535
the mean of the image is 41294.96
the SD of the image is 23388.95
the rms contrast (SD / mean) is 0.57

Test 3: input RGB


In [13]:
im = np.random.uniform(size=(256, 256, 3))
im[:128, :128] = 1
show_im(im)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (256, 256, 3)
image has range from 0.0 to max 1.0
the mean of the image is 0.62
the SD of the image is 0.33
the rms contrast (SD / mean) is 0.53

In [14]:
# save as uint8, check reload:
fname = 'test_3_8_bit.png'
pu.image.save_im(fname, im, bitdepth=8)
test = io.imread(fname)
show_im(test)


image is of type <class 'numpy.ndarray'>
image has data type uint8
image has dimensions (256, 256, 3)
image has range from 0 to max 255
the mean of the image is 159.34
the SD of the image is 84.34
the rms contrast (SD / mean) is 0.53

In [15]:
# save as uint16, check reload:
fname = 'test_3_16_bit.png'
pu.image.save_im(fname, im, bitdepth=16)
test = io.imread(fname)
show_im(test)


image is of type <class 'numpy.ndarray'>
image has data type uint16
image has dimensions (256, 256, 3)
image has range from 0 to max 65535
the mean of the image is 40950.76
the SD of the image is 21674.22
the rms contrast (SD / mean) is 0.53

psyutils internals

The backend of the image submodule of psyutils generates filters by applying some distributions to some axes. There are basically four axes needed for the filtering operations, which are returned as 2D numpy arrays: cartesian x and y, radial distance r (i.e. distance from the axis centre) and polar angle a. All angles are specified in radians.

psyutils provides functions to generate such axes, as well as a couple of others (e.g. log-spaced axes).

Axes

Psyutils' image filtering functions are build on the abstraction of axes. There are four types: x, y, radial and angular.


In [15]:
x, y = pu.image.axes_cart(size=(12, 12), axes_limits=(-3, 10))
show_im(x)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (12, 12)
image has range from -3.0 to max 10.0
the mean of the image is 3.5
the SD of the image is 4.08
the rms contrast (SD / mean) is 1.17

In [16]:
r, a = pu.image.axes_polar(size=101)
show_im(r)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (101, 101)
image has range from 0.0 to max 1.41
the mean of the image is 0.77
the SD of the image is 0.29
the rms contrast (SD / mean) is 0.37

In [17]:
show_im(a)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (101, 101)
image has range from -3.12 to max 3.14
the mean of the image is 0.02
the SD of the image is 1.8
the rms contrast (SD / mean) is 117.2

In [18]:
x, y = pu.image.axes_semilogx_cart(size=10, axes_limits=(-2, 2))
show_im(x)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (10, 10)
image has range from -2.0 to max 2.0
the mean of the image is 0.93
the SD of the image is 1.15
the rms contrast (SD / mean) is 1.23

In [19]:
show_im(y)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (10, 10)
image has range from -2.0 to max 2.0
the mean of the image is 0.0
the SD of the image is 1.28
the rms contrast (SD / mean) is inf
/Users/tomwallis/Dropbox/Python/my_packages/PsyUtils/psyutils/image/_image_utilities.py:200: RuntimeWarning: divide by zero encountered in double_scalars
  str(round(im.std()/im.mean(), ndigits=2)))

In [21]:
r, a = pu.image.axes_logradial_polar(size=100)
show_im(r)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (100, 100)
image has range from -4.25 to max 0.35
the mean of the image is -0.36
the SD of the image is 0.51
the rms contrast (SD / mean) is -1.43

In [22]:
a = pu.image.axes_angular_distance(size=100)
show_im(a)


image is of type <class 'numpy.ndarray'>
image has data type float64
image has dimensions (100, 100)
image has range from 0.01 to max 3.13
the mean of the image is 1.57
the SD of the image is 0.89
the rms contrast (SD / mean) is 0.57