File: preprocess.py

package info (click to toggle)
pybdsf 1.13.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 22,188 kB
  • sloc: fortran: 40,850; python: 14,895; ansic: 4,347; cpp: 1,586; makefile: 131; sh: 46
file content (176 lines) | stat: -rw-r--r-- 6,998 bytes parent folder | download | duplicates (2)
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
"""Module preprocess

Calculates some basic statistics of the image and sets up processing
parameters for PyBDSF.
"""
from __future__ import absolute_import

import numpy as N
from . import _cbdsm
from .image import *
from math import pi, sqrt, log
from . import const
from . import functions as func
from . import mylogger


class Op_preprocess(Op):
    """Preprocessing -- calculate some basic statistics and set
    processing parameters. Should assume that pixels outside the universe
    are blanked in QC ? """

    def __call__(self, img):
        mylog = mylogger.logging.getLogger("PyBDSF."+img.log+"Preprocess")
        bstat = func.bstat
        if img.opts.kappa_clip is None:
            kappa = -img.pixel_beamarea()
        else:
            kappa = img.opts.kappa_clip

        if img.opts.polarisation_do:
            pols = ['I', 'Q', 'U', 'V']
            ch0images = [img.ch0_arr, img.ch0_Q_arr, img.ch0_U_arr, img.ch0_V_arr]
            img.clipped_mean_QUV = []
            img.clipped_rms_QUV = []
        else:
            pols = ['I'] # assume I is always present
            ch0images = [img.ch0_arr]

        if hasattr(img, 'rms_mask'):
            mask = img.rms_mask
        else:
            mask = img.mask_arr
        opts = img.opts

        for ipol, pol in enumerate(pols):
            image = ch0images[ipol]

            ### basic stats
            mean, rms, cmean, crms, cnt = bstat(image, mask, kappa)
            if cnt > 198: cmean = mean; crms = rms
            if pol == 'I':
                if func.approx_equal(crms, 0.0, rel=None):
                    raise RuntimeError('Clipped rms appears to be zero. Check for regions '\
                                           'with values of 0 and\nblank them (with NaNs) '\
                                           'or use trim_box to exclude them.')
                img.raw_mean    = mean
                img.raw_rms     = rms
                img.clipped_mean= cmean
                img.clipped_rms = crms
                mylog.info('%s %.4f %s %.4f %s ' % ("Raw mean (Stokes I) = ", mean*1000.0, \
                           'mJy and raw rms = ',rms*1000.0, 'mJy'))
                mylog.info('%s %.4f %s %s %.4f %s ' % ("sigma clipped mean (Stokes I) = ", cmean*1000.0, \
                           'mJy and ','sigma clipped rms = ',crms*1000.0, 'mJy'))
            else:
                img.clipped_mean_QUV.append(cmean)
                img.clipped_rms_QUV.append(crms)
                mylog.info('%s %s %s %.4f %s %s %.4f %s ' % ("sigma clipped mean (Stokes ", pol, ") = ", cmean*1000.0, \
                           'mJy and ','sigma clipped rms = ',crms*1000.0, 'mJy'))

        image = img.ch0_arr
        # Check if pixels are outside the universe
        if opts.check_outsideuniv:
            mylogger.userinfo(mylog, "Checking for pixels outside the universe")
            noutside_univ = self.outside_univ(img)
            img.noutside_univ = noutside_univ
            frac_blank = round(float(noutside_univ)/float(image.shape[0]*image.shape[1]),3)
            mylogger.userinfo(mylog, "Number of additional pixels blanked", str(noutside_univ)
                              +' ('+str(frac_blank*100.0)+'%)')
        else:
            noutside_univ = 0

        # If needed, (re)mask the image
        if noutside_univ > 0:
            mask = N.isnan(img.ch0_arr)
            masked = mask.any()
            img.masked = masked
            if masked:
                img.mask_arr = mask
            img.blankpix = N.sum(mask)


        ### max/min pixel value & coordinates
        shape = image.shape[0:2]
        if mask is not None:
            img.blankpix = N.sum(mask)
        if img.blankpix == 0:
            max_idx = image.argmax()
            min_idx = image.argmin()
        else:
            max_idx = N.nanargmax(image)
            min_idx = N.nanargmin(image)

        img.maxpix_coord = N.unravel_index(max_idx, shape)
        img.minpix_coord = N.unravel_index(min_idx, shape)
        img.max_value    = image.flat[max_idx]
        img.min_value    = image.flat[min_idx]

        ### Solid angle of the image
        cdelt = N.array(img.wcs_obj.acdelt[:2])
        img.omega = N.prod(shape)*abs(N.prod(cdelt))/(180.*180./pi/pi)

        ### Total flux in ch0 image
        if 'atrous' in img.filename or img._pi or img.log == 'Detection image':
            # Don't do this estimate for atrous wavelet images
            # or polarized intensity image,
            # as it doesn't give the correct flux. Also, ignore
            # the flux in the detection image, as it's likely
            # wrong (e.g., not corrected for the primary beam).
            img.ch0_sum_jy = 0
        else:
            im_flux = N.nansum(image)/img.pixel_beamarea() # Jy
            img.ch0_sum_jy = im_flux
            mylogger.userinfo(mylog, 'Flux from sum of (non-blank) pixels',
                              '%.3f Jy' % (im_flux,))

        ### if image seems confused, then take background mean as zero instead
        alpha_sourcecounts = 2.5  # approx diff src count slope. 2.2?
        if opts.bmpersrc_th is None:
            if mask is not None:
                unmasked = N.where(~img.mask_arr)
                n = (image[unmasked] >= 5.*crms).sum()
            else:
                n = (image >= 5.*crms).sum()
            if n <= 0:
                n = 1
                mylog.info('No pixels in image > 5-sigma.')
                mylog.info('Taking number of pixels above 5-sigma as 1.')
            img.bmpersrc_th = N.prod(shape)/((alpha_sourcecounts-1.)*n)
            mylog.info('%s %6.2f' % ('Estimated bmpersrc_th = ', img.bmpersrc_th))
        else:
            img.bmpersrc_th = opts.bmpersrc_th
            mylog.info('%s %6.2f' % ('Taking default bmpersrc_th = ', img.bmpersrc_th))

        confused = False
        if opts.mean_map == 'default':
            if img.bmpersrc_th <= 25. or cmean/crms >= 0.1:
                confused = True
        img.confused = confused
        mylog.info('Parameter confused is '+str(img.confused))

        img.completed_Ops.append('preprocess')
        return img

    def outside_univ(self,img):
        """ Checks if a pixel is outside the universe and is not blanked,
        and blanks it. (fits files written by CASA dont do this).  """

        noutside = 0
        n, m = img.ch0_arr.shape
        for i in range(n):
            for j in range(m):
                out = False
                err = ''
                pix1 = (i,j)
                try:
                    skyc = img.pix2sky(pix1)
                    pix2 = img.sky2pix(skyc)
                    if abs(pix1[0]-pix2[0]) > 0.5 or abs(pix1[1]-pix2[1]) > 0.5: out=True
                except RuntimeError as err:
                    pass
                if out or ("8" in str(err)):
                    noutside += 1
                    ch0 = img.ch0_arr
                    ch0[pix1] = float("NaN")
                    img.ch0_arr = ch0
        return noutside