Beispiel #1
0
def mask_vertices(mask_im, vertices_file):
    """
    Modify the input image to exclude regions outside of the polygon

    Parameters
    ----------
    mask_im : pyrap.images image() object
        Mask image to modify
    vertices_file: str
        Filename of pickle file that contains direction dictionary with
        vertices

    Returns
    -------
    new_im:  pyrap.images image() object
        Modified mask image
    bool_mask :  pyrap.images image() object
        Modified mask image

    """
    import pyrap.images as pim

    ma = mask_im.coordinates()
    new_im = pim.image('',shape=mask_im.shape(), coordsys=ma)
    bool_mask = pim.image('',shape=mask_im.shape(), coordsys=ma)

    img_type = mask_im.imagetype()
    data = mask_im.getdata()
    bool_data = np.ones(data.shape)

    vertices = read_vertices(vertices_file)
    RAverts = vertices[0]
    Decverts = vertices[1]
    xvert = []
    yvert = []
    for RAvert, Decvert in zip(RAverts, Decverts):
        pixels = mask_im.topixel([1, 1, Decvert*np.pi/180.0,
            RAvert*np.pi/180.0])
        xvert.append(pixels[2]) # x -> Dec
        yvert.append(pixels[3]) # y -> RA
    poly = Polygon(xvert, yvert)

    # Find distance to nearest poly edge and unmask those that
    # are outside the facet (dist < 0)
    masked_ind = np.indices(data[0, 0].shape)
    dist = poly.is_inside(masked_ind[0], masked_ind[1])
    outside_ind = np.where(dist < 0.0)
    if len(outside_ind[0]) > 0:
        data[0, 0, masked_ind[0][outside_ind], [masked_ind[1][outside_ind]]] = 0
        bool_data[0, 0, masked_ind[0][outside_ind], [masked_ind[1][outside_ind]]] = 0

    new_im.putdata(data)
    bool_mask.putdata(bool_data)

    return new_im, bool_mask
Beispiel #2
0
def vertices_to_poly(vertices, ref_im):
    """Converts a list of RA, Dec vertices to a Polygon object"""
    RAverts = vertices[0]
    Decverts = vertices[1]
    xvert = []
    yvert = []
    for RAvert, Decvert in zip(RAverts, Decverts):
        try:
            pixels = ref_im.topixel(
                [0, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0])
        except:
            pixels = ref_im.topixel(
                [1, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0])
        xvert.append(pixels[2])  # x -> Dec
        yvert.append(pixels[3])  # y -> RA

    return Polygon(xvert, yvert)
Beispiel #3
0
def main(image_name, mask_name, atrous_do=False, threshisl=0.0, threshpix=0.0, rmsbox=None,
         rmsbox_bright=(35, 7), iterate_threshold=False, adaptive_rmsbox=False, img_format='fits',
         threshold_format='float', trim_by=0.0, vertices_file=None, atrous_jmax=6,
         pad_to_size=None, skip_source_detection=False, region_file=None, nsig=1.0,
         reference_ra_deg=None, reference_dec_deg=None, cellsize_deg=0.000417,
         use_adaptive_threshold=False):
    """
    Make a clean mask and return clean threshold

    Parameters
    ----------
    image_name : str
        Filename of input image from which mask will be made. If the image does
        not exist, a template image with center at (reference_ra_deg,
        reference_dec_deg) will be made internally
    mask_name : str
        Filename of output mask image
    atrous_do : bool, optional
        Use wavelet module of PyBDSM?
    threshisl : float, optional
        Value of thresh_isl PyBDSM parameter
    threshpix : float, optional
        Value of thresh_pix PyBDSM parameter
    rmsbox : tuple of floats, optional
        Value of rms_box PyBDSM parameter
    rmsbox_bright : tuple of floats, optional
        Value of rms_box_bright PyBDSM parameter
    iterate_threshold : bool, optional
        If True, threshold will be lower in 20% steps until
        at least one island is found
    adaptive_rmsbox : tuple of floats, optional
        Value of adaptive_rms_box PyBDSM parameter
    img_format : str, optional
        Format of output mask image (one of 'fits' or 'casa')
    threshold_format : str, optional
        Format of output threshold (one of 'float' or 'str_with_units')
    trim_by : float, optional
        Fraction by which the perimeter of the output mask will be
        trimmed (zeroed)
    vertices_file : str, optional
        Filename of file with vertices (must be a pickle file containing
        a dictionary with the vertices in the 'vertices' entry)
    atrous_jmax : int, optional
        Value of atrous_jmax PyBDSM parameter
    pad_to_size : int, optional
        Pad output mask image to a size of pad_to_size x pad_to_size
    skip_source_detection : bool, optional
        If True, source detection is not run on the input image
    region_file : str, optional
        Filename of region file in CASA format. If given, no mask image
        is made (the region file is used as the clean mask)
    nsig : float, optional
        Number of sigma of returned threshold value
    reference_ra_deg : float, optional
        RA for center of output mask image
    reference_dec_deg : float, optional
        Dec for center of output mask image
    cellsize_deg : float, optional
        Size of a pixel in degrees
    use_adaptive_threshold : bool, optional
        If True, use an adaptive threshold estimated from the negative values in
        the image

    Returns
    -------
    result : dict
        Dict with nsig-sigma rms threshold

    """
    if rmsbox is not None and type(rmsbox) is str:
        rmsbox = eval(rmsbox)

    if type(rmsbox_bright) is str:
        rmsbox_bright = eval(rmsbox_bright)

    if pad_to_size is not None and type(pad_to_size) is str:
        pad_to_size = int(pad_to_size)

    if type(atrous_do) is str:
        if atrous_do.lower() == 'true':
            atrous_do = True
            threshisl = 4.0 # override user setting to ensure proper source fitting
        else:
            atrous_do = False

    if type(iterate_threshold) is str:
        if iterate_threshold.lower() == 'true':
            iterate_threshold = True
        else:
            iterate_threshold = False

    if type(adaptive_rmsbox) is str:
        if adaptive_rmsbox.lower() == 'true':
            adaptive_rmsbox = True
        else:
            adaptive_rmsbox = False

    if type(skip_source_detection) is str:
        if skip_source_detection.lower() == 'true':
            skip_source_detection = True
        else:
            skip_source_detection = False

    if type(use_adaptive_threshold) is str:
        if use_adaptive_threshold.lower() == 'true':
            use_adaptive_threshold = True
        else:
            use_adaptive_threshold = False

    if reference_ra_deg is not None and reference_dec_deg is not None:
        reference_ra_deg = float(reference_ra_deg)
        reference_dec_deg = float(reference_dec_deg)

    if not os.path.exists(image_name):
        print('Input image not found. Making empty image...')
        if not skip_source_detection:
            print('ERROR: Source detection cannot be done on an empty image')
            sys.exit(1)
        if reference_ra_deg is not None and reference_dec_deg is not None:
            image_name = mask_name + '.tmp'
            make_template_image(image_name, reference_ra_deg, reference_dec_deg,
                cellsize_deg=float(cellsize_deg))
        else:
            print('ERROR: if image not found, a refernce position must be given')
            sys.exit(1)

    trim_by = float(trim_by)
    atrous_jmax = int(atrous_jmax)
    threshpix = float(threshpix)
    threshisl = float(threshisl)
    nsig = float(nsig)
    threshold = 0.0

    if not skip_source_detection:
        if vertices_file is not None:
            # Modify the input image to blank the regions outside of the polygon
            temp_img = pim.image(image_name)
            image_name += '.blanked'
            temp_img.saveas(image_name, overwrite=True)
            input_img = pim.image(image_name)
            data = input_img.getdata()

            vertices = read_vertices(vertices_file)
            RAverts = vertices[0]
            Decverts = vertices[1]
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(RAverts, Decverts):
                pixels = input_img.topixel([1, 1, Decvert*np.pi/180.0,
                    RAvert*np.pi/180.0])
                xvert.append(pixels[2]) # x -> Dec
                yvert.append(pixels[3]) # y -> RA
            poly = Polygon(xvert, yvert)

            # Find masked regions
            masked_ind = np.where(data[0, 0])

            # Find distance to nearest poly edge and set to NaN those that
            # are outside the facet (dist < 0)
            dist = poly.is_inside(masked_ind[0], masked_ind[1])
            outside_ind = np.where(dist < 0.0)
            if len(outside_ind[0]) > 0:
                data[0, 0, masked_ind[0][outside_ind], masked_ind[1][outside_ind]] = np.nan

            # Save changes
            input_img.putdata(data)

        if use_adaptive_threshold:
            # Get an estimate of the rms
            img = bdsm.process_image(image_name, mean_map='zero', rms_box=rmsbox,
                                     thresh_pix=threshpix, thresh_isl=threshisl,
                                     atrous_do=atrous_do, ini_method='curvature', thresh='hard',
                                     adaptive_rms_box=adaptive_rmsbox, adaptive_thresh=150,
                                     rms_box_bright=rmsbox_bright, rms_map=True, quiet=True,
                                     atrous_jmax=atrous_jmax, stop_at='isl')

            # Find min and max pixels
            max_neg_val = abs(np.min(img.ch0_arr))
            max_neg_pos = np.where(img.ch0_arr == np.min(img.ch0_arr))
            max_pos_val = abs(np.max(img.ch0_arr))
            max_pos_pos = np.where(img.ch0_arr == np.max(img.ch0_arr))

            # Estimate new thresh_isl from min pixel value's sigma, but don't let
            # it get higher than 1/2 of the peak's sigma
            threshisl_neg = 2.0 * max_neg_val / img.rms_arr[max_neg_pos][0]
            max_sigma = max_pos_val / img.rms_arr[max_pos_pos][0]
            if threshisl_neg > max_sigma / 2.0:
                threshisl_neg = max_sigma / 2.0

            # Use the new threshold only if it is larger than the user-specified one
            if threshisl_neg > threshisl:
                threshisl = threshisl_neg

        if iterate_threshold:
            # Start with given threshold and lower it until we get at least one island
            nisl = 0
            while nisl == 0:
                img = bdsm.process_image(image_name, mean_map='zero', rms_box=rmsbox,
                                         thresh_pix=threshpix, thresh_isl=threshisl,
                                         atrous_do=atrous_do, ini_method='curvature', thresh='hard',
                                         adaptive_rms_box=adaptive_rmsbox, adaptive_thresh=150,
                                         rms_box_bright=rmsbox_bright, rms_map=True, quiet=True,
                                         atrous_jmax=atrous_jmax)
                nisl = img.nisl
                threshpix /= 1.2
                threshisl /= 1.2
                if threshpix < 5.0:
                    break
        else:
            img = bdsm.process_image(image_name, mean_map='zero', rms_box=rmsbox,
                                     thresh_pix=threshpix, thresh_isl=threshisl,
                                     atrous_do=atrous_do, ini_method='curvature', thresh='hard',
                                     adaptive_rms_box=adaptive_rmsbox, adaptive_thresh=150,
                                     rms_box_bright=rmsbox_bright, rms_map=True, quiet=True,
                                     atrous_jmax=atrous_jmax)

        if img.nisl == 0:
            if region_file is None or region_file == '[]':
                print('No islands found. Clean mask cannot be made.')
                sys.exit(1)
            else:
                # Continue on and use user-supplied region file
                skip_source_detection = True
                threshold = nsig * img.clipped_rms

        # Check if there are large islands preset (indicating that multi-scale
        # clean is needed)
        has_large_isl = False
        for isl in img.islands:
            if isl.size_active > 100:
                # Assuming normal sampling, a size of 100 pixels would imply
                # a source of ~ 10 beams
                has_large_isl = True

    if (region_file is not None and region_file != '[]' and skip_source_detection):
        # Copy region file and return if source detection was not done
        os.system('cp {0} {1}'.format(region_file.strip('[]"'), mask_name))
        if threshold_format == 'float':
            return {'threshold_5sig': threshold}
        elif threshold_format == 'str_with_units':
            # This is done to get around the need for quotes around strings in casapy scripts
            # 'casastr/' is removed by the generic pipeline
            return {'threshold_5sig': 'casastr/{0}Jy'.format(threshold)}
    elif not skip_source_detection:
        img.export_image(img_type='island_mask', mask_dilation=0, outfile=mask_name,
                         img_format=img_format, clobber=True)

    if (vertices_file is not None or trim_by > 0 or pad_to_size is not None
        or (region_file is not None and region_file != '[]')
        or skip_source_detection):
        # Alter the mask in various ways
        if skip_source_detection:
            # Read the image
            mask_im = pim.image(image_name)
        else:
            # Read the PyBDSM mask
            mask_im = pim.image(mask_name)
        data = mask_im.getdata()
        coordsys = mask_im.coordinates()
        if reference_ra_deg is not None and reference_dec_deg is not None:
            values = coordsys.get_referencevalue()
            values[2][0] = reference_dec_deg/180.0*np.pi
            values[2][1] = reference_ra_deg/180.0*np.pi
            coordsys.set_referencevalue(values)
        imshape = mask_im.shape()
        del(mask_im)

        if pad_to_size is not None:
            imsize = pad_to_size
            coordsys['direction'].set_referencepixel([imsize/2, imsize/2])
            pixmin = (imsize - imshape[2]) / 2
            if pixmin < 0:
                print("The padded size must be larger than the original size.")
                sys.exit(1)
            pixmax = pixmin + imshape[2]
            data_pad = np.zeros((1, 1, imsize, imsize), dtype=np.float32)
            data_pad[0, 0, pixmin:pixmax, pixmin:pixmax] = data[0, 0]
            new_mask = pim.image('', shape=(1, 1, imsize, imsize), coordsys=coordsys)
            new_mask.putdata(data_pad)
        else:
            new_mask = pim.image('', shape=imshape, coordsys=coordsys)
            new_mask.putdata(data)

        data = new_mask.getdata()

        if skip_source_detection:
            # Mask all pixels
            data[:] = 1

        if vertices_file is not None:
            # Modify the clean mask to exclude regions outside of the polygon
            vertices = read_vertices(vertices_file)
            RAverts = vertices[0]
            Decverts = vertices[1]
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(RAverts, Decverts):
                try:
                    pixels = new_mask.topixel([0, 1, Decvert*np.pi/180.0,
                                               RAvert*np.pi/180.0])
                except:
                    pixels = new_mask.topixel([1, 1, Decvert*np.pi/180.0,
                                               RAvert*np.pi/180.0])
                xvert.append(pixels[2]) # x -> Dec
                yvert.append(pixels[3]) # y -> RA
            poly = Polygon(xvert, yvert)

            # Find masked regions
            masked_ind = np.where(data[0, 0])

            # Find distance to nearest poly edge and unmask those that
            # are outside the facet (dist < 0)
            dist = poly.is_inside(masked_ind[0], masked_ind[1])
            outside_ind = np.where(dist < 0.0)
            if len(outside_ind[0]) > 0:
                data[0, 0, masked_ind[0][outside_ind], masked_ind[1][outside_ind]] = 0

        if trim_by > 0.0:
            sh = np.shape(data)
            margin = int(sh[2] * trim_by / 2.0 )
            data[0, 0, 0:sh[2], 0:margin] = 0
            data[0, 0, 0:margin, 0:sh[3]] = 0
            data[0, 0, 0:sh[2], sh[3]-margin:sh[3]] = 0
            data[0, 0, sh[2]-margin:sh[2], 0:sh[3]] = 0

        if region_file is not None and region_file != '[]':
            # Merge the CASA regions with the mask
            casa_polys = read_casa_polys(region_file.strip('[]"'), new_mask)
            for poly in casa_polys:
                # Find unmasked regions
                unmasked_ind = np.where(data[0, 0] == 0)

                # Find distance to nearest poly edge and mask those that
                # are inside the casa region (dist > 0)
                dist = poly.is_inside(unmasked_ind[0], unmasked_ind[1])
                inside_ind = np.where(dist > 0.0)
                if len(inside_ind[0]) > 0:
                    data[0, 0, unmasked_ind[0][inside_ind], unmasked_ind[1][inside_ind]] = 1

        # Save changes
        new_mask.putdata(data)
        if img_format == 'fits':
            new_mask.tofits(mask_name, overwrite=True)
        elif img_format == 'casa':
            new_mask.saveas(mask_name, overwrite=True)
        else:
            print('Output image format "{}" not understood.'.format(img_format))
            sys.exit(1)

    if not skip_source_detection:
        if threshold_format == 'float':
            return {'threshold_5sig': nsig * img.clipped_rms, 'multiscale': has_large_isl}
        elif threshold_format == 'str_with_units':
            # This is done to get around the need for quotes around strings in casapy scripts
            # 'casastr/' is removed by the generic pipeline
            return {'threshold_5sig': 'casastr/{0}Jy'.format(nsig * img.clipped_rms),
                'multiscale': has_large_isl}
    else:
        return {'threshold_5sig': '0.0'}
Beispiel #4
0
def main(input_image_file, vertices_file, output_image_file, blank_value='zero',
    img_format='fits', image_is_casa_model=False, nterms=1):
    """
    Blank a region in an image

    Parameters
    ----------
    input_image_file : str
        Filename of input image from which mask will be made. If the image does
        not exist, a template image with center at (reference_ra_deg,
        reference_dec_deg) will be made internally
    vertices_file : str, optional
        Filename of file with vertices (must be a pickle file containing
        a dictionary with the vertices in the 'vertices' entry)
    output_image_file : str
        Filename of output image
    blank_value : str, optional
        Value for blanks (one of 'zero' or 'nan')
    img_format : str, optional
        Format of output mask image (one of 'fits' or 'casa')
    image_is_casa_model : bool, optional
        If True, the input and output image files are treated as the root name
        of a casa model image (or images)
    nterms : int, optional
        If image_is_casa_model is True, this argument sets the number of nterms
        for the model

    """
    if type(image_is_casa_model) is str:
        if image_is_casa_model.lower() == 'true':
            image_is_casa_model = True
        else:
            image_is_casa_model = False

    if type(nterms) is str:
        nterms = int(nterms)

    if image_is_casa_model:
        if nterms == 1:
            input_image_files = [input_image_file+'.model']
            output_image_files = [output_image_file+'.model']
        if nterms == 2:
            input_image_files = [input_image_file+'.model.tt0',
                input_image_file+'.model.tt1']
            output_image_files = [output_image_file+'.model.tt0',
                output_image_file+'.model.tt1']
        if nterms == 3:
            input_image_files = [input_image_file+'.model.tt0',
                input_image_file+'.model.tt1', input_image_file+'.model.tt2']
            output_image_files = [output_image_file+'.model.tt0',
                output_image_file+'.model.tt1', output_image_file+'.model.tt2']
    else:
        input_image_files = [input_image_file]
        output_image_files = [output_image_file]

    for input_image, output_image in zip(input_image_files, output_image_files):
        im = pim.image(input_image)
        data = im.getdata()
        coordsys = im.coordinates()
        imshape = im.shape()
        new_im = pim.image('', shape=imshape, coordsys=coordsys)

        # Construct polygon
        vertices = read_vertices(vertices_file)
        RAverts = vertices[0]
        Decverts = vertices[1]
        xvert = []
        yvert = []
        for RAvert, Decvert in zip(RAverts, Decverts):
            pixels = new_im.topixel([0, 1, Decvert*np.pi/180.0,
                RAvert*np.pi/180.0])
            xvert.append(pixels[2]) # x -> Dec
            yvert.append(pixels[3]) # y -> RA
        poly = Polygon(xvert, yvert)

        # Find distance to nearest poly edge and blank those that
        # are outside the facet (dist < 0)
        pix_ind = np.indices(data[0, 0].shape)
        dist = poly.is_inside(pix_ind[0], pix_ind[1])
        outside_ind = np.where(dist < 0.0)
        if len(outside_ind[0]) > 0:
            if blank_value == 'zero':
                blank_val = 0.0
            elif blank_value == 'nan':
                blank_val = np.nan
            else:
                print('Blank value type "{}" not understood.'.format(blank_with))
                sys.exit(1)
            data[0, 0, pix_ind[0][outside_ind], pix_ind[1][outside_ind]] = blank_val

        # Save changes
        new_im.putdata(data)
        if img_format == 'fits':
            new_im.tofits(output_image, overwrite=True)
        elif img_format == 'casa':
            new_im.saveas(output_image, overwrite=True)
        else:
            print('Output image format "{}" not understood.'.format(img_format))
            sys.exit(1)
Beispiel #5
0
def main(image_name,
         mask_name,
         atrous_do=False,
         threshisl=0.0,
         threshpix=0.0,
         rmsbox=None,
         rmsbox_bright=(35, 7),
         iterate_threshold=False,
         adaptive_rmsbox=False,
         img_format='fits',
         threshold_format='float',
         trim_by=0.0,
         vertices_file=None,
         atrous_jmax=6,
         pad_to_size=None,
         skip_source_detection=False,
         region_file=None,
         nsig=5.0,
         reference_ra_deg=None,
         reference_dec_deg=None):
    """
    Make a clean mask and return clean threshold

    Parameters
    ----------
    image_name : str
        Filename of input image from which mask will be made. If the image does
        not exist, a template image with center at (reference_ra_deg,
        reference_dec_deg) will be made internally
    mask_name : str
        Filename of output mask image
    atrous_do : bool, optional
        Use wavelet module of PyBDSM?
    threshisl : float, optional
        Value of thresh_isl PyBDSM parameter
    threshpix : float, optional
        Value of thresh_pix PyBDSM parameter
    rmsbox : tuple of floats, optional
        Value of rms_box PyBDSM parameter
    rmsbox_bright : tuple of floats, optional
        Value of rms_box_bright PyBDSM parameter
    iterate_threshold : bool, optional
        If True, threshold will be lower in 20% steps until
        at least one island is found
    adaptive_rmsbox : tuple of floats, optional
        Value of adaptive_rms_box PyBDSM parameter
    img_format : str, optional
        Format of output mask image (one of 'fits' or 'casa')
    threshold_format : str, optional
        Format of output threshold (one of 'float' or 'str_with_units')
    trim_by : float, optional
        Fraction by which the perimeter of the output mask will be
        trimmed (zeroed)
    vertices_file : str, optional
        Filename of file with vertices (must be a pickle file containing
        a dictionary with the vertices in the 'vertices' entry)
    atrous_jmax : int, optional
        Value of atrous_jmax PyBDSM parameter
    pad_to_size : int, optional
        Pad output mask image to a size of pad_to_size x pad_to_size
    skip_source_detection : bool, optional
        If True, source detection is not run on the input image
    region_file : str, optional
        Filename of region file in CASA format. If given, no mask image
        is made (the region file is used as the clean mask)
    nsig : float, optional
        Number of sigma of returned threshold value
    reference_ra_deg : float, optional
        RA for center of output mask image
    reference_dec_deg : float, optional
        Dec for center of output mask image

    Returns
    -------
    result : dict
        Dict with nsig-sigma rms threshold

    """
    if image_name is not None:
        if image_name.lower() == 'none':
            image_name = None

    if rmsbox is not None and type(rmsbox) is str:
        rmsbox = eval(rmsbox)

    if type(rmsbox_bright) is str:
        rmsbox_bright = eval(rmsbox_bright)

    if pad_to_size is not None and type(pad_to_size) is str:
        pad_to_size = int(pad_to_size)

    if type(atrous_do) is str:
        if atrous_do.lower() == 'true':
            atrous_do = True
            threshisl = 4.0  # override user setting to ensure proper source fitting
        else:
            atrous_do = False

    if type(iterate_threshold) is str:
        if iterate_threshold.lower() == 'true':
            iterate_threshold = True
        else:
            iterate_threshold = False

    if type(adaptive_rmsbox) is str:
        if adaptive_rmsbox.lower() == 'true':
            adaptive_rmsbox = True
        else:
            adaptive_rmsbox = False

    if type(skip_source_detection) is str:
        if skip_source_detection.lower() == 'true':
            skip_source_detection = True
        else:
            skip_source_detection = False

    if reference_ra_deg is not None and reference_dec_deg is not None:
        reference_ra_deg = float(reference_ra_deg)
        reference_dec_deg = float(reference_dec_deg)

    if not os.path.exists(image_name):
        print('Input image not found. Making empty image...')
        if not skip_source_detection:
            print('ERROR: Source detection cannot be done on an empty image')
            sys.exit(1)
        if reference_ra_deg is not None and reference_dec_deg is not None:
            image_name = mask_name + '.tmp'
            make_template_image(image_name, reference_ra_deg,
                                reference_dec_deg)
        else:
            print(
                'ERROR: if image not found, a refernce position must be given')
            sys.exit(1)

    trim_by = float(trim_by)
    atrous_jmax = int(atrous_jmax)
    threshpix = float(threshpix)
    threshisl = float(threshisl)
    nsig = float(nsig)

    if not skip_source_detection:
        if vertices_file is not None:
            # Modify the input image to blank the regions outside of the polygon
            temp_img = pim.image(image_name)
            image_name += '.blanked'
            temp_img.saveas(image_name, overwrite=True)
            input_img = pim.image(image_name)
            data = input_img.getdata()

            vertices = read_vertices(vertices_file)
            RAverts = vertices[0]
            Decverts = vertices[1]
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(RAverts, Decverts):
                pixels = input_img.topixel(
                    [1, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0])
                xvert.append(pixels[2])  # x -> Dec
                yvert.append(pixels[3])  # y -> RA
            poly = Polygon(xvert, yvert)

            # Find masked regions
            masked_ind = np.where(data[0, 0])

            # Find distance to nearest poly edge and set to NaN those that
            # are outside the facet (dist < 0)
            dist = poly.is_inside(masked_ind[0], masked_ind[1])
            outside_ind = np.where(dist < 0.0)
            if len(outside_ind[0]) > 0:
                data[0, 0, masked_ind[0][outside_ind],
                     masked_ind[1][outside_ind]] = np.nan

            # Save changes
            input_img.putdata(data)

        if iterate_threshold:
            # Start with given threshold and lower it until we get at least one island
            nisl = 0
            while nisl == 0:
                img = bdsm.process_image(image_name,
                                         mean_map='zero',
                                         rms_box=rmsbox,
                                         thresh_pix=threshpix,
                                         thresh_isl=threshisl,
                                         atrous_do=atrous_do,
                                         ini_method='curvature',
                                         thresh='hard',
                                         adaptive_rms_box=adaptive_rmsbox,
                                         adaptive_thresh=150,
                                         rms_box_bright=rmsbox_bright,
                                         rms_map=True,
                                         quiet=True,
                                         atrous_jmax=atrous_jmax)
                nisl = img.nisl
                threshpix /= 1.2
                threshisl /= 1.2
                if threshpix < 5.0:
                    break
        else:
            img = bdsm.process_image(image_name,
                                     mean_map='zero',
                                     rms_box=rmsbox,
                                     thresh_pix=threshpix,
                                     thresh_isl=threshisl,
                                     atrous_do=atrous_do,
                                     ini_method='curvature',
                                     thresh='hard',
                                     adaptive_rms_box=adaptive_rmsbox,
                                     adaptive_thresh=150,
                                     rms_box_bright=rmsbox_bright,
                                     rms_map=True,
                                     quiet=True,
                                     atrous_jmax=atrous_jmax)

        if img.nisl == 0:
            print('No islands found. Clean mask cannot be made.')
            sys.exit(1)

        # Check if there are large islands preset (indicating that multi-scale
        # clean is needed)
        has_large_isl = False
        for isl in img.islands:
            if isl.size_active > 100:
                # Assuming normal sampling, a size of 100 pixels would imply
                # a source of ~ 10 beams
                has_large_isl = True

    if (region_file is not None) and (region_file != '[]'):
        # Copy the CASA region file (stripped of brackets, etc.) and return
        os.system('cp {0} {1}'.format(region_file.strip('[]"'), mask_name))
        if not skip_source_detection:
            if threshold_format == 'float':
                return {
                    'threshold_5sig': nsig * img.clipped_rms,
                    'multiscale': has_large_isl
                }
            elif threshold_format == 'str_with_units':
                # This is done to get around the need for quotes around strings in casapy scripts
                # 'casastr/' is removed by the generic pipeline
                return {
                    'threshold_5sig':
                    'casastr/{0}Jy'.format(nsig * img.clipped_rms),
                    'multiscale':
                    has_large_isl
                }
        else:
            return {'threshold_5sig': '0.0'}
    elif not skip_source_detection:
        img.export_image(img_type='island_mask',
                         mask_dilation=0,
                         outfile=mask_name,
                         img_format=img_format,
                         clobber=True)

    if vertices_file is not None or trim_by > 0 or pad_to_size is not None or skip_source_detection:
        # Alter the mask in various ways
        if skip_source_detection:
            # Read the image
            mask_im = pim.image(image_name)
        else:
            # Read the PyBDSM mask
            mask_im = pim.image(mask_name)
        data = mask_im.getdata()
        coordsys = mask_im.coordinates()
        if reference_ra_deg is not None and reference_dec_deg is not None:
            values = coordsys.get_referencevalue()
            values[2][0] = reference_dec_deg / 180.0 * np.pi
            values[2][1] = reference_ra_deg / 180.0 * np.pi
            coordsys.set_referencevalue(values)
        imshape = mask_im.shape()
        del (mask_im)

        if pad_to_size is not None:
            imsize = pad_to_size
            coordsys['direction'].set_referencepixel([imsize / 2, imsize / 2])
            pixmin = (imsize - imshape[2]) / 2
            if pixmin < 0:
                print("The padded size must be larger than the original size.")
                sys.exit(1)
            pixmax = pixmin + imshape[2]
            data_pad = np.zeros((1, 1, imsize, imsize), dtype=np.float32)
            data_pad[0, 0, pixmin:pixmax, pixmin:pixmax] = data[0, 0]
            new_mask = pim.image('',
                                 shape=(1, 1, imsize, imsize),
                                 coordsys=coordsys)
            new_mask.putdata(data_pad)
        else:
            new_mask = pim.image('', shape=imshape, coordsys=coordsys)
            new_mask.putdata(data)

        data = new_mask.getdata()

        if skip_source_detection:
            # Mask all pixels
            data[:] = 1

        if vertices_file is not None:
            # Modify the clean mask to exclude regions outside of the polygon
            vertices = read_vertices(vertices_file)
            RAverts = vertices[0]
            Decverts = vertices[1]
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(RAverts, Decverts):
                try:
                    pixels = new_mask.topixel([
                        0, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0
                    ])
                except:
                    pixels = new_mask.topixel([
                        1, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0
                    ])
                xvert.append(pixels[2])  # x -> Dec
                yvert.append(pixels[3])  # y -> RA
            poly = Polygon(xvert, yvert)

            # Find masked regions
            masked_ind = np.where(data[0, 0])

            # Find distance to nearest poly edge and unmask those that
            # are outside the facet (dist < 0)
            dist = poly.is_inside(masked_ind[0], masked_ind[1])
            outside_ind = np.where(dist < 0.0)
            if len(outside_ind[0]) > 0:
                data[0, 0, masked_ind[0][outside_ind],
                     masked_ind[1][outside_ind]] = 0

        if trim_by > 0.0:
            sh = np.shape(data)
            margin = int(sh[2] * trim_by / 2.0)
            data[0, 0, 0:sh[2], 0:margin] = 0
            data[0, 0, 0:margin, 0:sh[3]] = 0
            data[0, 0, 0:sh[2], sh[3] - margin:sh[3]] = 0
            data[0, 0, sh[2] - margin:sh[2], 0:sh[3]] = 0

        # Save changes
        new_mask.putdata(data)
        if img_format == 'fits':
            new_mask.tofits(mask_name, overwrite=True)
        elif img_format == 'casa':
            new_mask.saveas(mask_name, overwrite=True)
        else:
            print(
                'Output image format "{}" not understood.'.format(img_format))
            sys.exit(1)

    if not skip_source_detection:
        if threshold_format == 'float':
            return {
                'threshold_5sig': nsig * img.clipped_rms,
                'multiscale': has_large_isl
            }
        elif threshold_format == 'str_with_units':
            # This is done to get around the need for quotes around strings in casapy scripts
            # 'casastr/' is removed by the generic pipeline
            return {
                'threshold_5sig':
                'casastr/{0}Jy'.format(nsig * img.clipped_rms),
                'multiscale': has_large_isl
            }
    else:
        return {'threshold_5sig': '0.0'}
Beispiel #6
0
def thiessen(directions_list, field_ra_deg, field_dec_deg, faceting_radius_deg,
    s=None, check_edges=False, target_ra=None, target_dec=None,
    target_radius_arcmin=None, beam_ratio=None):
    """
    Generates and add thiessen polygons or patches to input directions

    Parameters
    ----------
    directions_list : list of Direction objects
        List of input directions
    field_ra_deg : float
        RA in degrees of field center
    field_dec_deg : float
        Dec in degrees of field center
    faceting_radius_deg : float
        Maximum radius within which faceting will be done. Direction objects
        with postions outside this radius will get small rectangular patches
        instead of thiessen polygons
    s : LSMTool SkyModel object, optional
        Band to use to check for source near facet edges
    check_edges : bool, optional
        If True, check whether any know source falls on a facet edge. If sources
        are found that do, the facet is adjusted
    target_ra : str, optional
        RA of target source. E.g., '14h41m01.884'
    target_dec : str, optional
        Dec of target source. E.g., '+35d30m31.52'
    target_radius_arcmin : float, optional
        Radius in arcmin of target source
    beam_ratio : float, optional
        Ratio of semi-major (N-S) axis to semi-minor (E-W) axis for the primary
        beam

    """
    import lsmtool
    import shapely.geometry
    from shapely.ops import cascaded_union
    from itertools import combinations
    from astropy.coordinates import Angle

    # Select directions inside FOV (here defined as ellipse given by
    # faceting_radius_deg and the mean elevation)
    faceting_radius_pix = faceting_radius_deg / 0.066667 # radius in pixels
    field_x, field_y = radec2xy([field_ra_deg], [field_dec_deg],
        refRA=field_ra_deg, refDec=field_dec_deg)

    fx = []
    fy = []
    for th in range(0, 360, 1):
        fx.append(faceting_radius_pix * np.cos(th * np.pi / 180.0) + field_x[0])
        fy.append(faceting_radius_pix * beam_ratio * np.sin(th * np.pi / 180.0) + field_y[0])
    fov_poly_tuple = tuple([(xp, yp) for xp, yp in zip(fx, fy)])
    fov_poly = Polygon(fx, fy)

    points, _, _ = getxy(directions_list, field_ra_deg, field_dec_deg)
    for x, y, d in zip(points[0], points[1], directions_list):
        dist = fov_poly.is_inside(x, y)
        if dist < 0.0:
            # Source is outside of FOV, so use simple rectangular patches
            d.is_patch = True

    # Now do the faceting (excluding the patches)
    directions_list_thiessen = [d for d in directions_list if not d.is_patch]
    points, _, _ = getxy(directions_list_thiessen, field_ra_deg, field_dec_deg)
    points = points.T

    # Generate array of outer points used to constrain the facets
    nouter = 64
    means = np.ones((nouter, 2)) * points.mean(axis=0)
    offsets = []
    angles = [np.pi/(nouter/2.0)*i for i in range(0, nouter)]
    for ang in angles:
        offsets.append([np.cos(ang), np.sin(ang)])

    # Generate initial facets
    radius = 5.0 * faceting_radius_deg / 0.066667 # radius in pixels
    scale_offsets = radius * np.array(offsets)
    outer_box = means + scale_offsets
    points_all = np.vstack([points, outer_box])
    tri = Delaunay(points_all)
    circumcenters = np.array([_circumcenter(tri.points[t])
                              for t in tri.vertices])
    thiessen_polys = [_thiessen_poly(tri, circumcenters, n)
                      for n in range(len(points_all) - nouter)]

    # Check for vertices that are very close to each other, as this gives problems
    # to the edge adjustment below
    for thiessen_poly in thiessen_polys:
        dup_ind = 0
        for i, (v1, v2) in enumerate(zip(thiessen_poly[:-1], thiessen_poly[1:])):
            if (approx_equal(v1[0], v2[0], rel=1e-6) and
                approx_equal(v1[1], v2[1], rel=1e-6)):
                thiessen_poly.pop(dup_ind)
                dup_ind -= 1
            dup_ind += 1

    # Clip the facets at FOV
    for i, thiessen_poly in enumerate(thiessen_polys):
        polyv = np.vstack(thiessen_poly)
        poly_tuple = tuple([(xp, yp) for xp, yp in zip(polyv[:, 0], polyv[:, 1])])
        p1 = shapely.geometry.Polygon(poly_tuple)
        p2 = shapely.geometry.Polygon(fov_poly_tuple)
        if p1.intersects(p2):
            p1 = p1.intersection(p2)
            xyverts = [np.array([xp, yp]) for xp, yp in
                zip(p1.exterior.coords.xy[0].tolist(),
                p1.exterior.coords.xy[1].tolist())]
            thiessen_polys[i] = xyverts

    # Check for sources near / on facet edges and adjust regions accordingly
    if check_edges:
        log.info('Adjusting facets to avoid sources...')
        RA, Dec = s.getPatchPositions(asArray=True)
        sx, sy = radec2xy(RA, Dec, refRA=field_ra_deg, refDec=field_dec_deg)
        sizes = s.getPatchSizes(units='degree').tolist()

        if target_ra is not None and target_dec is not None and target_radius_arcmin is not None:
            log.info('Including target ({0}, {1}) in facet adjustment'.format(
                target_ra, target_dec))
            tra = Angle(target_ra).to('deg').value
            tdec = Angle(target_dec).to('deg').value
            tx, ty = radec2xy([tra], [tdec], refRA=field_ra_deg, refDec=field_dec_deg)
            sx.extend(tx)
            sy.extend(ty)
            sizes.append(target_radius_arcmin*2.0/1.2/60.0)

        # Set minimum size to 2*FWHM of resolution of high-res image
        fwhm = 2.0 * 25.0 / 3600.0 # degrees
        sizes = [max(size, fwhm) for size in sizes]

        # Filter sources to get only those close to a boundary. We need to iterate
        # until no sources are found
        niter = 0
        while niter < 3:
            niter += 1
            ind_near_edge = []
            for i, thiessen_poly in enumerate(thiessen_polys):
                polyv = np.vstack(thiessen_poly)
                poly_tuple = tuple([(x, y) for x, y in zip(polyv[:, 0], polyv[:, 1])])
                poly = Polygon(polyv[:, 0], polyv[:, 1])
                dists = poly.is_inside(sx, sy)
                for j, dist in enumerate(dists):
                    pix_radius = sizes[j] * 1.2 / 2.0 / 0.066667 # radius of source in pixels
                    if abs(dist) < pix_radius and j not in ind_near_edge:
                        ind_near_edge.append(j)
            if len(ind_near_edge) == 0:
                break
            sx_filt = np.array(sx)[ind_near_edge]
            sy_filt = np.array(sy)[ind_near_edge]
            sizes_filt = np.array(sizes)[ind_near_edge]

            # Adjust all facets for each source near a boundary
            for x, y, size in zip(sx_filt, sy_filt, sizes_filt):
                for i, thiessen_poly in enumerate(thiessen_polys):
                    polyv = np.vstack(thiessen_poly)
                    poly_tuple = tuple([(xp, yp) for xp, yp in zip(polyv[:, 0], polyv[:, 1])])
                    poly = Polygon(polyv[:, 0], polyv[:, 1])
                    dist = poly.is_inside(x, y)
                    p1 = shapely.geometry.Polygon(poly_tuple)

                    pix_radius = size * 1.2 / 2.0 / 0.066667 # size of source in pixels
                    if abs(dist) < pix_radius:
                        p2 = shapely.geometry.Point((x, y))
                        p2buf = p2.buffer(pix_radius)
                        if dist < 0.0:
                            # If point is outside, difference the polys
                            p1 = p1.difference(p2buf)
                        else:
                            # If point is inside, union the polys
                            p1 = p1.union(p2buf)
                        try:
                            xyverts = [np.array([xp, yp]) for xp, yp in
                                zip(p1.exterior.coords.xy[0].tolist(),
                                p1.exterior.coords.xy[1].tolist())]
                        except AttributeError:
                            log.error('Source avoidance has caused a facet to be '
                                'divided into multple parts. Please adjust the '
                                'parameters (e.g., if a target source is specified, '
                                'reduce its radius if possible)')
                            sys.exit(1)
                        thiessen_polys[i] = xyverts

    # Add the final facet and patch info to the directions
    patch_polys = []
    for d in directions_list:
        # Make calibrator patch
        sx, sy = radec2xy([d.ra], [d.dec], refRA=field_ra_deg, refDec=field_dec_deg)

        # Compute size of patch in pixels, with a factor of 0.8 so that
        # sources are not added along the edges (the areas outside of this 80%
        # region are masked during imaging in the make_clean_mask() call with
        # trim_by = 0.2
        patch_width = d.cal_imsize * 0.8 * d.cellsize_selfcal_deg / 0.066667
        x0 = sx[0] - patch_width / 2.0
        y0 = sy[0] - patch_width / 2.0
        selfcal_poly = [np.array([x0, y0]),
                np.array([x0, y0+patch_width]),
                np.array([x0+patch_width, y0+patch_width]),
                np.array([x0+patch_width, y0])]

        if d.is_patch:
            # For sources beyond max radius, set facet poly to calibrator poly
            # and clip to facet polys and previous patch polys
            #
            # First, make a copy of the calibrator poly so that it is not
            # altered by the clipping
            patch_poly = [np.copy(vert) for vert in selfcal_poly]

            # Now loop over the facets and patches and clip
            for facet_poly in thiessen_polys + patch_polys:
                polyv = np.vstack(facet_poly)
                poly_tuple = tuple([(xp, yp) for xp, yp in zip(polyv[:, 0], polyv[:, 1])])
                p1 = shapely.geometry.Polygon(poly_tuple)
                p2 = shapely.geometry.Polygon(patch_poly)
                if p2.intersects(p1):
                    p2 = p2.difference(p1)
                    try:
                        xyverts = [np.array([xp, yp]) for xp, yp in
                            zip(p2.exterior.coords.xy[0].tolist(),
                            p2.exterior.coords.xy[1].tolist())]
                        patch_poly = xyverts
                    except AttributeError:
                        pass

            add_facet_info(d, selfcal_poly, patch_poly, field_ra_deg, field_dec_deg)
            patch_polys.append(patch_poly)
        else:
            facet_poly = thiessen_polys[directions_list_thiessen.index(d)]
            add_facet_info(d, selfcal_poly, facet_poly, field_ra_deg, field_dec_deg)
Beispiel #7
0
def read_casa_polys(filename, image):
    """
    Reads casa region file and returns polys
    """
    with open(filename, 'r') as f:
        lines = f.readlines()

    polys = []
    for line in lines:
        if line.startswith('poly'):
            poly_str_temp = line.split('[[')[1]
            poly_str = poly_str_temp.split(']]')[0]
            poly_str_list = poly_str.split('], [')
            ra = []
            dec = []
            for pos in poly_str_list:
                RAstr, Decstr = pos.split(',')
                ra.append(Angle(RAstr, unit='hourangle').to('deg').value)
                dec.append(
                    Angle(Decstr.replace('.', ':', 2),
                          unit='deg').to('deg').value)
            poly_vertices = [np.array(ra), np.array(dec)]

            # Convert to image-plane polygon
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(np.array(ra), np.array(dec)):
                try:
                    pixels = image.topixel([
                        0, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0
                    ])
                except:
                    pixels = image.topixel([
                        1, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0
                    ])
                # remove points that are too close to each other
                if len(xvert) > 0:
                    dist = (xvert[-1] - pixels[2])**2 + (yvert[-1] -
                                                         pixels[3])**2
                    if dist < .5:
                        continue
                xvert.append(pixels[2])  # x -> Dec
                yvert.append(pixels[3])  # y -> RA
            # check if first and last points are too close
            dist = (xvert[-1] - xvert[0])**2 + (yvert[-1] - yvert[0])**2
            if dist < .5:
                xvert.pop()
                yvert.pop()
            # check if segments intersect
            newpolygon = Polygon(xvert, yvert)
            if newpolygon.check_intersections() > 0:
                raise ValueError(
                    "Found intersections in manually defined polygon! Aborting."
                )
            polys.append(newpolygon)

        elif line.startswith('ellipse'):
            ell_str_temp = line.split('[[')[1]
            if '], 0.0' not in ell_str_temp and '], 90.0' not in ell_str_temp:
                print(
                    'Only position angles of 0.0 and 90.0 are supported for CASA '
                    'regions of type "ellipse"')
                sys.exit(1)
            if '], 0.0' in ell_str_temp:
                ell_str = ell_str_temp.split('], 0.0')[0]
                pa = 0
            else:
                ell_str = ell_str_temp.split('], 90.0')[0]
                pa = 90
            ell_str_list = ell_str.split('], [')

            # Ellipse center
            RAstr, Decstr = ell_str_list[0].split(',')
            ra_center = Angle(RAstr, unit='hourangle').to('deg').value
            dec_center = Angle(Decstr.replace('.', ':', 2),
                               unit='deg').to('deg').value
            try:
                pixels = image.topixel([
                    0, 1, dec_center * np.pi / 180.0, ra_center * np.pi / 180.0
                ])
            except:
                pixels = image.topixel([
                    1, 1, dec_center * np.pi / 180.0, ra_center * np.pi / 180.0
                ])
            x_center = pixels[2]  # x -> Dec
            y_center = pixels[3]  # y -> RA

            # Ellipse semimajor and semiminor axes
            a_str, b_str = ell_str_list[1].split(',')
            a_deg = float(a_str.split('arcsec')[0]) / 3600.0
            b_deg = float(b_str.split('arcsec')[0]) / 3600.0
            try:
                pixels1 = image.topixel([
                    0, 1, (dec_center - a_deg / 2.0) * np.pi / 180.0,
                    ra_center * np.pi / 180.0
                ])
            except:
                pixels1 = image.topixel([
                    1, 1, (dec_center - a_deg / 2.0) * np.pi / 180.0,
                    ra_center * np.pi / 180.0
                ])
            a_pix1 = pixels1[2]
            try:
                pixels2 = image.topixel([
                    0, 1, (dec_center + a_deg / 2.0) * np.pi / 180.0,
                    ra_center * np.pi / 180.0
                ])
            except:
                pixels2 = image.topixel([
                    1, 1, (dec_center + a_deg / 2.0) * np.pi / 180.0,
                    ra_center * np.pi / 180.0
                ])
            a_pix2 = pixels2[2]
            a_pix = abs(a_pix2 - a_pix1)
            ex = []
            ey = []
            for th in range(0, 360, 1):
                if pa == 0:
                    # semimajor axis is along x-axis
                    ex.append(a_pix * np.cos(th * np.pi / 180.0) +
                              x_center)  # x -> Dec
                    ey.append(
                        a_pix * b_deg / a_deg * np.sin(th * np.pi / 180.0) +
                        y_center)  # y -> RA
                elif pa == 90:
                    # semimajor axis is along y-axis
                    ex.append(
                        a_pix * b_deg / a_deg * np.cos(th * np.pi / 180.0) +
                        x_center)  # x -> Dec
                    ey.append(a_pix * np.sin(th * np.pi / 180.0) +
                              y_center)  # y -> RA
            polys.append(Polygon(ex, ey))

        elif line.startswith('box'):
            poly_str_temp = line.split('[[')[1]
            poly_str = poly_str_temp.split(']]')[0]
            poly_str_list = poly_str.split('], [')
            ra = []
            dec = []
            for pos in poly_str_list:
                RAstr, Decstr = pos.split(',')
                ra.append(Angle(RAstr, unit='hourangle').to('deg').value)
                dec.append(
                    Angle(Decstr.replace('.', ':', 2),
                          unit='deg').to('deg').value)
            ra.insert(1, ra[0])
            dec.insert(1, dec[1])
            ra.append(ra[2])
            dec.append(dec[0])
            poly_vertices = [np.array(ra), np.array(dec)]

            # Convert to image-plane polygon
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(np.array(ra), np.array(dec)):
                try:
                    pixels = image.topixel([
                        0, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0
                    ])
                except:
                    pixels = image.topixel([
                        1, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0
                    ])
                xvert.append(pixels[2])  # x -> Dec
                yvert.append(pixels[3])  # y -> RA
            polys.append(Polygon(xvert, yvert))

        elif line.startswith('#'):
            pass

        else:
            print(
                'Only CASA regions of type "poly", "box", or "ellipse" are supported'
            )
            sys.exit(1)

    return polys
Beispiel #8
0
def main(input_image_file,
         vertices_file,
         output_image_file,
         blank_value='zero',
         img_format='fits',
         image_is_casa_model=False,
         nterms=1):
    """
    Blank a region in an image

    Parameters
    ----------
    input_image_file : str
        Filename of input image from which mask will be made. If the image does
        not exist, a template image with center at (reference_ra_deg,
        reference_dec_deg) will be made internally
    vertices_file : str, optional
        Filename of file with vertices (must be a pickle file containing
        a dictionary with the vertices in the 'vertices' entry)
    output_image_file : str
        Filename of output image
    blank_value : str, optional
        Value for blanks (one of 'zero' or 'nan')
    img_format : str, optional
        Format of output mask image (one of 'fits' or 'casa')
    image_is_casa_model : bool, optional
        If True, the input and output image files are treated as the root name
        of a casa model image (or images)
    nterms : int, optional
        If image_is_casa_model is True, this argument sets the number of nterms
        for the model

    """
    if type(image_is_casa_model) is str:
        if image_is_casa_model.lower() == 'true':
            image_is_casa_model = True
        else:
            image_is_casa_model = False

    if type(nterms) is str:
        nterms = int(nterms)

    if image_is_casa_model:
        if nterms == 1:
            input_image_files = [input_image_file + '.model']
            output_image_files = [output_image_file + '.model']
        if nterms == 2:
            input_image_files = [
                input_image_file + '.model.tt0',
                input_image_file + '.model.tt1'
            ]
            output_image_files = [
                output_image_file + '.model.tt0',
                output_image_file + '.model.tt1'
            ]
        if nterms == 3:
            input_image_files = [
                input_image_file + '.model.tt0',
                input_image_file + '.model.tt1',
                input_image_file + '.model.tt2'
            ]
            output_image_files = [
                output_image_file + '.model.tt0',
                output_image_file + '.model.tt1',
                output_image_file + '.model.tt2'
            ]
    else:
        input_image_files = [input_image_file]
        output_image_files = [output_image_file]

    for input_image, output_image in zip(input_image_files,
                                         output_image_files):
        im = pim.image(input_image)
        data = im.getdata()
        coordsys = im.coordinates()
        imshape = im.shape()
        new_im = pim.image('', shape=imshape, coordsys=coordsys)

        # Construct polygon
        vertices = read_vertices(vertices_file)
        RAverts = vertices[0]
        Decverts = vertices[1]
        xvert = []
        yvert = []
        for RAvert, Decvert in zip(RAverts, Decverts):
            pixels = new_im.topixel(
                [0, 1, Decvert * np.pi / 180.0, RAvert * np.pi / 180.0])
            xvert.append(pixels[2])  # x -> Dec
            yvert.append(pixels[3])  # y -> RA
        poly = Polygon(xvert, yvert)

        # Find distance to nearest poly edge and blank those that
        # are outside the facet (dist < 0)
        pix_ind = np.indices(data[0, 0].shape)
        dist = poly.is_inside(pix_ind[0], pix_ind[1])
        outside_ind = np.where(dist < 0.0)
        if len(outside_ind[0]) > 0:
            if blank_value == 'zero':
                blank_val = 0.0
            elif blank_value == 'nan':
                blank_val = np.nan
            else:
                print(
                    'Blank value type "{}" not understood.'.format(blank_with))
                sys.exit(1)
            data[0, 0, pix_ind[0][outside_ind],
                 pix_ind[1][outside_ind]] = blank_val

        # Save changes
        new_im.putdata(data)
        if img_format == 'fits':
            new_im.tofits(output_image, overwrite=True)
        elif img_format == 'casa':
            new_im.saveas(output_image, overwrite=True)
        else:
            print(
                'Output image format "{}" not understood.'.format(img_format))
            sys.exit(1)
Beispiel #9
0
def read_casa_polys(filename, image):
    """
    Reads casa region file and returns polys
    """
    with open(filename, 'r') as f:
        lines = f.readlines()

    polys = []
    for line in lines:
        if line.startswith('poly'):
            poly_str_temp = line.split('[[')[1]
            poly_str = poly_str_temp.split(']]')[0]
            poly_str_list = poly_str.split('], [')
            ra = []
            dec = []
            for pos in poly_str_list:
                RAstr, Decstr = pos.split(',')
                ra.append(Angle(RAstr, unit='hourangle').to('deg').value)
                dec.append(Angle(Decstr.replace('.', ':', 2), unit='deg').to('deg').value)
            poly_vertices = [np.array(ra), np.array(dec)]

            # Convert to image-plane polygon
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(np.array(ra), np.array(dec)):
                try:
                    pixels = image.topixel([0, 1, Decvert*np.pi/180.0,
                                               RAvert*np.pi/180.0])
                except:
                    pixels = image.topixel([1, 1, Decvert*np.pi/180.0,
                                               RAvert*np.pi/180.0])
                # remove points that are too close to each other
                if len(xvert)>0:
                    dist = (xvert[-1]-pixels[2])**2 + (yvert[-1]-pixels[3])**2
                    if dist < .5:
                        continue
                xvert.append(pixels[2]) # x -> Dec
                yvert.append(pixels[3]) # y -> RA
            # check if first and last points are too close
            dist = (xvert[-1]-xvert[0])**2 + (yvert[-1]-yvert[0])**2
            if dist < .5:
                xvert.pop()
                yvert.pop()
            # check if segments intersect
            newpolygon = Polygon(xvert, yvert)
            if newpolygon.check_intersections() > 0:
                raise ValueError("Found intersections in manually defined polygon! Aborting.")
            polys.append(newpolygon)

        elif line.startswith('ellipse'):
            ell_str_temp = line.split('[[')[1]
            if '], 0.0' not in ell_str_temp and '], 90.0' not in ell_str_temp:
                print('Only position angles of 0.0 and 90.0 are supported for CASA '
                    'regions of type "ellipse"')
                sys.exit(1)
            if '], 0.0' in ell_str_temp:
                ell_str = ell_str_temp.split('], 0.0')[0]
                pa = 0
            else:
                ell_str = ell_str_temp.split('], 90.0')[0]
                pa = 90
            ell_str_list = ell_str.split('], [')

            # Ellipse center
            RAstr, Decstr = ell_str_list[0].split(',')
            ra_center = Angle(RAstr, unit='hourangle').to('deg').value
            dec_center = Angle(Decstr.replace('.', ':', 2), unit='deg').to('deg').value
            try:
                pixels = image.topixel([0, 1, dec_center*np.pi/180.0,
                    ra_center*np.pi/180.0])
            except:
                pixels = image.topixel([1, 1, dec_center*np.pi/180.0,
                    ra_center*np.pi/180.0])
            x_center = pixels[2] # x -> Dec
            y_center = pixels[3] # y -> RA

            # Ellipse semimajor and semiminor axes
            a_str, b_str = ell_str_list[1].split(',')
            a_deg = float(a_str.split('arcsec')[0])/3600.0
            b_deg = float(b_str.split('arcsec')[0])/3600.0
            try:
                pixels1 = image.topixel([0, 1, (dec_center-a_deg/2.0)*np.pi/180.0,
                    ra_center*np.pi/180.0])
            except:
                pixels1 = image.topixel([1, 1, (dec_center-a_deg/2.0)*np.pi/180.0,
                    ra_center*np.pi/180.0])
            a_pix1 = pixels1[2]
            try:
                pixels2 = image.topixel([0, 1, (dec_center+a_deg/2.0)*np.pi/180.0,
                    ra_center*np.pi/180.0])
            except:
                pixels2 = image.topixel([1, 1, (dec_center+a_deg/2.0)*np.pi/180.0,
                    ra_center*np.pi/180.0])
            a_pix2 = pixels2[2]
            a_pix = abs(a_pix2 - a_pix1)
            ex = []
            ey = []
            for th in range(0, 360, 1):
                if pa == 0:
                    # semimajor axis is along x-axis
                    ex.append(a_pix * np.cos(th * np.pi / 180.0)
                        + x_center) # x -> Dec
                    ey.append(a_pix * b_deg / a_deg * np.sin(th * np.pi / 180.0) + y_center) # y -> RA
                elif pa == 90:
                    # semimajor axis is along y-axis
                    ex.append(a_pix * b_deg / a_deg * np.cos(th * np.pi / 180.0)
                        + x_center) # x -> Dec
                    ey.append(a_pix * np.sin(th * np.pi / 180.0) + y_center) # y -> RA
            polys.append(Polygon(ex, ey))

        elif line.startswith('box'):
            poly_str_temp = line.split('[[')[1]
            poly_str = poly_str_temp.split(']]')[0]
            poly_str_list = poly_str.split('], [')
            ra = []
            dec = []
            for pos in poly_str_list:
                RAstr, Decstr = pos.split(',')
                ra.append(Angle(RAstr, unit='hourangle').to('deg').value)
                dec.append(Angle(Decstr.replace('.', ':', 2), unit='deg').to('deg').value)
            ra.insert(1, ra[0])
            dec.insert(1, dec[1])
            ra.append(ra[2])
            dec.append(dec[0])
            poly_vertices = [np.array(ra), np.array(dec)]

            # Convert to image-plane polygon
            xvert = []
            yvert = []
            for RAvert, Decvert in zip(np.array(ra), np.array(dec)):
                try:
                    pixels = image.topixel([0, 1, Decvert*np.pi/180.0,
                                               RAvert*np.pi/180.0])
                except:
                    pixels = image.topixel([1, 1, Decvert*np.pi/180.0,
                                               RAvert*np.pi/180.0])
                xvert.append(pixels[2]) # x -> Dec
                yvert.append(pixels[3]) # y -> RA
            polys.append(Polygon(xvert, yvert))

        elif line.startswith('#'):
            pass

        else:
            print('Only CASA regions of type "poly", "box", or "ellipse" are supported')
            sys.exit(1)

    return polys
Beispiel #10
0
def main(input_image_file, vertices_file, output_image_file, blank_value='zero',
    image_is_wsclean_model=False):
    """
    Blank a region in an image

    Parameters
    ----------
    input_image_file : str
        Filename of input image to blank
    vertices_file : str, optional
        Filename of file with vertices (must be a pickle file containing
        a dictionary with the vertices in the 'vertices' entry)
    output_image_file : str
        Filename of output image
    blank_value : str, optional
        Value for blanks (one of 'zero' or 'nan')
    image_is_wsclean_model : bool, optional
        If True, the input and output image files are treated as the root name
        of a WSClean model image (or images)

    """
    if type(image_is_wsclean_model) is str:
        if image_is_wsclean_model.lower() == 'true':
            image_is_wsclean_model = True
        else:
            image_is_wsclean_model = False

    if image_is_wsclean_model:
        input_image_files = glob.glob(input_image_file+'*-model.fits')
        output_image_files = [f.replace(input_image_file, output_image_file) for f in input_image_files]
    else:
        input_image_files = [input_image_file]
        output_image_files = [output_image_file]

    if blank_value == 'zero':
        blank_val = 0.0
    elif blank_value == 'nan':
        blank_val = np.nan
    else:
        print('Blank value type "{}" not understood.'.format(blank_with))
        sys.exit(1)

    # Construct polygon of facet region
    header = pyfits.getheader(input_image_files[0], 0)
    w = wcs.WCS(header)
    RAind = w.axis_type_names.index('RA')
    Decind = w.axis_type_names.index('DEC')
    vertices = read_vertices(vertices_file)
    RAverts = vertices[0]
    Decverts = vertices[1]
    xvert = []
    yvert = []
    for RAvert, Decvert in zip(RAverts, Decverts):
        ra_dec = np.array([[0.0, 0.0, 0.0, 0.0]])
        ra_dec[0][RAind] = RAvert
        ra_dec[0][Decind] = Decvert
        xvert.append(w.wcs_world2pix(ra_dec, 0)[0][Decind])
        yvert.append(w.wcs_world2pix(ra_dec, 0)[0][RAind])
    poly = Polygon(xvert, yvert)

    for input_image, output_image in zip(input_image_files, output_image_files):
        hdu = pyfits.open(input_image, memmap=False)
        data = hdu[0].data

        # Find limits of facet poly and blank pixels outside them
        xmin = max(int(np.min(xvert)) - 2, 0)
        xmax = min(int(np.max(xvert)) + 2, data.shape[2])
        ymin = max(int(np.min(yvert)) - 2, 0)
        ymax = min(int(np.max(yvert)) + 2, data.shape[3])
        data[0, 0, :, :ymin] = blank_val
        data[0, 0, :, ymax:] = blank_val
        data[0, 0, :xmin, :] = blank_val
        data[0, 0, xmax:, :] = blank_val

        # Find distance to nearest poly edge and blank those that
        # are outside the facet (dist < 0)
        pix_ind = np.indices((xmax-xmin, ymax-ymin))
        pix_ind[0] += xmin
        pix_ind[1] += ymin
        dist = poly.is_inside(pix_ind[0], pix_ind[1])
        outside_ind = np.where(dist < 0.0)
        if len(outside_ind[0]) > 0:
            data[0, 0, pix_ind[0][outside_ind], pix_ind[1][outside_ind]] = blank_val

        hdu[0].data = data
        hdu.writeto(output_image, clobber=True)
Beispiel #11
0
def main(input_image_file,
         vertices_file,
         output_image_file,
         blank_value='zero',
         image_is_wsclean_model=False):
    """
    Blank a region in an image

    Parameters
    ----------
    input_image_file : str
        Filename of input image to blank
    vertices_file : str, optional
        Filename of file with vertices (must be a pickle file containing
        a dictionary with the vertices in the 'vertices' entry)
    output_image_file : str
        Filename of output image
    blank_value : str, optional
        Value for blanks (one of 'zero' or 'nan')
    image_is_wsclean_model : bool, optional
        If True, the input and output image files are treated as the root name
        of a WSClean model image (or images)

    """
    if type(image_is_wsclean_model) is str:
        if image_is_wsclean_model.lower() == 'true':
            image_is_wsclean_model = True
        else:
            image_is_wsclean_model = False

    if image_is_wsclean_model:
        input_image_files = glob.glob(input_image_file + '*-model.fits')
        output_image_files = [
            f.replace(input_image_file, output_image_file)
            for f in input_image_files
        ]
    else:
        input_image_files = [input_image_file]
        output_image_files = [output_image_file]

    if blank_value == 'zero':
        blank_val = 0.0
    elif blank_value == 'nan':
        blank_val = np.nan
    else:
        print('Blank value type "{}" not understood.'.format(blank_with))
        sys.exit(1)

    # Construct polygon of facet region
    header = pyfits.getheader(input_image_files[0], 0)
    w = wcs.WCS(header)
    RAind = w.axis_type_names.index('RA')
    Decind = w.axis_type_names.index('DEC')
    vertices = read_vertices(vertices_file)
    RAverts = vertices[0]
    Decverts = vertices[1]
    xvert = []
    yvert = []
    for RAvert, Decvert in zip(RAverts, Decverts):
        ra_dec = np.array([[0.0, 0.0, 0.0, 0.0]])
        ra_dec[0][RAind] = RAvert
        ra_dec[0][Decind] = Decvert
        xvert.append(w.wcs_world2pix(ra_dec, 0)[0][Decind])
        yvert.append(w.wcs_world2pix(ra_dec, 0)[0][RAind])
    poly = Polygon(xvert, yvert)

    for input_image, output_image in zip(input_image_files,
                                         output_image_files):
        hdu = pyfits.open(input_image, memmap=False)
        data = hdu[0].data

        # Find limits of facet poly and blank pixels outside them
        xmin = max(int(np.min(xvert)) - 2, 0)
        xmax = min(int(np.max(xvert)) + 2, data.shape[2])
        ymin = max(int(np.min(yvert)) - 2, 0)
        ymax = min(int(np.max(yvert)) + 2, data.shape[3])
        data[0, 0, :, :ymin] = blank_val
        data[0, 0, :, ymax:] = blank_val
        data[0, 0, :xmin, :] = blank_val
        data[0, 0, xmax:, :] = blank_val

        # Find distance to nearest poly edge and blank those that
        # are outside the facet (dist < 0)
        pix_ind = np.indices((xmax - xmin, ymax - ymin))
        pix_ind[0] += xmin
        pix_ind[1] += ymin
        dist = poly.is_inside(pix_ind[0], pix_ind[1])
        outside_ind = np.where(dist < 0.0)
        if len(outside_ind[0]) > 0:
            data[0, 0, pix_ind[0][outside_ind],
                 pix_ind[1][outside_ind]] = blank_val

        hdu[0].data = data
        hdu.writeto(output_image, clobber=True)