This file is indexed.

/usr/lib/python3/dist-packages/photutils/isophote/tests/test_fitter.py is in python3-photutils 0.4-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# Licensed under a 3-clause BSD style license - see LICENSE.rst
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import numpy as np
import pytest

from astropy.io import fits
from astropy.tests.helper import remote_data

from .make_test_data import make_test_image
from ..fitter import EllipseFitter, CentralEllipseFitter
from ..geometry import EllipseGeometry
from ..harmonics import fit_first_and_second_harmonics
from ..integrator import MEAN
from ..isophote import Isophote
from ..sample import EllipseSample, CentralEllipseSample
from ...datasets import get_path

try:
    import scipy    # noqa
    HAS_SCIPY = True
except ImportError:
    HAS_SCIPY = False


DATA = make_test_image(random_state=123)
DEFAULT_POS = 256


def test_gradient():
    sample = EllipseSample(DATA, 40.)
    sample.update()

    assert sample.mean == pytest.approx(200.02, abs=0.01)
    assert sample.gradient == pytest.approx(-4.222, abs=0.001)
    assert sample.gradient_error == pytest.approx(0.0003, abs=0.0001)
    assert sample.gradient_relative_error == pytest.approx(7.45e-05,
                                                           abs=1.e-5)
    assert sample.sector_area == pytest.approx(2.00, abs=0.01)


@pytest.mark.skipif('not HAS_SCIPY')
def test_fitting_raw():
    """
    This test performs a raw (no EllipseFitter), 1-step correction in
    one single ellipse coefficient.
    """

    # pick first guess ellipse that is off in just
    # one of the parameters (eps).
    sample = EllipseSample(DATA, 40., eps=2*0.2)
    sample.update()
    s = sample.extract()

    harmonics = fit_first_and_second_harmonics(s[0], s[2])
    y0, a1, b1, a2, b2 = harmonics[0]

    # when eps is off, b2 is the largest (in absolute value).
    assert abs(b2) > abs(a1)
    assert abs(b2) > abs(b1)
    assert abs(b2) > abs(a2)

    correction = (b2 * 2. * (1. - sample.geometry.eps) /
                  sample.geometry.sma / sample.gradient)
    new_eps = sample.geometry.eps - correction

    # got closer to test data (eps=0.2)
    assert new_eps == pytest.approx(0.21, abs=0.01)


@pytest.mark.skipif('not HAS_SCIPY')
def test_fitting_small_radii():
    sample = EllipseSample(DATA, 2.)
    fitter = EllipseFitter(sample)
    isophote = fitter.fit()

    assert isinstance(isophote, Isophote)
    assert isophote.ndata == 13


@pytest.mark.skipif('not HAS_SCIPY')
def test_fitting_eps():
    # initial guess is off in the eps parameter
    sample = EllipseSample(DATA, 40., eps=2*0.2)
    fitter = EllipseFitter(sample)
    isophote = fitter.fit()

    assert isinstance(isophote, Isophote)
    g = isophote.sample.geometry
    assert g.eps >= 0.19
    assert g.eps <= 0.21


@pytest.mark.skipif('not HAS_SCIPY')
def test_fitting_pa():
    data = make_test_image(pa=np.pi/4, noise=0.01, random_state=123)

    # initial guess is off in the pa parameter
    sample = EllipseSample(data, 40)
    fitter = EllipseFitter(sample)
    isophote = fitter.fit()
    g = isophote.sample.geometry

    assert g.pa >= (np.pi/4 - 0.05)
    assert g.pa <= (np.pi/4 + 0.05)


@pytest.mark.skipif('not HAS_SCIPY')
def test_fitting_xy():
    pos = DEFAULT_POS - 5
    data = make_test_image(x0=pos, y0=pos, random_state=123)

    # initial guess is off in the x0 and y0 parameters
    sample = EllipseSample(data, 40)
    fitter = EllipseFitter(sample)
    isophote = fitter.fit()
    g = isophote.sample.geometry

    assert g.x0 >= (pos - 1)
    assert g.x0 <= (pos + 1)
    assert g.y0 >= (pos - 1)
    assert g.y0 <= (pos + 1)


@pytest.mark.skipif('not HAS_SCIPY')
def test_fitting_all():
    # build test image that is off from the defaults
    # assumed by the EllipseSample constructor.
    POS = DEFAULT_POS - 5
    ANGLE = np.pi / 4
    EPS = 2 * 0.2
    data = make_test_image(x0=POS, y0=POS, eps=EPS, pa=ANGLE,
                           random_state=123)
    sma = 60.

    # initial guess is off in all parameters. We find that the initial
    # guesses, especially for position angle, must be kinda close to the
    # actual value. 20% off max seems to work in this case of high SNR.
    sample = EllipseSample(data, sma, position_angle=(1.2 * ANGLE))
    fitter = EllipseFitter(sample)
    isophote = fitter.fit()

    assert isophote.stop_code == 0

    g = isophote.sample.geometry
    assert g.x0 >= (POS - 1.5)      # position within 1.5 pixel
    assert g.x0 <= (POS + 1.5)
    assert g.y0 >= (POS - 1.5)
    assert g.y0 <= (POS + 1.5)
    assert g.eps >= (EPS - 0.01)    # eps within 0.01
    assert g.eps <= (EPS + 0.01)
    assert g.pa >= (ANGLE - 0.05)   # pa within 5 deg
    assert g.pa <= (ANGLE + 0.05)

    sample_m = EllipseSample(data, sma, position_angle=(1.2 * ANGLE),
                             integrmode=MEAN)
    fitter_m = EllipseFitter(sample_m)
    isophote_m = fitter_m.fit()

    assert isophote_m.stop_code == 0


@remote_data
@pytest.mark.skipif('not HAS_SCIPY')
class TestM51(object):
    def setup_class(self):
        path = get_path('isophote/M51.fits', location='photutils-datasets',
                        cache=True)
        hdu = fits.open(path)
        self.data = hdu[0].data
        hdu.close()

    def test_m51(self):
        # here we evaluate the detailed convergence behavior
        # for a particular ellipse where we can see the eps
        # parameter jumping back and forth.
        # sample = EllipseSample(self.data, 13.31000001, eps=0.16,
        #                        position_angle=((-37.5+90)/180.*np.pi))
        # sample.update()
        # fitter = EllipseFitter(sample)
        # isophote = fitter.fit()

        # we start the fit with initial values taken from
        # previous isophote, as determined by the old code.

        # sample taken in high SNR region
        sample = EllipseSample(self.data, 21.44, eps=0.18,
                               position_angle=(36./180.*np.pi))
        fitter = EllipseFitter(sample)
        isophote = fitter.fit()

        assert isophote.ndata == 119
        assert isophote.intens == pytest.approx(685.4, abs=0.1)

        # last sample taken by the original code, before turning inwards.
        sample = EllipseSample(self.data, 61.16, eps=0.219,
                               position_angle=((77.5+90)/180*np.pi))
        fitter = EllipseFitter(sample)
        isophote = fitter.fit()

        assert isophote.ndata == 382
        assert isophote.intens == pytest.approx(155.0, abs=0.1)

    def test_m51_outer(self):
        # sample taken at the outskirts of the image, so many
        # data points lay outside the image frame. This checks
        # for the presence of gaps in the sample arrays.
        sample = EllipseSample(self.data, 330., eps=0.2,
                               position_angle=((90)/180*np.pi),
                               integrmode='median')
        fitter = EllipseFitter(sample)
        isophote = fitter.fit()

        assert not np.any(isophote.sample.values[2] == 0)

    def test_m51_central(self):
        # this code finds central x and y offset by about 0.1 pixel wrt the
        # spp code. In here we use as input the position computed by this
        # code, thus this test is checking just the extraction algorithm.
        g = EllipseGeometry(257.02, 258.1, 0.0, 0.0, 0.0, 0.1, False)
        sample = CentralEllipseSample(self.data, 0.0, geometry=g)
        fitter = CentralEllipseFitter(sample)
        isophote = fitter.fit()

        # the central pixel intensity is about 3% larger than
        # found by the spp code.
        assert isophote.ndata == 1
        assert isophote.intens <= 7560.
        assert isophote.intens >= 7550.