コード例 #1
0
ファイル: geometry.py プロジェクト: chllym/prysm
def rotated_ellipse(width_major, width_minor, major_axis_angle=0, samples=128):
    """Generate a binary mask for an ellipse, centered at the origin.

    The major axis will notionally extend to the limits of the array, but this
    will not be the case for rotated cases.

    Parameters
    ----------
    width_major : `float`
        width of the ellipse in its major axis
    width_minor : `float`
        width of the ellipse in its minor axis
    major_axis_angle : `float`
        angle of the major axis w.r.t. the x axis, degrees
    samples : `int`
        number of samples

    Returns
    -------
    `numpy.ndarray`
        An ndarray of shape (samples,samples) of value 0 outside the ellipse,
        and value 1 inside the ellipse

    Notes
    -----
    The formula applied is:
         ((x-h)cos(A)+(y-k)sin(A))^2      ((x-h)sin(A)+(y-k)cos(A))^2
        ______________________________ + ______________________________ 1
                     a^2                               b^2
    where x and y are the x and y dimensions, A is the rotation angle of the
    major axis, h and k are the centers of the the ellipse, and a and b are
    the major and minor axis widths.  In this implementation, h=k=0 and the
    formula simplifies to:
            (x*cos(A)+y*sin(A))^2             (x*sin(A)+y*cos(A))^2
        ______________________________ + ______________________________ 1
                     a^2                               b^2

    see SO:
    https://math.stackexchange.com/questions/426150/what-is-the-general-equation-of-the-ellipse-that-is-not-in-the-origin-and-rotate

    Raises
    ------
    ValueError
        Description

    """
    if width_minor > width_major:
        raise ValueError(
            'By definition, major axis must be larger than minor.')

    arr = m.ones((samples, samples))
    lim = width_major
    x, y = m.linspace(-lim, lim, samples), m.linspace(-lim, lim, samples)
    xv, yv = m.meshgrid(x, y)
    A = m.radians(-major_axis_angle)
    a, b = width_major, width_minor
    major_axis_term = ((xv * m.cos(A) + yv * m.sin(A))**2) / a**2
    minor_axis_term = ((xv * m.sin(A) - yv * m.cos(A))**2) / b**2
    arr[major_axis_term + minor_axis_term > 1] = 0
    return arr
コード例 #2
0
ファイル: detector.py プロジェクト: fossabot/prysm
    def analytic_ft(self, unit_x, unit_y):
        ''' Analytic fourier transform of a pixel aperture

        Args:
            unit_x (numpy.ndarray): sample points in x axis.

            unit_y (numpy.ndarray): sample points in y axis.

        Returns:
            `numpy.ndarray`: 2D numpy array containing the analytic fourier transform.

        '''
        xq, yq = np.meshgrid(unit_x, unit_y)
        return (cos(2 * xq * self.width_x / 1e3) *
                cos(2 * yq * self.width_y / 1e3)).astype(config.precision)
コード例 #3
0
ファイル: objects.py プロジェクト: samkberry/prysm
    def __init__(self,
                 angle=4,
                 background='white',
                 sample_spacing=2,
                 samples=256):
        """Create a new TitledSquare instance.

        Parameters
        ----------
        angle : `float`
            angle in degrees to tilt w.r.t. the x axis
        background : `string`
            white or black; the square will be the opposite color of the background
        sample_spacing : `float`
            spacing of samples
        samples : `int`
            number of samples

        """
        radius = 0.3
        if background.lower() == 'white':
            arr = m.ones((samples, samples))
            fill_with = 0
        else:
            arr = m.zeros((samples, samples))
            fill_with = 1

        # TODO: optimize by working with index numbers directly and avoid
        # creation of X,Y arrays for performance.
        x = m.linspace(-0.5, 0.5, samples)
        y = m.linspace(-0.5, 0.5, samples)
        xx, yy = m.meshgrid(x, y)
        sf = samples * sample_spacing

        # TODO: convert inline operation to use of rotation matrix
        angle = m.radians(angle)
        xp = xx * m.cos(angle) - yy * m.sin(angle)
        yp = xx * m.sin(angle) + yy * m.cos(angle)
        mask = (abs(xp) < radius) * (abs(yp) < radius)
        arr[mask] = fill_with
        super().__init__(data=arr,
                         unit_x=x * sf,
                         unit_y=y * sf,
                         has_analytic_ft=False)
コード例 #4
0
    def __init__(self,
                 angle=8,
                 background='white',
                 sample_spacing=2,
                 samples=384):
        ''' Creates a new TitledSquare instance.

        Args:
            angle (`float`): angle in degrees to tilt w.r.t. the x axis.

            background (`string`): white or black; the square will be the opposite
                color of the background.

            sample_spacing (`float`): spacing of samples.

            samples (`int`): number of samples.

        Returns:
            `TiltedSquare`: new TiltedSquare instance.

        '''
        radius = 0.3
        if background.lower() == 'white':
            arr = np.ones((samples, samples))
            fill_with = 0
        else:
            arr = np.zeros((samples, samples))
            fill_with = 1

        # TODO: optimize by working with index numbers directly and avoid
        # creation of X,Y arrays for performance.
        x = np.linspace(-0.5, 0.5, samples)
        y = np.linspace(-0.5, 0.5, samples)
        xx, yy = np.meshgrid(x, y)

        # TODO: convert inline operation to use of rotation matrix
        angle = np.radians(angle)
        xp = xx * cos(angle) - yy * sin(angle)
        yp = xx * sin(angle) + yy * cos(angle)
        mask = (abs(xp) < radius) * (abs(yp) < radius)
        arr[mask] = fill_with
        super().__init__(data=arr,
                         sample_spacing=sample_spacing,
                         synthetic=True)
コード例 #5
0
ファイル: objects.py プロジェクト: samkberry/prysm
    def __init__(self,
                 spokes,
                 sinusoidal=True,
                 background='black',
                 sample_spacing=2,
                 samples=256):
        """Produces a Siemen's Star.

        Parameters
        ----------
        spokes : `int`
            number of spokes in the star.
        sinusoidal : `bool`
            if True, generates a sinusoidal Siemen' star, else, generates a bar/block siemen's star
        background : 'string', {'black', 'white'}
            background color
        sample_spacing : `float`
            spacing of samples, in microns
        samples : `int`
            number of samples per dimension in the synthetic image

        Raises
        ------
        ValueError
            background other than black or white

        """
        relative_width = 0.9
        self.spokes = spokes

        # generate a coordinate grid
        x = m.linspace(-1, 1, samples)
        y = m.linspace(-1, 1, samples)
        xx, yy = m.meshgrid(x, y)
        rv, pv = cart_to_polar(xx, yy)
        ext = sample_spacing * samples / 2
        ux, uy = m.arange(-ext, ext,
                          sample_spacing), m.arange(-ext, ext, sample_spacing)

        # generate the siemen's star as a (rho,phi) polynomial
        arr = m.cos(spokes / 2 * pv)

        if not sinusoidal:  # make binary
            arr[arr < 0] = -1
            arr[arr > 0] = 1

        # scale to (0,1) and clip into a disk
        arr = (arr + 1) / 2
        if background.lower() in ('b', 'black'):
            arr[rv > relative_width] = 0
        elif background.lower() in ('w', 'white'):
            arr[rv > relative_width] = 1
        else:
            raise ValueError('invalid background color')

        super().__init__(data=arr, unit_x=ux, unit_y=uy, has_analytic_ft=False)
コード例 #6
0
    def analytic_ft(self, unit_x, unit_y):
        """Analytic fourier transform of a pixel aperture.

        Parameters
        ----------
        unit_x : `numpy.ndarray`
            sample points in x axis
        unit_y : `numpy.ndarray`
            sample points in y axis

        Returns
        -------
        `numpy.ndarray`
            2D numpy array containing the analytic fourier transform

        """
        xq, yq = m.meshgrid(unit_x, unit_y)
        return (m.cos(2 * xq * self.width_x) *
                m.cos(2 * yq * self.width_y)).astype(config.precision)
コード例 #7
0
    def __init__(self,
                 num_spokes,
                 sinusoidal=True,
                 background='black',
                 sample_spacing=2,
                 samples=384):
        ''' Produces a Siemen's Star.

        Args:
            num_spokes (`int`): number of spokes in the star.

            sinusoidal (`bool`): if True, generates a sinusoidal Siemen' star.
                If false, generates a bar/block siemen's star.

            background ('string'): "black" or "white".

            sample_spacing (`float`): Spacing of samples, in microns.

            samples (`int`): number of samples per dimension in the synthetic image.

        '''
        relative_width = 0.9
        self.num_spokes = num_spokes

        # generate a coordinate grid
        x = np.linspace(-1, 1, samples)
        y = np.linspace(-1, 1, samples)
        xx, yy = np.meshgrid(x, y)
        rv, pv = cart_to_polar(xx, yy)

        # generate the siemen's star as a (rho,phi) polynomial
        arr = cos(num_spokes / 2 * pv)

        if not sinusoidal:  # make binary
            arr[arr < 0] = -1
            arr[arr > 0] = 1

        # scale to (0,1) and clip into a disk
        arr = (arr + 1) / 2
        if background.lower() in ('b', 'black'):
            arr[rv > relative_width] = 0
        elif background.lower() in ('w', 'white'):
            arr[rv > relative_width] = 1
        else:
            raise ValueError('invalid background color')

        super().__init__(data=arr,
                         sample_spacing=sample_spacing,
                         synthetic=True)
コード例 #8
0
def cie_1976_wavelength_annotations(wavelengths, fig=None, ax=None):
    ''' Draws lines normal to the spectral locust on a CIE 1976 diagram and
        writes the text for each wavelength.

    Args:
        wavelengths (`iterable`): set of wavelengths to annotate.

        fig (`matplotlib.figure.Figure`): figure to draw on.

        ax (`matplotlib.axes.Axis`): axis to draw in.

    Returns:

        `tuple` containing:

            `matplotlib.figure.Figure`: figure containing the annotations.

            `matplotlib.axes.Axis`: axis containing the annotations.

    Notes:
        see SE:
        https://stackoverflow.com/questions/26768934/annotation-along-a-curve-in-matplotlib

    '''
    # some tick parameters
    tick_length = 0.025
    text_offset = 0.06

    # convert wavelength to u' v' coordinates
    wavelengths = np.asarray(wavelengths)
    idx = np.arange(1, len(wavelengths) - 1, dtype=int)
    wvl_lbl = wavelengths[idx]
    uv = XYZ_to_uvprime(wavelength_to_XYZ(wavelengths))
    u, v = uv[..., 0][idx], uv[..., 1][idx]
    u_last, v_last = uv[..., 0][idx - 1], uv[..., 1][idx - 1]
    u_next, v_next = uv[..., 0][idx + 1], uv[..., 1][idx + 1]

    angle = atan2(v_next - v_last, u_next - u_last) + pi / 2
    cos_ang, sin_ang = cos(angle), sin(angle)
    u1, v1 = u + tick_length * cos_ang, v + tick_length * sin_ang
    u2, v2 = u + text_offset * cos_ang, v + text_offset * sin_ang

    fig, ax = share_fig_ax(fig, ax)
    tick_lines = LineCollection(np.c_[u, v, u1, v1].reshape(-1, 2, 2), color='0.25', lw=1.25)
    ax.add_collection(tick_lines)
    for i in range(len(idx)):
        ax.text(u2[i], v2[i], str(wvl_lbl[i]), va="center", ha="center", clip_on=True)

    return fig, ax
コード例 #9
0
ファイル: objects.py プロジェクト: samkberry/prysm
    def __init__(self,
                 angle=4,
                 contrast=0.9,
                 crossed=False,
                 sample_spacing=2,
                 samples=256):
        """Create a new TitledSquare instance.

        Parameters
        ----------
        angle : `float`
            angle in degrees to tilt w.r.t. the y axis
        contrast : `float`
            difference between minimum and maximum values in the image
        crossed : `bool`, optional
            whether to make a single edge (crossed=False) or pair of crossed edges (crossed=True)
            aka a "BMW target"
        sample_spacing : `float`
            spacing of samples
        samples : `int`
            number of samples

        """
        diff = (1 - contrast) / 2
        arr = m.full((samples, samples), 1 - diff)
        x = m.linspace(-0.5, 0.5, samples)
        y = m.linspace(-0.5, 0.5, samples)
        xx, yy = m.meshgrid(x, y)
        sf = samples * sample_spacing

        angle = m.radians(angle)
        xp = xx * m.cos(angle) - yy * m.sin(angle)
        # yp = xx * m.sin(angle) + yy * m.cos(angle)  # do not need this
        mask = xp > 0  # single edge
        if crossed:
            mask = xp > 0  # set of 4 edges
            upperright = mask & m.rot90(mask)
            lowerleft = m.rot90(upperright, 2)
            mask = upperright | lowerleft

        arr[mask] = diff
        self.contrast = contrast
        self.black = diff
        self.white = 1 - diff
        super().__init__(data=arr,
                         unit_x=x * sf,
                         unit_y=y * sf,
                         has_analytic_ft=False)
コード例 #10
0
def polar_to_cart(rho, phi):
    ''' Returns the (x,y) coordinates of the (rho,phi) input points.

    Args:
        rho (`float` or `numpy.ndarray`): radial coordinate.

        phi (`float` or `numpy.ndarray`): azimuthal cordinate.

    Returns:
        `tuple` containing:

            `float` or `numpy.ndarray`: x coordinate.

            `float` or `numpy.ndarray`: y coordinate.

    '''
    x = rho * cos(phi)
    y = rho * sin(phi)
    return x, y
コード例 #11
0
def polar_to_cart(rho, phi):
    '''Return the (x,y) coordinates of the (rho,phi) input points.

    Parameters
    ----------
    rho : `numpy.ndarray` or number
        radial coordinate
    phi : `numpy.ndarray` or number
        azimuthal coordinate

    Returns
    -------
    x : `numpy.ndarray` or number
        x coordinate
    y : `numpy.ndarray` or number
        y coordinate

    '''
    x = rho * m.cos(phi)
    y = rho * m.sin(phi)
    return x, y
コード例 #12
0
ファイル: geometry.py プロジェクト: fossabot/prysm
def generate_vertices(num_sides, radius=1):
    ''' Generates a list of vertices for a convex regular polygon with the given
        number of sides and radius.

    Args:
        num_sides (`int`): number of sides to the polygon.

        radius (`float`): radius of the polygon.

    Returns:
        `numpy.ndarray`: array with first column X points, second column Y points

    '''
    angle = 2 * pi / num_sides
    pts = []
    for point in range(num_sides):
        x = radius * sin(point * angle)
        y = radius * cos(point * angle)
        pts.append((int(x), int(y)))

    return np.asarray(pts)
コード例 #13
0
ファイル: interferogram.py プロジェクト: samkberry/prysm
    def total_integrated_scatter(self, wavelength, incident_angle=0):
        """Calculate the total integrated scatter (TIS) for an angle or angles.

        Parameters
        ----------
        wavelength : `float`
            wavelength of light in microns.
        incident_angle : `float` or `numpy.ndarray`
            incident angle(s) of light.

        Returns
        -------
        `float` or `numpy.ndarray`
            TIS value.

        """
        if self.spatial_unit != 'μm':
            raise ValueError('Use microns for spatial unit when evaluating TIS.')

        upper_limit = 1 / wavelength
        kernel = 4 * m.pi * m.cos(m.radians(incident_angle))
        kernel *= self.bandlimited_rms(upper_limit, None) / wavelength
        return 1 - m.exp(-kernel**2)
コード例 #14
0
ファイル: geometry.py プロジェクト: chllym/prysm
def generate_vertices(num_sides, radius=1):
    """Generate a list of vertices for a convex regular polygon with the given number of sides and radius.

    Parameters
    ----------
    num_sides : `int`
        number of sides to the polygon
    radius : `float`
        radius of the polygon

    Returns
    -------
    `numpy.ndarray`
        array with first column X points, second column Y points

    """
    angle = 2 * m.pi / num_sides
    pts = []
    for point in range(num_sides):
        x = radius * m.sin(point * angle)
        y = radius * m.cos(point * angle)
        pts.append((int(x), int(y)))

    return m.asarray(pts)
コード例 #15
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z42(rho, phi):
    return (84 * rho**9 - 168 * rho**7 + 105 * rho**5 - 20 * rho**3) * cos(3 * phi)
コード例 #16
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z40(rho, phi):
    return (28 * rho**8 - 42 * rho**6 + 15 * rho**4) * cos(4 * phi)
コード例 #17
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z38(rho, phi):
    return (7 * rho**7 - 6 * rho**5) * cos(5 * phi)
コード例 #18
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z36(rho, phi):
    return rho**6 * cos(6 * phi)
コード例 #19
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z33(rho, phi):
    return (5 * rho - 60 * rho**3 + 210 * rho**5 - 280 * rho**7 + 126 * rho**9)\
        * cos(phi)
コード例 #20
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z31(rho, phi):
    return (10 * rho**2 - 30 * rho**4 + 21 * rho**6) * cos(2 * phi)
コード例 #21
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z30(rho, phi):
    return (10 * rho**3 - 30 * rho**5 + 21 * rho**7) * cos(3 * phi)
コード例 #22
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z27(rho, phi):
    return (6 * rho**6 - 5 * rho**4) * cos(4 * phi)
コード例 #23
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z25(rho, phi):
    return rho**5 * cos(5 * phi)
コード例 #24
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z22(rho, phi):
    return (-4 * rho + 30 * rho**3 - 60 * rho**5 + 35 * rho**7) * cos(phi)
コード例 #25
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z1(rho, phi):
    return rho * cos(phi)
コード例 #26
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z44(rho, phi):
    return (210 * rho**10 - 504 * rho**8 + 420 * rho**6 - 140 * rho**4 + 15 * rho**2) \
        * cos(2 * phi)
コード例 #27
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z46(rho, phi):
    return (462 * rho**11 - 1260 * rho**9 + 1260 * rho**7 - 560 * rho**5 + 105 * rho**3 - 6 * rho) \
        * cos(phi)
コード例 #28
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z18(rho, phi):
    return (5 * rho**5 - 4 * rho**3) * cos(3 * phi)
コード例 #29
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z4(rho, phi):
    return rho**2 * cos(2 * phi)
コード例 #30
0
ファイル: fringezernike.py プロジェクト: fossabot/prysm
def Z20(rho, phi):
    return (6 * rho**2 - 20 * rho**4 + 15 * rho**6) * cos(2 * phi)