peri/viz/interaction.py
class OrthoPrefeature(OrthoViewer):
    def __init__(self, image, pos, viewrad=None, cmap='Greys_r', part_col=
                [0., 1., 1., 1.], **kwargs):
        """
        Interactive viewing of a 3D featured image, to examine the quality of
        initial featuring.

        There are 3 modes of the viewer, accessed by pressing these keys:
         * 'V' : switch to view mode (click window to move view)
         * 'A' : add particle mode (click position to add)
         * 'R' : remove particle mode (click position to remove)
        The viewer starts in view mode.
        The complete list of particle positions is stored in ``self.pos``

        Parameters
        ----------
        im : numpy.ndarray
            The image to check pre-featuring on.
        pos : [N,3] numpy.ndarray
            The initial guess for the particle positions, in pixel units
        viewrad : Float or None, optional.
            The width of the particles to plot. Default is 2.
        cmap : String, optional
            A valid matplotlib colormap. Default is `'Greys_r'`.s
        part_col : list-like, optional
            Color of the particles.... RGBA. Default is cyan.
        **kwargs : All keyword args to OrthoViewer....
        """
        #Possible to modify this to include varying radii
        self.image = image.copy()
        self.pos = pos
        if viewrad is None:
            self.viewrad = 2.
        else:
            self.viewrad = viewrad
        self.mode = 'view'
        super(OrthoPrefeature, self).__init__(image, cmap=cmap, **kwargs)
        if self.vmin is None:
            self.vmin = image.min()
        if self.vmax is None:
            self.vmax = image.max()

        if type(self.cmap) == str:
            self._cmap = mpl.cm.get_cmap(self.cmap)
        else:  #better be a cmap function
            self._cmap = self.cmap
        self.particle_field = np.zeros(image.shape, dtype='float')
        self.part_col = part_col

        self._draw_im()
        self.update_particle_field(poses=None)
        self.update_field()
        self.draw()

    def _draw_im(self):
        rscl = np.clip((self.image - self.vmin) / (self.vmax - self.vmin), 0,1)
        self._image = self._cmap(rscl)

    def _particle_func(self, coords, pos, wid):
        """Draws a gaussian, range is (0,1]. Coords = [3,n]"""
        dx, dy, dz = [c - p for c,p in zip(coords, pos)]
        dr2 = dx*dx + dy*dy + dz*dz
        return np.exp(-dr2/(2*wid*wid))

    def update_particle_field(self, poses=None, add=True):
        if poses is None:
            poses = self.pos
        wid = self.viewrad
        for p in poses:
            #1. get tile
            l = np.clip(p-2*wid, 0, self.image.shape)
            r = np.clip(p+2*wid, 0, self.image.shape)
            t = Tile(l, r, mins=0, maxs=self.image.shape)
            #2. update:
            c = t.coords(form='broadcast')
            if add:
                self.particle_field[t.slicer] += self._particle_func(c, p, wid)
            else:
                self.particle_field[t.slicer] -= self._particle_func(c, p, wid)

    def update_field(self, poses=None):
        """updates self.field"""
        m = np.clip(self.particle_field, 0, 1)
        part_color = np.zeros(self._image.shape)
        for a in range(4): part_color[:,:,:,a] = self.part_col[a]
        self.field = np.zeros(self._image.shape)
        for a in range(4):
            self.field[:,:,:,a] = m*part_color[:,:,:,a] + (1-m) * self._image[:,:,:,a]

    def draw_ortho(self, im, g, cmap=None, vmin=0, vmax=1):
        im, vmin, vmax, cmap = self.field, self.vmin, self.vmax, self.cmap

        if self.fourier:
            im = np.abs(im)

        slices = self.slices
        int_slice = np.clip(np.round(slices), 0,
                            np.array(im.shape[:3])-1).astype('int')

        # if vmin is None:
            # vmin = im.min()
        # if vmax is None:
            # vmax = im.max()

        # if self.cmap == 'RdBu_r':
            # val = np.max(np.abs([vmin, vmax]))
            # vmin = -val
            # vmax = val

        self.g['xy'].cla()
        self.g['yz'].cla()
        self.g['xz'].cla()
        self.g['in'].cla()

        self.g['xy'].imshow(im[int_slice[0],:,:], cmap=cmap)
        self.g['xy'].hlines(slices[1], 0, im.shape[2], colors='y', linestyles='dashed', lw=1)
        self.g['xy'].vlines(slices[2], 0, im.shape[1], colors='y', linestyles='dashed', lw=1)
        self._format_ax(self.g['xy'])

        self.g['yz'].imshow(im[:,int_slice[1],:], vmin=vmin, vmax=vmax, cmap=cmap)
        self.g['yz'].hlines(slices[0], 0, im.shape[2], colors='y', linestyles='dashed', lw=1)
        self.g['yz'].vlines(slices[2], 0, im.shape[0], colors='y', linestyles='dashed', lw=1)
        self._format_ax(self.g['yz'])

        self.g['xz'].imshow(np.rollaxis(im[:,:,int_slice[2]], 1), cmap=cmap)
        self.g['xz'].hlines(slices[1], 0, im.shape[0], colors='y', linestyles='dashed', lw=1)
        self.g['xz'].vlines(slices[0], 0, im.shape[1], colors='y', linestyles='dashed', lw=1)
        self._format_ax(self.g['xz'])

        if self.dohist:
            tt = np.real(self.field).ravel()
            c, s = tt.mean(), 5*tt.std()
            y,x = np.histogram(tt, bins=np.linspace(c-s, c+s, 700), normed=True)
            x = (x[1:] + x[:-1])/2

            self.g['in'].plot(x, y, 'k-', lw=1)
            self.g['in'].fill_between(x, y, 1e-10, alpha=0.5)
            self.g['in'].set_yscale('log', nonposy='clip')

            self.g['in'].set_xlim(c-s, c+s)
            self.g['in'].set_ylim(1e-3*y.max(), 1.4*y.max())

        self._format_ax(self.g['in'])

        pl.draw()

    def register_events(self):
        self._calls = []
        self._calls.append(self.fig.canvas.mpl_connect('key_press_event',
                self.key_press_event))

        if self.mode == 'view':
            self._calls.append(self.fig.canvas.mpl_connect(
                    'button_press_event', self.mouse_press_view))
        elif self.mode == 'add':
            self._calls.append(self.fig.canvas.mpl_connect(
                    'button_press_event', self.mouse_press_add))
        elif self.mode == 'remove':
            self._calls.append(self.fig.canvas.mpl_connect(
                    'button_press_event', self.mouse_press_remove))

    def key_press_event(self, event):
        self.event = event

        if event.key == 'v':
            self.mode = 'view'
        elif event.key == 'a':
            self.mode = 'add'
        elif event.key == 'r':
            self.mode = 'remove'

        log.info("Switching mode to {}".format(self.mode))

        for c in self._calls:
            self.fig.canvas.mpl_disconnect(c)

        self.register_events()

    def mouse_press_add(self, event):
        self.event = event

        p = self._pt_xyz(event)
        if p is not None:
            p = np.array(p)

            log.info("Adding particle at {}".format(p))
            self.pos = np.append(self.pos, p.reshape((1,-1)), axis=0)
        self.update_particle_field(poses=p.reshape((1,-1)))
        self.update_field()
        self.draw()

    def mouse_press_remove(self, event):
        self.event = event

        p = self._pt_xyz(event)
        if p is not None:
            log.info("Removing particle near {}".format(p))
            rp = self._remove_closest_particle(p)
        self.update_particle_field(poses=rp.reshape((1,-1)), add=False)
        self.update_field()
        self.draw()

    def _remove_closest_particle(self, p):
        """removes the closest particle in self.pos to ``p``"""
        #1. find closest pos:
        dp = self.pos - p
        dist2 = (dp*dp).sum(axis=1)
        ind = dist2.argmin()
        rp = self.pos[ind].copy()
        #2. delete
        self.pos = np.delete(self.pos, ind, axis=0)
        return rp

    def _pt_xyz(self, event):
        x0 = event.xdata
        y0 = event.ydata

        f = False
        if event.inaxes == self.g['xy']:
            x = x0
            y = y0
            z = self.slices[0]
            f = True
        if event.inaxes == self.g['yz']:
            x = x0
            y = self.slices[1]
            z = y0
            f = True
        if event.inaxes == self.g['xz']:
            x = self.slices[2]
            y = y0
            z = x0
            f = True

        if f:
            return np.array((z,y,x))
        return None
peri/runner.py
RLOG = logger.log.getChild('runner')
    I = ilms.LegendrePoly2P1D(order=order, constval=image.get_image().mean())
def locate_spheres(image, feature_rad, dofilter=False, order=(3 ,3, 3),
                    trim_edge=True, **kwargs):
def get_initial_featuring(statemaker, feature_rad, actual_rad=None,
        im_name=None, tile=None, invert=True, desc='', use_full_path=False,
        featuring_params={}, statemaker_kwargs={}, **kwargs):
def feature_from_pos_rad(statemaker, pos, rad, im_name=None, tile=None,
        desc='', use_full_path=False, statemaker_kwargs={}, **kwargs):
def optimize_from_initial(s, max_mem=1e9, invert='guess', desc='', rz_order=3,
        min_rad=None, max_rad=None):
def translate_featuring(state_name=None, im_name=None, use_full_path=False,
        **kwargs):
def get_particles_featuring(feature_rad, state_name=None, im_name=None,
        use_full_path=False, actual_rad=None, invert=True, featuring_params={},
        **kwargs):
def _pick_state_im_name(state_name, im_name, use_full_path=False):
def _translate_particles(s, max_mem=1e9, desc='', min_rad='calc',
        max_rad='calc', invert='guess', rz_order=0, do_polish=True):
def link_zscale(st):
def finish_state(st, desc='finish-state', invert='guess'):
peri/comp/exactpsf.py
def moment(p, v, order=1):
    def __init__(self, shape=None, zrange=None, laser_wavelength=0.488,
            zslab=0., zscale=1.0, kfki=0.889, n2n1=1.44/1.518, alpha=1.173,
            polar_angle=0., pxsize=0.125, support_factor=2, normalize=False,
            sigkf=0.0, nkpts=None, cutoffval=None, measurement_iterations=None,
            k_dist='gaussian', use_J1=True, sph6_ab=None, global_zscale=False,
            cutbyval=False, cutfallrate=0.25, cutedgeval=1e-12,
            pinhole_width=None, do_pinhole=False, *args, **kwargs):
    def psffunc(self, *args, **kwargs):
    def zscale(self):
    def set_shape(self, shape, inner):
    def psf_slice(self, zint, size=11, zoffset=0., getextent=False):
    def todict(self):
    def pack_args(self):
    def _p2k(self, v):
    def _tz(self, z):
    def drift(self, z):
    def measure_size_drift(self, z, size=31, zoffset=0.):
    def characterize_psf(self):
    def get_padding_size(self, tile, z=None):
    def update(self, params, values):
    def update_values(self, params, values):
    def set_tile(self, tile):
    def _kpad(self, field, finalshape, zpad=False, norm=True):
    def execute(self, field):
    def nopickle(self):
    def __getstate__(self):
    def __setstate__(self, idict):
    def __init__(self, shape=None, zrange=None, laser_wavelength=0.488, zslab=0.,
            zscale=1.0, kfki=0.889, n2n1=1.44/1.518, alpha=1.173, polar_angle=0.,
            pxsize=0.125, support_factor=2, normalize=False, sigkf=0.0,
            nkpts=None, cutoffval=None, measurement_iterations=None,
            k_dist='gaussian', use_J1=True, sph6_ab=None, global_zscale=False,
            cutbyval=False, cutfallrate=0.25, cutedgeval=1e-12,
            pinhole_width=None, do_pinhole=False, *args, **kwargs):
    def pack_args(self):
    def psffunc(self, *args, **kwargs):
    def __init__(self, shape=None, zrange=None, laser_wavelength=0.488, zslab=0.,
            zscale=1.0, kfki=0.889, n2n1=1.44/1.518, alpha=1.173, polar_angle=0.,
            pxsize=0.125, support_factor=2, normalize=False, sigkf=0.0,
            nkpts=None, cutoffval=None, measurement_iterations=None,
            k_dist='gaussian', use_J1=True, sph6_ab=None, global_zscale=False,
            cutbyval=False, cutfallrate=0.25, cutedgeval=1e-12,
            *args, **kwargs):
    def pack_args(self):
    def psffunc(self, x, y, z, **kwargs):
    def __init__(self, cheb_degree=6, cheb_evals=8, *args, **kwargs):
    def update(self, params, values):
    def psf(self, z):
    def execute(self, field):
    def __str__(self):
    def __init__(self, support_size=[35,17,25], *args, **kwargs):
    def characterize_psf(self):
    def __str__(self):
class ExactPSF(psfs.PSF):
class ExactLineScanConfocalPSF(ExactPSF):
class ExactPinholeConfocalPSF(ExactPSF):
class ChebyshevPSF(ExactPSF):
class FixedSSChebPSF(ChebyshevPSF):
class ChebyshevLineScanConfocalPSF(ChebyshevPSF, ExactLineScanConfocalPSF):
class FixedSSChebLinePSF(FixedSSChebPSF, ExactLineScanConfocalPSF):
class ChebyshevPinholeConfocalPSF(ChebyshevPSF, ExactPinholeConfocalPSF):
class FixedSSChebPinholePSF(FixedSSChebPSF, ExactPinholeConfocalPSF):
peri/comp/objs.py
MAX_VOLUME_ITERATIONS = 10
    N = self.N
class PlatonicParticlesCollection(Component):
class PlatonicSpheresCollection(PlatonicParticlesCollection):
class Slab(Component):
    def __init__(self, pos, shape=None, param_prefix='sph', category='obj',
                support_pad=4, float_precision=np.float64):
    def _draw_particle(self, pos, sign=1):
    def _update_type(self, params):
    def setup_variables(self):
    def get_values(self, params):
    def set_values(self, params, values):
    def add_particle(pos):
    def remove_particle(pos):
    def _drawargs(self):
    def _tile(self, n):
    def param_particle(self, ind):
    def exports(self):
    def _p2i(self, param):
    def initialize(self):
    def get(self):
    def N(self):
    def _vps(self, inds):
    def param_positions(self):
    def param_particle_pos(self, ind):
    def _trans(self, pos):
    def get_positions(self):
    def closest_particle(self, x):
    def params(self):
    def values(self):
    def _i2p(self, ind, coord):
    def get_update_tile(self, params, values):
    def update(self, params, values):
    def __str__(self):
def norm(a):
def inner(r, p, a, zscale=1.0):
def sphere_bool(dr, a, alpha):
def sphere_lerp(dr, a, alpha):
def sphere_logistic(dr, a, alpha):
def sphere_triangle_cdf(dr, a, alpha):
def sphere_analytical_gaussian(dr, a, alpha=0.2765):
def sphere_analytical_gaussian_trim(dr, a, alpha=0.2765, cut=1.6):
def sphere_analytical_gaussian_fast(dr, a, alpha=0.2765, cut=1.20):
def sphere_constrained_cubic(dr, a, alpha):
def exact_volume_sphere(rvec, pos, radius, zscale=1.0, volume_error=1e-5,
        function=sphere_analytical_gaussian, max_radius_change=1e-2, args=()):
    def __init__(self, pos, rad, shape=None, zscale=1.0, support_pad=4,
            method='exact-gaussian-fast', alpha=None, user_method=None,
            exact_volume=True, volume_error=1e-5, max_radius_change=1e-2,
            param_prefix='sph', grouping='particle', category='obj',
            float_precision=np.float64):
    def _drawargs(self):
    def setup_variables(self):
    def get_values(self, params):
    def set_values(self, params, values):
    def set_draw_method(self, method, alpha=None, user_method=None):
    def _draw_particle(self, pos, rad, sign=1):
    def param_radii(self):
    def param_particle(self, ind):
    def param_particle_pos(self, ind):
    def param_particle_rad(self, ind):
    def add_particle(self, pos, rad):
    def remove_particle(self, inds):
    def get_radii(self):
    def exports(self):
    def _p2i(self, param):
    def _update_type(self, params):
    def _tile(self, n):
    def update(self, params, values):
    def __str__(self):
    def __repr__(self):
    def __getstate__(self):
    def __setstate__(self, idict):
    def __init__(self, zpos=0, angles=(0,0), param_prefix='slab', shape=None,
            float_precision=np.float64, category='obj'):
    def rmatrix(self):
    def normal(self):
    def _setup(self):
    def _draw_slab(self):
    def initialize(self):
    def set_tile(self, tile):
    def update(self, params, values):
    def get(self):
    def get_update_tile(self, params, values):
    def __getstate__(self):
    def __setstate__(self, idict):
    def __str__(self):
    def __repr__(self):
peri/states.py
class UpdateError(Exception):
class State(comp.ParameterGroup):
        class _Statewrap(object):
class PolyFitState(State):
class ImageState(State, comp.ComponentCollection):
def sample(field, inds=None, slicer=None, flat=True):
    def __init__(self, params, values, logpriors=None, hyper_params=['sigma'],
                hyper_values=[0.04], **kwargs):
    def data(self):
    def model(self):
    def residuals(self):
    def error(self):
    def loglikelihood(self):
    def logposterior(self):
    def logprior(self):
    def update(self, params, values):
    def update_hyper(self, params, values):
    def update_sigma(self, sigma):
    def push_update(self, params, values):
    def pop_update(self):
    def temp_update(self, params, values):
    def param_all(self):
    def _grad_one_param(self, funct, p, dl=2e-5, rts=False, nout=1, **kwargs):
    def _hess_two_param(self, funct, p0, p1, dl=2e-5, rts=False, **kwargs):
    def _grad(self, funct, params=None, dl=2e-5, rts=False, nout=1, out=None,
            **kwargs):
    def _jtj(self, funct, params=None, dl=2e-5, rts=False, **kwargs):
    def _hess(self, funct, params=None, dl=2e-5, rts=False, **kwargs):
    def _dograddoc(self, f):
    def build_funcs(self):
        def m(inds=None, slicer=None, flat=True):
        def r(inds=None, slicer=None, flat=True):
        def l():
        def r_e(**kwargs):
        def m_e(**kwargs):
            def __init__(self, obj):
            def __getitem__(self, d=None):
    def crb(self, params=None, *args, **kwargs):
    def __str__(self):
    def __repr__(self):
    def __init__(self, x, y, order=2, coeffs=None, sigma=1.0):
    def update(self, params, values):
    def data(self):
    def model(self):
    def __init__(self, image, comps, mdl=models.ConfocalImageModel(), sigma=0.04,
            priors=None, pad=24, model_as_data=False):
    def set_model(self, mdl):
    def set_image(self, image):
    def set_tile_full(self):
    def model_to_data(self, sigma=0.0):
    def reset(self):
    def calculate_model(self):
    def data(self):
    def model(self):
    def residuals(self):
    def loglikelihood(self):
    def get_update_io_tiles(self, params, values):
    def update(self, params, values):
    def get(self, name):
    def set(self, name, obj):
    def _calc_model(self):
    def _calc_residuals(self):
    def _calc_logprior(self):
    def _calc_loglikelihood(self, model=None, tile=None):
    def update_sigma(self, sigma):
    def update_from_model_change(self, oldmodel, newmodel, tile):
    def exports(self):
    def register(self, parent):
    def __str__(self):
        def _pad(s):
    def __getstate__(self):
    def __setstate__(self, idct):
    def set_mem_level(self, mem_level='hi'):
def save(state, filename=None, desc='', extra=None):
def load(filename):
        N = np.size(self.data)
                J = j + i
peri/viz/interaction.py
class OrthoManipulator(object):
    def __init__(self, state, size=8, cmap_abs='bone', cmap_diff='RdBu',
            incsize=18.0, orientation=None, vrange_img=1.0, vrange_diff=0.1):
        """
        An interactive viewer for states for a model in 3D. Can work with
        any 3D state with a set of components. This viewer relies heavily on
        hotkeys. The various modes that can be accessed are by pressing
        these keys:

            'Q' : switch between 'model', 'residual', and 'component' views
            'A' : add particle mode (click position to add)
            'R' : remove particle mode (click position to remove)
            'E' : optimize particle (click particle to optimize)
            'W' : switch to fourier views
            'V' : switch to view mode (click window to move view)
            'C' : If in 'component' views, cycles through components

        Parameters:
        -----------
        state : `peri.states.State` object
            The state object to look at

        size : float
            Relative window size, this is based on matplotlib.figure(figsize)
            and has some units which I don't understand.

        cmap_abs : string
            Colormap to use for the one sided scales such as data and model

        cmap_dff : string
            Colormap to use for difference images, such as residuals

        orientation : one of ['h', 'v', 'horizontal', 'vertical']
            The orientation of the two views

        vrange_img : float
            vmax for imshow for absolute images

        vrange_diff : float
            vmax - vmin for imshow for difference images
        """
        self.incsize = incsize
        self.modes = ['view', 'add', 'remove', 'grab', 'optimize']
        self.mode = self.modes[0]

        self.insets = ['exposure']
        self.inset = 'none'

        self.views = ['field', 'diff', 'comp']
        self.view = self.views[0]

        self.modifiers = ['none', 'fft']
        self.modifier = self.modifiers[0]

        self.components = [
            c.category for c in state.comps if isinstance(c.get(), np.ndarray)
        ]
        self.component = self.components[0]

        self.state = state
        self.cmap_abs = cmap_abs
        self.cmap_diff = cmap_diff
        self.vrange_img = vrange_img
        self.vrange_diff = vrange_diff

        self.state.set_tile(self.state.oshape)

        z,y,x = [float(i) for i in self.state.data.shape]
        w = float(x + z)
        h = float(y + z)

        #tooltip = mpl.rcParams['toolbar']
        #mpl.rcParams['toolbar'] = 'None'

        orientation = orientation or ('horizontal' if w/h < 1.4 else 'vertical')
        if orientation == 'horizontal' or orientation == 'h':
            self.fig = make_clean_figure(figsize=(2*size*w/h, size),
                    remove_keybindings=True)
            Sx, Sy, xoff, yoff = 0.5, 1.0, 0.5, 0.0
        elif orientation == 'vertical' or orientation == 'v':
            self.fig = make_clean_figure(figsize=(2*size*w/h, size),
                    remove_keybindings=True)
            Sx, Sy, xoff, yoff = 1.0, 0.5, 0.0, 0.5
        else:
            raise AttributeError("orientation must be one of '(h)orizontal', '(v)ertical'")

        #mpl.rcParams['toolbar'] = tooltip

        self.gl = {}
        # rect = l,b,w,h
        self.gl['xy'] = self.fig.add_axes((Sx*0.0, yoff+Sy*(1-y/h), Sx*x/w,     Sy*y/h))
        self.gl['yz'] = self.fig.add_axes((Sx*0.0, yoff+Sy*0.0,     Sx*x/w,     Sy*(1-y/h)))
        self.gl['xz'] = self.fig.add_axes((Sx*x/w, yoff+Sy*(1-y/h), Sx*(1-x/w), Sy*y/h))
        self.gl['in'] = self.fig.add_axes((Sx*x/w, yoff+Sy*0.0,     Sx*(1-x/w), Sy*(1-y/h)))

        self.gr = {}
        self.gr['xy'] = self.fig.add_axes((xoff+Sx*0.0, Sy*(1-y/h), Sx*x/w,     Sy*y/h))
        self.gr['yz'] = self.fig.add_axes((xoff+Sx*0.0, Sy*0.0,     Sx*x/w,     Sy*(1-y/h)))
        self.gr['xz'] = self.fig.add_axes((xoff+Sx*x/w, Sy*(1-y/h), Sx*(1-x/w), Sy*y/h))
        self.gr['in'] = self.fig.add_axes((xoff+Sx*x/w, Sy*0.0,     Sx*(1-x/w), Sy*(1-y/h)))

        self.slices = (np.array(self.state.data.shape)/2).astype('int')

        self._grab = None
        self.set_field()
        self.draw()
        self.register_events()

    def _format_ax(self, ax):
        ax.set_xticks([])
        ax.set_yticks([])

    def set_field(self):
        if self.view == 'field':
            out = self.state.model
            vmin, vmax = 0.0, self.vrange_img
            cmap = self.cmap_abs

        if self.view == 'diff':
            out = (self.state.data - self.state.model)
            vmin, vmax = -self.vrange_diff, self.vrange_diff
            cmap = self.cmap_diff

        if self.view == 'comp':
            out = self.state.get(self.component).get()[self.state.inner]
            vmin, vmax = 0.0, self.vrange_img
            cmap = self.cmap_abs

        if self.modifier == 'fft':
            out = np.real(np.abs(np.fft.fftn(out)))**0.2
            out[0,0,0] = 1.0
            out = np.fft.fftshift(out)
            vmin = out.min()
            vmax = out.max()
            cmap = self.cmap_abs

        self.field = out
        self.vmin = vmin
        self.vmax = vmax
        self.cmap = cmap

    def draw(self):
        self.draw_ortho(self.state.data, self.gl, cmap=self.cmap_abs, vmin=0.0, vmax=self.vrange_img)
        self.draw_ortho(self.field, self.gr, cmap=self.cmap, vmin=self.vmin, vmax=self.vmax)

    def draw_ortho(self, im, g, cmap=None, vmin=0, vmax=1):
        slices = self.slices
        int_slice = np.clip(np.round(slices), 0, np.array(im.shape)-1).astype('int')

        g['xy'].cla()
        g['yz'].cla()
        g['xz'].cla()
        g['in'].cla()

        g['xy'].imshow(im[int_slice[0],:,:], vmin=vmin, vmax=vmax, cmap=cmap)
        g['xy'].hlines(slices[1], 0, im.shape[2], colors='y', linestyles='dashed', lw=1)
        g['xy'].vlines(slices[2], 0, im.shape[1], colors='y', linestyles='dashed', lw=1)
        self._format_ax(g['xy'])

        g['yz'].imshow(im[:,int_slice[1],:], vmin=vmin, vmax=vmax, cmap=cmap)
        g['yz'].hlines(slices[0], 0, im.shape[2], colors='y', linestyles='dashed', lw=1)
        g['yz'].vlines(slices[2], 0, im.shape[0], colors='y', linestyles='dashed', lw=1)
        self._format_ax(g['yz'])

        g['xz'].imshow(im[:,:,int_slice[2]].T, vmin=vmin, vmax=vmax, cmap=cmap)
        g['xz'].hlines(slices[1], 0, im.shape[0], colors='y', linestyles='dashed', lw=1)
        g['xz'].vlines(slices[0], 0, im.shape[1], colors='y', linestyles='dashed', lw=1)
        self._format_ax(g['xz'])

        if self.inset == 'exposure':
            m = im*self.state.image_mask
            self.pix = np.r_[m[slices[0],:,:], m[:,:,slices[2]], m[:,slices[1],:]].ravel()
            self.pix = self.pix[self.pix != 0.]
            g['in'].hist(self.pix, bins=300, histtype='step')
            g['in'].semilogy()

            g['in'].set_xlim(0, 1)
            g['in'].set_ylim(9e-1, 1e3)

            if self.view == 'diff' and g == self.gr:
                g['in'].set_xlim(-0.3, 0.3)

        self._format_ax(g['in'])
        pl.draw()

    def register_events(self):
        self._calls = []

        self._calls.append(self.fig.canvas.mpl_connect('key_press_event', self.key_press_event))

        if self.mode == 'view':
            self._calls.append(self.fig.canvas.mpl_connect('button_press_event', self.mouse_press_view))
        if self.mode == 'add':
            self._calls.append(self.fig.canvas.mpl_connect('button_press_event', self.mouse_press_add))
        if self.mode == 'remove':
            self._calls.append(self.fig.canvas.mpl_connect('button_press_event', self.mouse_press_remove))
        if self.mode == 'optimize':
            self._calls.append(self.fig.canvas.mpl_connect('button_press_event', self.mouse_press_optimize))
        if self.mode == 'grab':
            self._calls.append(self.fig.canvas.mpl_connect('button_press_event', self.mouse_press_grab))
            self._calls.append(self.fig.canvas.mpl_connect('motion_notify_event', self.mouse_move_grab))
            self._calls.append(self.fig.canvas.mpl_connect('button_release_event', self.mouse_release_grab))
            self._calls.append(self.fig.canvas.mpl_connect('scroll_event', self.mouse_scroll_grab))

    def mouse_press_grab(self, event):
        self.event = event
        p = self._pt_xyz(event)

        if p is not None:
            self._grab = self.state.obj_closest_particle(p)

            param = self.state.param_particle_pos(self._grab)
            self._pos = self.state.get_values(param)
        else:
            self._grab = None

    def mouse_release_grab(self, event):
        self.event = event
        self._grab = None

    def mouse_move_grab(self, event):
        self.event = event
        p = self._pt_xyz(event)

        if p is not None and self._grab is not None:
            b = self.state.param_particle_pos(self._grab)
            self.state.update(b, p)
            self.set_field()
            self.draw()

    def mouse_scroll_grab(self, event):
        self.event = event
        p = self._pt_xyz(event)

        if p is not None:
            n = self.state.obj_closest_particle(p)
            b = self.state.param_particle_rad(n)
            self.state.update(b, np.array(self.state.get_values(b))+event.step/self.incsize)
            self.set_field()
            self.draw()

    def _pt_xyz(self, event):
        x0 = event.xdata
        y0 = event.ydata

        f = False
        for g in [self.gl, self.gr]:
            if event.inaxes == g['xy']:
                x = x0
                y = y0
                z = self.slices[0]
                f = True
            if event.inaxes == g['yz']:
                x = x0
                y = self.slices[1]
                z = y0
                f = True
            if event.inaxes == g['xz']:
                x = self.slices[2]
                y = y0
                z = x0
                f = True

        if f:
            return np.array((z,y,x))
        return None

    def mouse_press_view(self, event):
        self.event = event

        p = self._pt_xyz(event)
        if p is not None:
            log.info("Moving view to [{:.5}, {:.5}, {:.5}]".format(*p))
            self.slices = p
        self.draw()

    def mouse_press_add(self, event):
        self.event = event

        p = self._pt_xyz(event)
        if p is not None:
            p = np.array(p)

            r = self.state.obj_get_radii()
            if len(r) == 0:
                r = 5.0
            else:
                r = r.mean()

            log.info("Adding particle at [{:.5}, {:.5}, {:.5}], {:.4}".format(
                    *(p.tolist() + [r])))
            self.state.obj_add_particle(p, r)
        self.state.set_tile(self.state.oshape)
        self.set_field()
        self.draw()

    def mouse_press_remove(self, event):
        self.event = event

        p = self._pt_xyz(event)
        if p is not None:
            log.info("Removing particle near [{:.5}, {:.5}, {:.5}]".format(*p))
            ind = self.state.obj_closest_particle(p)
            self.state.obj_remove_particle(ind)
        self.state.set_tile(self.state.oshape)
        self.set_field()
        self.draw()

    def mouse_press_optimize(self, event):
        self.event = event
        p = self._pt_xyz(event)

        if p is not None:
            log.info("Optimizing particle near [{:.5}, {:.5}, {:.5}]".format(*p))
            n = self.state.obj_closest_particle(p)
            old_err = self.state.error
            _ = opt.do_levmarq_particles(self.state, np.array([n]), max_iter=2)
            new_err = self.state.error
            log.info('{}->{}'.format(old_err, new_err))

        self.state.set_tile(self.state.oshape)
        self.set_field()
        self.draw()

    def cycle(self, c, clist):
        return clist[(clist.index(c)+1) % len(clist)]

    def key_press_event(self, event):
        self.event = event

        if event.key == 'c':
            self.component = self.cycle(self.component, self.components)
            self.set_field()
            self.draw()
            return
        if event.key == 'v':
            self.mode = 'view'
        if event.key == 'a':
            self.mode = 'add'
        if event.key == 'r':
            self.mode = 'remove'
        if event.key == 'g':
            self.mode = 'grab'
        if event.key == 'e':
            self.mode = 'optimize'
        if event.key == 'q':
            self.view = self.cycle(self.view, self.views)
            self.set_field()
            self.draw()
            return
        if event.key == 'w':
            self.modifier = self.cycle(self.modifier, self.modifiers)
            self.set_field()
            self.draw()
            return

        log.info("Switching mode to {}".format(self.mode))

        for c in self._calls:
            self.fig.canvas.mpl_disconnect(c)

        self.register_events()
peri/viz/interaction.py
class OrthoViewer(object):
    def __init__(self, field, onesided=True, vmin=None, vmax=None, cmap=None,
                 dohist=False, fourier=False, tooltips=False, size=8):
        """
        Easy interactive viewing of 3D ndarray with view selection.

        Navigate in 3D by clicking on the three panels which are slices
        through the array at a given position.

        Parameters
        ----------
        field : np.ndarray
            The field to view
        onesided : bool, optional
            Whether to use the default one-sided or two-sided colormap.
            Over-ridden by cmap. Default is True
        vmin, vmax : numeric, optional
            The min, max colorbar range, as passed to the matplotlib
            colormap. Default is the (min, max) of the data.
        cmap : {None, valid matplotlib colormap}, optional
            Use to directly a specific colormap, e.g. `'bone'`. If None
            selects `'bone' if onesided else 'RdBu'`. Default is None
        dohist : bool, optional
            Set to True to include a histogram of `field` in an
            additional panel. Default is False
        fourier : bool, optional
            Set to True to view the Fourier transform of field. Default
            is False
        tooltips : bool, optional
            Whether to include the tooltips bar on the figure. Default
            is False
        size : numeric, optional
            The rough figure size of the viewer; the actual size is re-
            scaled based on the field's size. Default is 8
        """
        self.vmin = vmin
        self.vmax = vmax
        self.field = field
        self.onesided = onesided
        self.dohist = dohist
        self.fourier = fourier

        if cmap is not None:
            self.cmap = cmap
        else:
            self.cmap = 'bone' if self.onesided else 'RdBu_r'

        z,y,x = [float(i) for i in self.field.shape]
        w = float(x + z)
        h = float(y + z)

        self.fig = make_clean_figure(
            figsize=(size * w/h, size), remove_tooltips=not tooltips,
            remove_keybindings=True)

        self.g = {}
        # rect = l,b,w,h
        self.g['xy'] = self.fig.add_axes((0.0, 1-y/h, x/w,   y/h))
        self.g['yz'] = self.fig.add_axes((0.0, 0.0,   x/w,   1-y/h))
        self.g['xz'] = self.fig.add_axes((x/w, 1-y/h, 1-x/w, y/h))
        self.g['in'] = self.fig.add_axes((x/w, 0.0,   1-x/w, 1-y/h))

        self.slices = (np.array(self.field.shape)/2).astype('int')

        if self.fourier:
            self.field = np.fft.fftshift(np.fft.fftn(self.field))

        self.draw()
        self.register_events()

    def _format_ax(self, ax):
        ax.set_xticks([])
        ax.set_yticks([])

    def draw(self):
        self.draw_ortho(self.field, self.g, cmap=self.cmap, vmin=self.vmin, vmax=self.vmax)

    def draw_ortho(self, im, g, cmap=None, vmin=0, vmax=1):
        im, vmin, vmax, cmap = self.field, self.vmin, self.vmax, self.cmap

        if self.fourier:
            im = np.abs(im)

        slices = self.slices
        int_slice = np.clip(np.round(slices), 0, np.array(im.shape)-1).astype('int')

        if vmin is None:
            vmin = im.min()
        if vmax is None:
            vmax = im.max()

        if self.cmap == 'RdBu_r':
            val = np.max(np.abs([vmin, vmax]))
            vmin = -val
            vmax = val

        self.g['xy'].cla()
        self.g['yz'].cla()
        self.g['xz'].cla()
        self.g['in'].cla()

        self.g['xy'].imshow(im[int_slice[0],:,:], vmin=vmin, vmax=vmax, cmap=cmap)
        self.g['xy'].hlines(slices[1], 0, im.shape[2], colors='y', linestyles='dashed', lw=1)
        self.g['xy'].vlines(slices[2], 0, im.shape[1], colors='y', linestyles='dashed', lw=1)
        self._format_ax(self.g['xy'])

        self.g['yz'].imshow(im[:,int_slice[1],:], vmin=vmin, vmax=vmax, cmap=cmap)
        self.g['yz'].hlines(slices[0], 0, im.shape[2], colors='y', linestyles='dashed', lw=1)
        self.g['yz'].vlines(slices[2], 0, im.shape[0], colors='y', linestyles='dashed', lw=1)
        self._format_ax(self.g['yz'])

        self.g['xz'].imshow(im[:,:,int_slice[2]].T, vmin=vmin, vmax=vmax, cmap=cmap)
        self.g['xz'].hlines(slices[1], 0, im.shape[0], colors='y', linestyles='dashed', lw=1)
        self.g['xz'].vlines(slices[0], 0, im.shape[1], colors='y', linestyles='dashed', lw=1)
        self._format_ax(self.g['xz'])

        if self.dohist:
            tt = np.real(self.field).ravel()
            c, s = tt.mean(), 5*tt.std()
            y,x = np.histogram(tt, bins=np.linspace(c-s, c+s, 700), normed=True)
            x = (x[1:] + x[:-1])/2

            self.g['in'].plot(x, y, 'k-', lw=1)
            self.g['in'].fill_between(x, y, 1e-10, alpha=0.5)
            self.g['in'].set_yscale('log', nonposy='clip')

            self.g['in'].set_xlim(c-s, c+s)
            self.g['in'].set_ylim(1e-3*y.max(), 1.4*y.max())

        self._format_ax(self.g['in'])

        pl.draw()

    def register_events(self):
        self._calls = []
        self._calls.append(self.fig.canvas.mpl_connect('button_press_event', self.mouse_press_view))

    def _pt_xyz(self, event):
        x0 = event.xdata
        y0 = event.ydata

        f = False
        if event.inaxes == self.g['xy']:
            x = x0
            y = y0
            z = self.slices[0]
            f = True
        if event.inaxes == self.g['yz']:
            x = x0
            y = self.slices[1]
            z = y0
            f = True
        if event.inaxes == self.g['xz']:
            x = self.slices[2]
            y = y0
            z = x0
            f = True

        if f:
            return np.array((z,y,x))
        return None

    def mouse_press_view(self, event):
        self.event = event

        p = self._pt_xyz(event)
        if p is not None:
            self.slices = p
        self.draw()
peri/util.py
def oddify(num):
def listify(a):
def delistify(a, b=None):
def amin(a, b):
def amax(a, b):
def aN(a, dim=3, dtype='int'):
def getdtype(types):
def getdim(a):
def isint(dtype):
    def patch(self, var):
    def __init__(self, left, right=None, mins=None, maxs=None,
                 size=None, centered=False, dim=None, dtype='int'):
    def _build_caches(self):
    def slicer(self):
    def oslicer(self, tile):
    def shape(self):
    def bounds(self):
    def center(self):
    def volume(self):
    def kcenter(self):
    def corners(self):
    def _format_vector(self, vecs, form='broadcast'):
    def coords(self, norm=False, form='broadcast'):
    def kvectors(self, norm=False, form='broadcast', real=False, shift=False):
    def __str__(self):
    def __repr__(self):
    def contains(self, items, pad=0):
    def intersection(tiles, *args):
    def boundingtile(tiles, *args):
    def __eq__(self, other):
    def __ne__(self, other):
    def __and__(self, other):
    def __or__(self, other):
    def copy(self):
    def translate(self, dr):
    def pad(self, pad):
    def overhang(self, tile):
    def reflect_overhang(self, clip):
    def astype(self, dtype):
    def __getstate__(self):
    def __setstate__(self, idct):
    def __init__(self, image, tile=None, filters=None):
    def get_image(self):
    def get_padded_image(self, pad, padval=0):
    def filtered_image(self, im):
    def set_tile(self, tile):
    def set_filter(self, slices, values):
    def __repr__(self):
    def __str__(self):
    def __init__(self, image=None, shape=None):
    def __getstate__(self):
    def __setstate__(self, idct):
    def __repr__(self):
    def __str__(self):
    def __init__(self, filename, tile=None, invert=False, exposure=None,
            float_precision=np.float64):
    def load_image(self):
    def set_scale(self, exposure):
    def get_scale(self):
    def get_scale_from_raw(raw, scaled):
    def __getstate__(self):
    def __setstate__(self, idct):
    def __repr__(self):
    def __str__(self):
def cdd(d, k):
    def __init__(self, num, label='Progress', value=0, screen=79,
            time_remaining=True, bar=True, bar_symbol='=', bar_caps='[]',
            bar_decimals=2, display=True):
    def _estimate_time(self):
    def _draw(self):
    def increment(self):
    def update(self, value=0):
    def end(self):
def newcache():
def memoize(cache_max_size=1e9):
    def memoize_inner(obj):
        def wrapper(self, *args, **kwargs):
                def clear_cache(self):
def patch_docs(subclass, superclass):
def indir(path):
class CompatibilityPatch(object):
class Tile(CompatibilityPatch):
class Image(object):
class NullImage(Image):
class RawImage(Image, CompatibilityPatch):
class ProgressBar(object):
peri/comp/comp.py
class NotAParameterError(Exception):
class ParameterGroup(object):
class Component(ParameterGroup, util.CompatibilityPatch):
class GlobalScalar(Component):
class ComponentCollection(Component):
    def __init__(self, params=None, values=None, ordered=True, category='param'):
    def update(self, params, values):
    def get_values(self, params):
    def set_values(self, params, values):
    def params(self):
    def values(self):
    def nopickle(self):
    def initargs(self):
    def __str__(self):
    def __repr__(self):
    def __getstate__(self):
    def __setstate__(self, idct):
    def __init__(self, params, values, ordered=True, category='comp'):
    def initialize(self):
    def get_update_tile(self, params, values):
    def get_padding_size(self, tile):
    def set_tile(self, tile):
    def set_shape(self, shape, inner):
    def execute(self, *args, **kwargs):
    def get(self):
    def exports(self):
    def nopickle(self):
    def register(self, obj):
    def trigger_parameter_change(self):
    def trigger_update(self, params, values):
    def __call__(self, *args, **kwargs):
    def __init__(self, name, value, shape=None):
    def initargs(self):
    def get(self):
    def get_update_tile(self, params, values):
def reduce_add(x):
def reduce_mul(x):
    def __init__(self, comps, field_reduce_func=None, category='comp'):
    def initargs(self):
    def initialize(self):
    def setup_params(self):
    def split_params(self, params, values=None):
    def affected_components(self, params):
    def update(self, params, values):
    def get_values(self, params):
    def set_values(self, params, values):
    def params(self):
    def values(self):
    def get_update_tile(self, params, values):
    def get_padding_size(self, tile):
    def set(self, name, obj):
    def get(self):
    def set_tile(self, tile):
    def set_shape(self, shape, inner):
    def sync_params(self):
        def _normalize(comps, param):
    def trigger_parameter_change(self):
    def setup_passthroughs(self):
    def exports(self):
    def nopickle(self):
    def __str__(self):
        def _pad(s):
    def __repr__(self):
peri/comp/ilms.py
class Polynomial3D(Component):
class LegendrePoly3D(Polynomial3D):
class Polynomial2P1D(Polynomial3D):
class LegendrePoly2P1D(Polynomial2P1D):
class ChebyshevPoly2P1D(Polynomial2P1D):
class BarnesPoly(Component, util.CompatibilityPatch):
class BarnesStreakLegPoly2P1D(BarnesPoly):
class BarnesXYLegPolyZ(BarnesPoly):
    def __init__(self, order=(1,1,1), tileinfo=None, constval=None,
            category='ilm', shape=None, float_precision=np.float64):
    def initialize(self):
    def rvecs(self):
    def term_ijk(self, index):
    def term(self, index):
    def set_tile(self, tile):
    def update(self, params, values):
    def get(self):
    def get_params(self):
    def get_update_tile(self, params, values):
    def nopickle(self):
    def __str__(self):
    def __repr__(self):
    def __getstate__(self):
    def __setstate__(self, idict):
    def __init__(self, *args, **kwargs):
    def rvecs(self):
    def term_ijk(self, index):
    def __init__(self, order=(1,1,1), tileinfo=None, constval=None,
            operation='*', category='ilm', shape=None,
            float_precision=np.float64):
    def initialize(self):
    def calc_field(self):
    def term_ijk(self, index):
    def update(self, params, values):
    def nopickle(self):
    def __init__(self, order=(1,1,1), **kwargs):
    def rvecs(self):
    def term_ijk(self, index):
    def __init__(self, order=(1,1,1), **kwargs):
    def term_ijk(self, index):
    def __init__(self, npts=(40,20), zorder=7, op='*', barnes_dist=1.75,
            barnes_clip_size=3, local_updates=True, category='ilm', shape=None,
            float_precision=np.float64, donorm=True):
    def _setup_barnes_params(self):
    def _setup_rvecs(self):
    def _barnes(self, y):
    def _barnes_val(self):
    def _barnes_full(self):
    def get_update_tile(self, params, values):
    def randomize_parameters(self, params, values):
    def param_barnes_scales(self):
    def param_barnes_pts(self, ind=None):
    def param_barnes_poly(self):
    def _barnes_poly(self, n=0):
    def _term(self, index):
    def off(self):
    def scale(self):
    def calc_field(self):
    def calc_poly(self):
    def initialize(self):
    def set_tile(self, tile):
    def update(self, params, values):
    def get(self):
    def nopickle(self):
    def __str__(self):
    def __repr__(self):
    def __getstate__(self):
    def __setstate__(self, idict):
    def __init__(self, npts=(40,20), zorder=7, op='*', barnes_dist=1.75,
            barnes_clip_size=3, local_updates=True, category='ilm', shape=None,
            float_precision=np.float64, donorm=True):
    def _setup_barnes_params(self):
    def _setup_rvecs(self):
    def _barnes(self, y, n=0):
    def _barnes_val(self, n=0):
    def _barnes_full(self):
    def get_update_tile(self, params, values):
    def randomize_parameters(self, ptp=0.2, fourier=False, vmin=None, vmax=None):
    def __setstate__(self, idict):
    def __init__(self, npts=(40,20), zorder=7, op='*', barnes_dist=1.75,
            barnes_clip_size=3, category='ilm', shape=None,
            float_precision=np.float64):
    def _setup_barnes_params(self):
    def _barnes(self, pos):
    def _barnes_val(self):
    def _setup_rvecs(self):
    def _barnes_full(self):
    def get_update_tile(self, params, values):
    def randomize_parameters(self, **kwargs):
    def nopickle(self):
            N = len(p)
peri/models.py
class ModelError(Exception):
class Model(object):
class ConfocalImageModel(Model):
class SmoothFieldModel(Model):
class BlurredFieldModel(Model):
class ParticlesOnlyModel(Model):
class ConfocalDyedParticlesModel(Model):
class BrightfieldImageModel(Model):
class IlluminatedParticlesModel(Model):
    def __init__(self, modelstr, varmap, registry={}):
    def check_consistency(self):
    def check_inputs(self, comps):
    def diffname(self, name):
    def get_base_model(self):
    def get_difference_model(self, category):
    def map_vars(self, comps, funcname='get', diffmap=None, **kwargs):
    def evaluate(self, comps, funcname='get', diffmap=None, **kwargs):
    def __str__(self):
    def __repr__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):
    def __init__(self):


Question: import numpy as np
from peri import util
from peri import models
from peri import states
from peri import runner
from peri.comp import objs
from peri.comp import comp
from peri.comp import ilms
from peri.comp import exactpsf
from peri.viz.interaction import OrthoViewer, OrthoPrefeature, OrthoManipulator
"""
This script runs a stylized version of the first half of the tutorial
Rather than load in images, it creates a fake image automatically and
fits that image.

To ensure reproducibility in the image and fit, we set a seed for
numpy's random number generator as well.
"""
# All imports that are used in the tutorial

# Setting the seed for reproducibility
np.random.seed(10)

# Some hard-coded constants to create an image like the tutorial:
POS = np.array([
    [ 11.21179157,  17.46765157,  51.33777692],
    [ 11.58861193,  26.21946861,   7.25906492],
    [ 12.22432771,  33.29800381,  60.62781668],
    [ 13.81552881,   3.57275724,  56.57804871],
    [ 12.16116169,   9.64765222,  17.41146506],
    [ 12.99814567,  25.05624667,  19.14195006],
    [ 12.33297547,  27.87231729,  32.28411386],
    [ 11.17763533,   7.7027628 ,  46.04353855],
    [ 12.91657615,  46.61849718,  48.63677961],
    [ 14.81333917,  48.40786972,  60.60469214],
    [ 12.84487501,  57.87788986,  20.86220673],
    [ 13.84516711,  16.29322417,  27.40591646],
    [ 14.02667269,  47.73248733,  33.11841744],
    [ 12.74104265,  60.46075745,  53.37425778],
    [ 15.23753621,  19.69860468,  41.39355538],
    [ 15.27358198,  38.40844312,  26.7318029 ],
    [ 14.73745621,  44.55385863,  11.56340392],
    [ 14.5520839 ,  61.13087086,  38.83619259],
    [ 15.9845791 ,   5.01834357,  27.06605462],
    [ 16.82137875,  10.45300331,   4.88053659],
    [ 17.41277388,  56.40107326,   8.04239777],
    [ 18.59775505,  30.36350004,  51.76679362],
    [ 23.56714996,   8.4685478 ,  45.50606527],
    [ 19.934775  ,  14.18808795,  58.83030076],
    [ 23.99089835,  45.85751264,  41.88734528],
    [ 23.54841012,  34.97472514,   7.23703913],
    [ 25.59591733,  51.71560733,  23.59087041],
    [ 25.25933489,  23.53414842,  30.43759373],
    [ 25.40082208,  31.76936258,  38.99569137],
    [ 27.18259911,  19.49270884,  18.0170834 ],
    [ 28.16163767,  23.28581272,   3.53311514],
    [ 31.05964615,   1.95065449,  16.10347881],
    [ 31.84595942,   3.03071488,  56.75101873],
    [ 32.75471038,  44.04633015,  57.00660619],
    [ 34.06851749,  51.35646408,  45.66477822],
    [ 20.81021722,  -1.61882524,   2.86862165],
    [ 33.79707333,  12.93023334,  64.14614371],
    [ 25.65539255,  54.53882984,  65.37602364],
    [ 11.72914944,  49.52578712,  -3.78024544],
    [ 11.99716121,  -3.42522376,  39.93710065],
    [ 12.07976975,  -3.26316596,  17.33460135],
    [ 11.87397218,  28.44581287,  -4.29102325],
    [ 19.44967917,  -3.85110892,  63.58394976],
    [ 32.08661926,  67.28521317,  39.28653216],
    [ 14.83352727,  18.03760489,  -4.66327152],
    [ 19.82193879,  -4.05004067,  48.38192053],
    [ 26.7578654 ,  69.44148187,  29.26388449]])
ILMVALS = [1.9, -0.14, 0.0064, 0.0062, 0.078, 0.12, 0.12, 0.12, 0.12, 0.12,
            0.12, 0.12, 0.11, 0.11, 0.11, 0.11, -0.086, 0.02, 0.014, -0.00019,
            -0.01, -0.0058, 0.0012, 0.0052, -0.0014, -0.026, -0.037, 0.0053,
            -0.017, -0.012, -0.004, 0.00034, -0.0012, -0.011, -0.056, -0.099,
            -0.015, -0.011, -0.00043, -0.091, -0.51, -0.15, 0.39, -0.3, 0.63,
            -0.27, 0.19, -0.09]
BKGVALS = [-1.2, 0.0082, 0.0043, -0.0047, -0.0059, -1.4, 0.82, -0.59, 0.99,
            0.00067, 0.33]

def scramble_positions(p, delete_frac=0.1):
    """randomly deletes particles and adds 1-px noise for a realistic
    initial featuring guess"""
    probs = [1-delete_frac, delete_frac]
    m = np.random.choice([True, False], p.shape[0], p=probs)
    jumble = np.random.randn(m.sum(), 3)
    return p[m] + jumble


def create_img():
    """Creates an image, as a `peri.util.Image`, which is similar
    to the image in the tutorial"""
    # 1. particles + coverslip
    rad = 0.5 * np.random.randn(POS.shape[0]) + 4.5  # 4.5 +- 0.5 px particles

Answer:     part = objs.PlatonicSpheresCollection(POS, rad, zscale=0.89)