def getDECamCorners(header): """ Get the DECam profile corners for a given header and WCS """ wcs = wcsutil.WCS(header) nx = header['NAXIS1'] ny = header['NAXIS2'] ra0, dec0 = wcs.image2sky(nx / 2.0, ny / 2.0) DECam_header = createDECam_TANheader(ra0, dec0) DECam_wcs = wcsutil.WCS(DECam_header) r, d = DECam_wcs.image2sky(DECAM_CORNERS_X, DECAM_CORNERS_Y) x, y = wcs.sky2image(r, d) return x, y
def drawDECamCorners_Sky(ra, dec, pixscale=0.27, label=False, units='deg', **kwargs): """ Draws DECam Corners on the sky (RA,DEC) using matplotlib Plot function on the current plot """ header = createDECam_TANheader(ra, dec, pixscale=pixscale) wcs = wcsutil.WCS(header) r, d = wcs.image2sky(DECAM_CORNERS_X, DECAM_CORNERS_Y) # Into RA,DEC ax = plt.gca() if units == 'rad': r, d = deg2rad(r, d) ax.plot(r, d, **kwargs) if label: # Probably will never use if units == 'rad': r0, d0 = deg2rad(numpy.array([ra]), numpy.array([dec])) else: r0, d0 = ra, dec ax.text(r0, d0, label, ha='center', va='center') return
def drawDECamCCDs_Sky(ra, dec, trim=True, pixscale=0.27, label=False, **kwargs): """ Draws DECam CCDs on the sky (RA,DEC) using matplotlib Plot function on the current plot """ header = createDECam_TANheader(ra, dec, pixscale=pixscale) wcs = wcsutil.WCS(header) ax = plt.gca() if trim: SECTIONS = TRIM_CCDSECTIONS else: SECTIONS = CCDSECTIONS for k, v in list(SECTIONS.items()): (x1, x2, y1, y2) = v x = numpy.array([x1, x2, x2, x1, x1]) y = numpy.array([y1, y1, y2, y2, y1]) r, d = wcs.image2sky(x, y) # Into RA,DEC ax.plot(r, d, **kwargs) if label: # Probably will never use r1, d1 = wcs.image2sky(x1, y1) r2, d2 = wcs.image2sky(x2, y2) ax.text(0.5 * (r2 + r1), 0.5 * (d2 + d1), "CCD%s" % k, ha='center', va='center') return
def produce(self, RAS, DecS): outpath = os.path.normpath(self.outfile) outdir = os.path.dirname(self.outfile) if not os.path.exists(outdir): os.makedirs(outdir) if os.path.exists(outpath): os.remove(outpath) self.RA = string.atof(RAS) self.Dec = string.atof(DecS) if not os.path.exists(self.infile): self.posX = 0. self.posY = 0. return fitsfile = os.path.normpath(self.infile) fits = fitsio.FITS(fitsfile) if fitsfile.find('.fz') > 0: prihdr = fits[1].read_header() self.ncol = prihdr['ZNAXIS1'] self.nrow = prihdr['ZNAXIS2'] data1 = fits[self.ext + 1].read() else: prihdr = fits[0].read_header() self.ncol = prihdr['NAXIS1'] self.nrow = prihdr['NAXIS2'] data1 = fits[self.ext].read() w = wcsutil.WCS(prihdr) objx, objy = w.sky2image(self.RA, self.Dec) self.posX = objx self.posY = objy data = self.makeStamp(data1, objx, objy) self.writeStamp(data, prihdr)
def getDECamCCDs(header, plot=False, trim=True, **kwargs): """ Get the x,y positions of the DECam CCDs for a given header with WCS and plot them (optional) """ wcs = wcsutil.WCS(header) nx = header['NAXIS1'] ny = header['NAXIS2'] ra0, dec0 = wcs.image2sky(nx / 2.0, ny / 2.0) DECam_header = createDECam_TANheader(ra0, dec0) DECam_wcs = wcsutil.WCS(DECam_header) if plot: ax = plt.gca() if trim: SECTIONS = TRIM_CCDSECTIONS else: SECTIONS = CCDSECTIONS ccds = {} for k, v in list(SECTIONS.items()): (x1, x2, y1, y2) = v if plot: DECam_x = [x1, x2, x2, x1, x1] DECam_y = [y1, y1, y2, y2, y1] r_plot, d_plot = DECam_wcs.image2sky(DECam_x, DECam_y) # Into RA,DEC x_plot, y_plot = wcs.sky2image(r_plot, d_plot) ax.plot(x_plot, y_plot, **kwargs) r, d = DECam_wcs.image2sky([x1, x2], [y1, y2]) x, y = wcs.sky2image(r, d) # Make the the interger (pixels) x = numpy.ceil(x).astype(int) y = numpy.ceil(y).astype(int) # Sort them to get the slices right x.sort() y.sort() ccds[k] = x.tolist(), y.tolist() # We get back lists return ccds
def DESDM_corners(hdr, border=0): """ set the DESDM corners DESDM CCD Image Corner Coordinates definitions for DECam see: https://desdb.cosmology.illinois.edu/confluence/display/PUB/CCD+Image+Corners+Coordinates x-axis (RA4,DEC4) (RA3,DEC3) Corner 4 +-----------------+ Corner 3 (1,NAXIS2) | | (NAXIS1,NAXIS2) | | | | | | | | | | | | | | | | y-axis | | | | | | | | | | | | (RA1,DEC1)| | (RA2,DEC2) Corner 1 +-----------------+ Corner 2 (1,1) (NAXIS1,1) For fpacked images, NAXIS1/NAXIS2 do not represent the true dimensions of the image, so we need to use ZNAXIS1/ZNAXIS2 instead when they are present. """ if hdr.get('znaxis1') and hdr.get('znaxis2'): nx = hdr['znaxis1'] ny = hdr['znaxis2'] else: nx = hdr['naxis1'] ny = hdr['naxis2'] wcs = wcsutil.WCS(hdr) rac1, decc1 = wcs.image2sky(1 + border, 1 + border) rac2, decc2 = wcs.image2sky(nx - border, 1 + border) rac3, decc3 = wcs.image2sky(nx - border, ny - border) rac4, decc4 = wcs.image2sky(1 + border, ny - border) ra0, dec0 = wcs.image2sky(nx / 2.0, ny / 2.0) return ra0, dec0, rac1, decc1, rac2, decc2, rac3, decc3, rac4, decc4
def update_wcs_matrix(header, x0, y0, naxis1, naxis2): """ Update the wcs header object with the right CRPIX[1,2] CRVAL[1,2] for a given subsection """ # We need to make a deep copy/otherwise if fails h = copy.deepcopy(header) # Get the wcs object wcs = wcsutil.WCS(h) # Recompute CRVAL1/2 on the new center x0,y0 CRVAL1, CRVAL2 = wcs.image2sky(x0, y0) # Asign CRPIX1/2 on the new image CRPIX1 = int(naxis1 / 2.0) CRPIX2 = int(naxis2 / 2.0) # Update the values h['CRVAL1'] = CRVAL1 h['CRVAL2'] = CRVAL2 h['CRPIX1'] = CRPIX1 h['CRPIX2'] = CRPIX2 return h
def worker(image, return_dict, procnum): #Read image header and get coordinates of the image print(i, 'working with image', image) hdr = pf.open(image) w = astrowcs.WCS(hdr[1].header) corners_image = w.calc_footprint(center=False) corners_pixels = w.calc_footprint(center=True) nx = hdr[1].header['naxis1'] ny = hdr[1].header['naxis2'] coords1 = corners_image[0] coords2 = corners_image[1] coords3 = corners_image[2] coords4 = corners_image[3] #Read image data image_data = pf.getdata(image) #Define healsparse polygon within the image ra = [coords1[0], coords4[0], coords3[0], coords2[0]] dec = [coords1[1], coords4[1], coords3[1], coords2[1]] raC = (np.max(ra) + np.min(ra)) / 2. decC = (np.max(dec) + np.min(dec)) / 2. nside = 2**17 poly = hs.Polygon(ra=ra, dec=dec, value=1) smap = poly.get_map(nside=nside, dtype=np.int16) a = smap.validPixels b = hp.nest2ring(nside, a) #a are pixels in NEST, b in RING #Get center coordinates of each pixel raF, decF = hp.pix2ang(nside, a, lonlat=True, nest=True) #Get nx, ny in image from healsparse pixels myhdr = hdr[1].header wcs = wc.WCS(myhdr) xn, yn = wcs.sky2image(raF, decF) #Get associated weight values values = [] for x, y in zip(xn, yn): values.append(image_data[int(y - 1), int(x - 1)]) values = np.array(values) #Define healsparse map hsp_map_2 = hs.HealSparseMap.makeEmpty(512, nside, dtype=np.int16) hsp_map_2.updateValues(a, values) #Degrade to nside=4096 low_res_hsp = hsp_map_2.degrade(4096) j = low_res_hsp.validPixels test_values_2 = low_res_hsp.getValuePixel(j) #Uncomment if you want in RING format #k=hp.nest2ring(4096,j) #hp_aux = np.zeros(hp.nside2npix(4096))+hp.UNSEEN #hp_aux[j] = test_values_2 hdr.close() return_dict[procnum] = np.array([j, test_values_2]).transpose()
def __call__(cls, streak_list, image_list, streak_name_in, streak_name_out, image_name_in, image_name_out, add_width, max_extrapolate, plotfile=None): """ Read input list of streak detections and predict where a streak crossed a CCD but was missed. Then create new copies of images, altering masks to set STREAK bit in new streaks. :Parameters: - `streak_list`: list of input streak file names - `image_list`: list of names of image files to be updated - `streak_name_in`: string to replace in input streak filenames - `streak_name_out`: replacement string for output streak filenames - `image_name_in`: string to replace in input image filenames - `image_name_out`: replacement string for output image filenames - `add_width`: number of pixels to grow (or shrink) streak width - `max_extrapolate`: farthest to start a new streak from endpoint of an existing one (degrees) - `plotfile`: if given, a diagram of streaks is drawn into this file """ logger.info('Reading {:d} streak files'.format(len(streak_list))) # Read in all the streak RA/Dec, into a dictionary keyed by CCDNUM, # which should be in the primary header. Also save a dictionary of # the file names for these streak_corners = {} streak_names = {} for streakfile in streak_list: logger.info(f"Reading streak file {streakfile}") with fitsio.FITS(streakfile, 'r') as fits: ccdnum = fits[0].read_header()['CCDNUM'] streak_names[ccdnum] = streakfile tab = fits[1].read() if len(tab) > 0: streak_corners[ccdnum] = fits[1].read()['CORNERS_WCS'] logger.info('Reading WCS from {:d} CCDs'.format(len(image_list))) # Read in the WCS for each CCD for which we have an image, # also put into dicts keyed by CCDNUM # Will get these directly from FITS instead of using DESImage in order # to save reading all of the data. wcs = {} crval1 = [] crval2 = [] for imgfile in image_list: try: hdr = fitsio.read_header(imgfile, 0) ccd = hdr['CCDNUM'] crval1.append(hdr['CRVAL1']) crval2.append(hdr['CRVAL2']) # Due to a bug in fitsio 1.0.0rc1+0, we need to clean up the # header before feeding it to wcsutil and remove the 'None' and other problematic items for k in hdr: # Try to access the item, if failed we have to remove it if not k: hdr.delete(k) continue try: _ = hdr[k] except: logger.info( "Removing keyword: {:s} from header".format(k)) hdr.delete(k) wcs[ccd] = wcsutil.WCS(hdr) except Exception as e: print(e) ### logger.error('Failure reading WCS from {:s}'.format(imgfile)) return 1 # Determine a center for local gnomonic projection ra0 = np.median(crval1) dec0 = np.median(crval2) # Calculate upper and lower bounds of each CCD in the local # gnomonic system. ccd_x1 = np.zeros(63, dtype=float) ccd_x2 = np.zeros(63, dtype=float) ccd_y1 = np.zeros(63, dtype=float) ccd_y2 = np.zeros(63, dtype=float) ccd_xmin = 1. ccd_xmax = 2048. ccd_ymin = 1. ccd_ymax = 4096. ccd_corners_xpix = np.array([ccd_xmin, ccd_xmin, ccd_xmax, ccd_xmax]) ccd_corners_ypix = np.array([ccd_ymin, ccd_ymax, ccd_ymax, ccd_ymin]) for ccd, w in wcs.items(): ra, dec = w.image2sky(ccd_corners_xpix, ccd_corners_ypix) x_corners, y_corners = gnomonic(ra, dec, ra0, dec0) ccd_x1[ccd] = np.min(x_corners) ccd_y1[ccd] = np.min(y_corners) ccd_x2[ccd] = np.max(x_corners) ccd_y2[ccd] = np.max(y_corners) # Now collect information on all of the streak segments that we have ccdnum = [] ra_corner = [] dec_corner = [] for ccd, streaks in streak_corners.items(): if ccd not in wcs: # Skip segments on CCDs that have no WCS logger.warning( 'No WCS found for streaks on CCD {:d}'.format(ccd)) continue n1, _, _ = streaks.shape for i in range(n1): ccdnum.append(ccd) ra_corner.append(streaks[i, :, 0]) dec_corner.append(streaks[i, :, 1]) # Put streak corners into gnomonic system for this exposure x1, y1 = gnomonic(np.array([r[0] for r in ra_corner], dtype=float), np.array([d[0] for d in dec_corner], dtype=float), ra0, dec0) x2, y2 = gnomonic(np.array([r[1] for r in ra_corner], dtype=float), np.array([d[1] for d in dec_corner], dtype=float), ra0, dec0) x3, y3 = gnomonic(np.array([r[2] for r in ra_corner], dtype=float), np.array([d[2] for d in dec_corner], dtype=float), ra0, dec0) x4, y4 = gnomonic(np.array([r[3] for r in ra_corner], dtype=float), np.array([d[3] for d in dec_corner], dtype=float), ra0, dec0) ccdnum = np.array(ccdnum, dtype=int) # Describe each segmet by two endpoints at the midpoints of short sides # Will need to decide which is the short side d12 = np.hypot(x2 - x1, y2 - y1) d23 = np.hypot(x3 - x2, y3 - y2) xleft = np.where(d12 < d23, 0.5 * (x1 + x2), 0.5 * (x2 + x3)) yleft = np.where(d12 < d23, 0.5 * (y1 + y2), 0.5 * (y2 + y3)) xright = np.where(d12 < d23, 0.5 * (x3 + x4), 0.5 * (x4 + x1)) yright = np.where(d12 < d23, 0.5 * (y3 + y4), 0.5 * (y4 + y1)) dx = xright - xleft dy = yright - yleft # Calculate a width as 2x the # largest perp distance from a vertex to this line w1 = np.abs(dx * (y1 - yleft) - dy * (x1 - xleft)) / np.hypot(dx, dy) w2 = np.abs(dx * (y2 - yleft) - dy * (x2 - xleft)) / np.hypot(dx, dy) w3 = np.abs(dx * (y3 - yleft) - dy * (x3 - xleft)) / np.hypot(dx, dy) w4 = np.abs(dx * (y4 - yleft) - dy * (x4 - xleft)) / np.hypot(dx, dy) wmax = np.maximum(w1, w2) wmax = np.maximum(wmax, w3) wmax = np.maximum(wmax, w4) wmax = 2 * wmax # Rearrange so that xleft <= xright swapit = xright < xleft tmp = np.where(swapit, xleft, xright) xleft = np.where(swapit, xright, xleft) xright = np.array(tmp) tmp = np.where(swapit, yleft, yright) yleft = np.where(swapit, yright, yleft) yright = np.array(tmp) # Get the crossing points of the lines into CCDs xc1, xc2, yc1, yc2 = boxCross(xleft, yleft, dx, dy, ccd_x1[ccdnum], ccd_x2[ccdnum], ccd_y1[ccdnum], ccd_y2[ccdnum]) # Get rid of segments that appear to miss their host CCDs miss = xc2 < xc1 # Take 1st crossing point instead of left point if it has higher x, or vertical # with higher y, i.e. truncate the track segment at the edge of the CCD. replace = np.where(dx == 0, yc1 > yleft, xc1 > xleft) xc1 = np.where(replace, xc1, xleft) yc1 = np.where(replace, yc1, yleft) # Likewise truncate segment at right-hand crossing replace = np.where(dx == 0, yc2 < yright, xc2 < xright) xc2 = np.where(replace, xc2, xright) yc2 = np.where(replace, yc2, yright) # Backfill the non-intersections again - note that above # maneuvers will leave xc2<xc1 for streaks that miss their CCDs, # unless vertical ??? xc1[miss] = 0. xc2[miss] = -1. # Get a final verdict on hit or miss miss = np.where(dx == 0, yc2 < yc1, xc2 < xc1) # Save information on all valid streaks xc1 = xc1[~miss] xc2 = xc2[~miss] yc1 = yc1[~miss] yc2 = yc2[~miss] wmax = wmax[~miss] ccdnum = ccdnum[~miss] # Express segments as slopes and midpoints dx = xc2 - xc1 dy = yc2 - yc1 mx = dx / np.hypot(dx, dy) my = dy / np.hypot(dx, dy) # Mark segments that are probably spurious edge detections EDGE_SLOPE = 2. # Degrees from horizontal for edge streaks EDGE_DISTANCE = 0.005 # Max degrees from streak center to CCD edge for spurious streaks horizontal = np.abs(my) < np.sin(EDGE_SLOPE * np.pi / 180.) ymid = 0.5 * (yc1 + yc2) nearedge = np.logical_or(ccd_y2[ccdnum] - ymid < EDGE_DISTANCE, ymid - ccd_y1[ccdnum] < EDGE_DISTANCE) nearedge = np.logical_and(nearedge, horizontal) # Check short edges too vertical = np.abs(mx) < np.sin(EDGE_SLOPE * np.pi / 180.) xmid = 0.5 * (xc1 + xc2) tmp = np.logical_or(ccd_x2[ccdnum] - xmid < EDGE_DISTANCE, xmid - ccd_x1[ccdnum] < EDGE_DISTANCE) nearedge = np.logical_or(nearedge, np.logical_and(tmp, vertical)) # Decide which segments are "friends" of each other. # To be a friend, the center of each must be close # to the extension of the line of the other. # Accumulate a list of tracks, each track is a list of # individual streaks that are friends of friends tracks = [] for i in range(len(xc1)): if nearedge[i]: continue # Do not use edge tracks itstrack = [i] # start new track with just this for t in tracks: # Search other tracks for friends for j in t: if friends(xc1, xc2, yc1, yc2, mx, my, ccdnum, i, j): itstrack += t # Merge track tracks.remove(t) # Get rid of old one break # No need to check others tracks.append(itstrack) # Now iterate through tracks, seeing if they have missing segments # Create arrays to hold information on new tracks new_ccdnum = [] new_xc1 = [] new_xc2 = [] new_yc1 = [] new_yc2 = [] new_ra1 = [] new_ra2 = [] new_dec1 = [] new_dec2 = [] new_width = [] new_extrapolated = [] new_nearest = [] for t in tracks: if len(t) < 2: continue # Do not extrapolate singlet tracks ids = np.array( t) # Make an array of indices of segments in this track # Fit a quadratic path to the streak endpoints xx = np.concatenate((xc1[ids], xc2[ids])) yy = np.concatenate((yc1[ids], yc2[ids])) # If the track slope is mostly along x, then we'll have the independent # variable xx be x and dependent yy will be y. But if track # is more vertical, then we'll look at functions x(y) instead. xOrder = np.median(np.abs(mx[ids])) > np.median(np.abs(my[ids])) if not xOrder: xx, yy = yy, xx # Record limits of detected tracks' independent variable xxmin = np.min(xx) xxmax = np.max(xx) # Fit a quadratic to the points, or # linear if only one streak # Allow up to nclip points to clip RESID_TOLERANCE = 6. / 3600. # Clip >6" deviants nclip = 2 for i in range(nclip + 1): if len(xx) > 2: A = np.vstack((np.ones_like(xx), xx, xx * xx)) else: A = np.vstack((np.ones_like(xx), xx)) coeffs = np.linalg.lstsq(A.T, yy, rcond=None)[0] resid = yy - np.dot(A.T, coeffs) j = np.argmax(np.abs(resid)) if i == nclip or np.abs(resid[j]) < RESID_TOLERANCE: break xx = np.delete(xx, j) yy = np.delete(yy, j) # Calculate the y(x1),y(x2) where tracks # cross the left/right of every CCD, then # find the ones that will cross CCD's y. # These are CCD bounds, with xx being the quadratic's argument if xOrder: xx1 = ccd_x1 xx2 = ccd_x2 yy1 = ccd_y1 yy2 = ccd_y2 else: xx1 = ccd_y1 xx2 = ccd_y2 yy1 = ccd_x1 yy2 = ccd_x2 if len(coeffs) == 2: A2 = np.vstack((np.ones_like(xx2), xx2)).T A1 = np.vstack((np.ones_like(xx1), xx1)).T else: A2 = np.vstack((np.ones_like(xx2), xx2, xx2 * xx2)).T A1 = np.vstack((np.ones_like(xx1), xx1, xx1 * xx1)).T # yyc[12] are the dependent coordinate at crossings of xx[12] bounds yyc1 = np.dot(A1, coeffs) yyc2 = np.dot(A2, coeffs) # Now we ask whether the y value of streak at either edge crossing # is in the y range of a CCD missed = np.logical_or( np.maximum(yyc1, yyc2) < yy1, np.minimum(yyc1, yyc2) > yy2) # Also skip any CCD where we already have a streak for iccd in ccdnum[ids]: missed[iccd] = True missed[0] = True # There is no CCD0 missed[61] = True # Never use this one either, it's always dead # Now find intersection of new streaks with edges of their CCDs # Define a function for the streak path that we'll use for solving def poly(x, coeffs, ysolve): y = coeffs[0] + x * coeffs[1] if len(coeffs) > 2: y += coeffs[2] * x * x return y - ysolve EDGE_TOLERANCE = 0.2 / 3600. # Find x/y of edge to this accuracy (0.2 arcsec) for iccd in np.where(~missed)[0]: # This is a loop over every CCD that the track crosses but has no detected segment # Determine an (xx,yy) pair for its entry and exit from the CCD new_yy1 = yyc1[iccd] new_yy2 = yyc2[iccd] new_xx1 = xx1[iccd] new_xx2 = xx2[iccd] # left side: if new_yy1 < yy1[iccd]: new_xx1 = newton(poly, new_xx1, args=(coeffs, yy1[iccd]), tol=EDGE_TOLERANCE) elif new_yy1 > yy2[iccd]: new_xx1 = newton(poly, new_xx1, args=(coeffs, yy2[iccd]), tol=EDGE_TOLERANCE) new_yy1 = poly(new_xx1, coeffs, 0.) # right side if new_yy2 < yy1[iccd]: new_xx2 = newton(poly, new_xx2, args=(coeffs, yy1[iccd]), tol=EDGE_TOLERANCE) elif new_yy2 > yy2[iccd]: new_xx2 = newton(poly, new_xx2, args=(coeffs, yy2[iccd]), tol=EDGE_TOLERANCE) new_yy2 = poly(new_xx2, coeffs, 0.) # Does the solution lie outside the input streaks? extrapolated = new_xx1 < xxmin or new_xx2 > xxmax width = np.median(wmax[ids]) # Calculate distance to nearest unclipped streak member nearest = min(np.min(np.hypot(xx - new_xx1, yy - new_yy1)), np.min(np.hypot(xx - new_xx2, yy - new_yy2))) if not xOrder: # swap xx,yy back if we had y as the independent variable new_xx1, new_yy1 = new_yy1, new_xx1 new_xx2, new_yy2 = new_yy2, new_xx2 # Project the coordinates back to RA, Dec ra1, dec1 = gnomonicInverse(new_xx1, new_yy1, ra0, dec0) ra2, dec2 = gnomonicInverse(new_xx2, new_yy2, ra0, dec0) # Append this streak to list of new ones new_ccdnum.append(iccd) new_xc1.append(new_xx1) new_xc2.append(new_xx2) new_yc1.append(new_yy1) new_yc2.append(new_yy2) new_ra1.append(ra1) new_ra2.append(ra2) new_dec1.append(dec1) new_dec2.append(dec2) new_width.append(width) new_extrapolated.append(extrapolated) new_nearest.append(nearest) # Make all lists into arrays new_ccdnum = np.array(new_ccdnum, dtype=int) new_xc1 = np.array(new_xc1, dtype=float) new_xc2 = np.array(new_xc2, dtype=float) new_yc1 = np.array(new_yc1, dtype=float) new_yc2 = np.array(new_yc2, dtype=float) new_ra1 = np.array(new_ra1, dtype=float) new_ra2 = np.array(new_ra2, dtype=float) new_dec1 = np.array(new_dec1, dtype=float) new_dec2 = np.array(new_dec2, dtype=float) new_width = np.array(new_width, dtype=float) new_extrapolated = np.array(new_extrapolated, dtype=bool) new_nearest = np.array(new_nearest, dtype=float) # Decide which new segments will be masked maskit = np.logical_or(~new_extrapolated, new_nearest <= max_extrapolate) logger.info('Identified {:d} missing streak segments for masking'.format(\ np.count_nonzero(maskit))) # Make the diagnostic plot if desired if plotfile is not None: pl.figure(figsize=(6, 6)) pl.xlim(-1.1, 1.1) pl.ylim(-1.1, 1.1) pl.gca().set_aspect('equal') # Draw CCD outlines and numbers for ccd, w in wcs.items(): ra, dec = w.image2sky(ccd_corners_xpix, ccd_corners_ypix) x_corners, y_corners = gnomonic(ra, dec, ra0, dec0) x = x_corners.tolist() y = y_corners.tolist() x.append(x[0]) y.append(y[0]) pl.plot(x, y, 'k-', label=None) x = np.mean(x_corners) y = np.mean(y_corners) pl.text(x, y, str(ccd), horizontalalignment='center', verticalalignment='center', fontsize=14) # Draw input streaks marked as edge labelled = False for i in np.where(nearedge)[0]: x = (xc1[i], xc2[i]) y = (yc1[i], yc2[i]) if not labelled: pl.plot(x, y, 'm-', lw=2, label='edge') labelled = True else: pl.plot(x, y, 'm-', lw=2, label=None) # Draw linked tracks s = set() for t in tracks: if len(t) > 1: s = s.union(set(t)) labelled = False for i in s: x = (xc1[i], xc2[i]) y = (yc1[i], yc2[i]) if not labelled: pl.plot(x, y, 'b-', lw=2, label='connected') labelled = True else: pl.plot(x, y, 'b-', lw=2, label=None) # Draw singleton tracks as those that are neither edge nor connected s = s.union(set(np.where(nearedge)[0])) single = set(range(len(xc1))) single = single.difference(s) labelled = False for i in single: x = (xc1[i], xc2[i]) y = (yc1[i], yc2[i]) if not labelled: pl.plot(x, y, 'c-', lw=2, label='unconnected') labelled = True else: pl.plot(x, y, 'c-', lw=2, label=None) # Draw missed tracks that will be masked labelled = False for i in np.where(maskit)[0]: x = (new_xc1[i], new_xc2[i]) y = (new_yc1[i], new_yc2[i]) if not labelled: pl.plot(x, y, 'r-', lw=2, label='new masked') labelled = True else: pl.plot(x, y, 'r-', lw=2, label=None) # Draw missed tracks that will not be masked labelled = False for i in np.where(~maskit)[0]: x = (new_xc1[i], new_xc2[i]) y = (new_yc1[i], new_yc2[i]) if not labelled: pl.plot(x, y, 'r:', lw=2, label='new skipped') labelled = True else: pl.plot(x, y, 'r:', lw=2, label=None) # legend pl.legend(framealpha=0.3, fontsize='small') pl.savefig(plotfile) # Now accumulate pixel coordinates of corners of all new streaks to mask added_streak_ccds = [] added_streak_corners = [] for id, ccd in enumerate(new_ccdnum): ccd = new_ccdnum[id] if not maskit[id]: continue # Only proceed with the ones to be masked # Get a pixel scale from the WCS, in arcsec/pix xmid = np.mean(ccd_corners_xpix) ymid = np.mean(ccd_corners_ypix) ra, dec = wcs[ccd].image2sky(xmid, ymid) ra2, dec2 = wcs[ccd].image2sky(xmid + 1, ymid) pixscale = np.hypot( np.cos(dec * np.pi / 180.) * (ra - ra2), dec - dec2) # width of streak, in pixels w = new_width[id] / pixscale + add_width if w <= 0.: continue # Don't mask streaks of zero width # Make RA/Dec of track endpoints x = np.array([new_xc1[id], new_xc2[id]]) y = np.array([new_yc1[id], new_yc2[id]]) ra, dec = gnomonicInverse(x, y, ra0, dec0) # Convert to pixel coordinates x, y = wcs[ccd].sky2image(ra, dec) line = Line(x[0], y[0], x[1], y[1]) # Create bounding rectangle of track corners_pix = boxTrack(line, w, ccd_xmin, ccd_xmax, ccd_ymin, ccd_ymax) added_streak_ccds.append(ccd) added_streak_corners.append(np.array(corners_pix)) added_streak_ccds = np.array(added_streak_ccds) # Make new copies of streak files, adding new ones logger.debug('Rewriting streak files') for ccd, streakfile_in in streak_names.items(): nmatch = len(re.findall(streak_name_in, streakfile_in)) if nmatch != 1: logger.error('Could not update streak file named <' + streakfile_in + '>') return 1 streakfile_out = re.sub(streak_name_in, streak_name_out, streakfile_in) # Use file system to make fresh copy of table's FITS file shutil.copy2(streakfile_in, streakfile_out) # Find new streaks for this ccd add_ids = np.where(added_streak_ccds == ccd)[0] if len(add_ids) > 0: # Open the table and add new streaks' info try: fits = fitsio.FITS(streakfile_out, 'rw') addit = np.recarray(len(add_ids), dtype=[('LABEL', '>i4'), ('CORNERS', '>f4', (4, 2)), ('CORNERS_WCS', '>f8', (4, 2))]) if fits[1]['LABEL'][:]: first_label = np.max(fits[1]['LABEL'][:]) + 1 else: first_label = 1 addit.LABEL = np.arange(first_label, first_label + len(addit)) for i, id in enumerate(add_ids): corners_pix = added_streak_corners[id] addit.CORNERS[i] = corners_pix ra, dec = wcs[ccd].image2sky(corners_pix[:, 0], corners_pix[:, 1]) addit.CORNERS_WCS[i] = np.vstack((ra, dec)).T fits[1].append(addit) fits.close() except Exception as e: print(e) logger.error('Failure updating streak file <{:s}>'.format( streakfile_out)) return 1 logger.debug('Remasking images') for imgfile_in in image_list: # Make the name needed for output nmatch = len(re.findall(image_name_in, imgfile_in)) if nmatch != 1: logger.error( 'Could not create output name for image file named <' + imgfile_in + '>') return 1 imgfile_out = re.sub(image_name_in, image_name_out, imgfile_in) logger.info(f"Loading image: {imgfile_in}") sci = DESImage.load(imgfile_in) ccd = sci.header['CCDNUM'] # Find added streaks for this ccd add_ids = np.where(added_streak_ccds == ccd)[0] if len(add_ids) > 0: shape = sci.mask.shape yy, xx = np.indices(shape) points = np.vstack((xx.flatten(), yy.flatten())).T inside = None for id in add_ids: # From Alex's immask routine: mark interior pixels # for each added streak v = added_streak_corners[id] vertices = [(v[0, 0], v[0, 1]), (v[1, 0], v[1, 1]), (v[2, 0], v[2, 1]), (v[3, 0], v[3, 1]), (v[0, 0], v[0, 1])] path = matplotlib.path.Path(vertices) if inside is None: inside = path.contains_points(points) else: inside = np.logical_or(inside, path.contains_points(points)) # Make the list of masked pixels if inside is None: ymask, xmask = np.array(0, dtype=int), np.array(0, dtype=int) else: ymask, xmask = np.nonzero(inside.reshape(shape)) sci.mask[ymask, xmask] |= parse_badpix_mask('STREAK') # Write something into the image header sci['DESCNCTS'] = time.asctime(time.localtime()) + \ ' Mask {:d} new streaks'.format(len(add_ids)) # sci['HISTORY'] = time.asctime(time.localtime()) + \ # ' Mask {:d} new streaks'.format(len(add_ids)) logger.info(f"Saving to: {imgfile_out}") sci.save(imgfile_out) logger.info('Finished connecting streaks') ret_code = 0 return ret_code
def streakMask(self, streak_file, addWidth=0., addLength=100., maxExtrapolate=0): ''' Produce a list of pixels in the image that should be masked for streaks in the input table. streaktab is the output table of new streaks to add image is a FITS HDU, with header and image data addWidth is additional number of pixels to add to half-width addLength is length added to each end of streak (pixels) Returns: ypix, xpix: 1d arrays with indices of affected pixels nStreaks: number of new streaks masked ''' # Read the streaks table first try: tab = fitsio.FITS(streak_file) streaktab = tab[1].read() except: logger.error('Could not read streak file {:s}'.format(streak_file)) sys.exit(1) image_header = self.sci.header image_data = self.sci.data # Pixscale in degrees pixscale = astrometry.get_pixelscale(image_header, units='arcsec') / 3600. shape = image_data.shape # # Due to a bug in fitsio 1.0.0rc1+0, we need to clean up the # # header before feeding it to wcsutil and remove the 'None' and other problematic items # for k in image_header: # # Try to access the item, if failed we hace to remove it # try: # item = image_header[k] # except: # logger.info("Removing keyword: {:s} from header".format(k)) # image_header.delete(k) w = wcsutil.WCS(image_header) # WE NEED TO UPDATE THIS WHEN THE TABLE IS PER EXPNUM use = np.logical_and(streaktab['expnum'] == image_header['EXPNUM'], streaktab['ccdnum'] == image_header['CCDNUM']) logger.info('{:d} streaks found to mask'.format(np.count_nonzero(use))) nStreaks = 0 inside = None for row in streaktab[use]: if maxExtrapolate > 0: if row['extrapolated'] and row['nearest'] > maxExtrapolate: logger.info('Skipping extrapolated streak') continue width = row['width'] ra = np.array((row['ra1'], row['ra2'])) dec = np.array((row['dec1'], row['dec2'])) x, y = w.sky2image(ra, dec) x1, x2, y1, y2 = x[0], x[1], y[0], y[1] # Slope of the line, cos/sin form mx = (x2 - x1) / np.hypot(x2 - x1, y2 -y1) my = (y2 - y1) / np.hypot(x2 - x1, y2 -y1) #displacement for width of streak: wx = width / pixscale + addWidth wy = wx * mx wx = wx * -my # grow length x1 -= addLength * mx x2 += addLength * mx y1 -= addLength * my y2 += addLength * my # From Alex's immask routine: mark interior pixels vertices = [(x1 + wx, y1 + wy), (x2 + wx, y2 + wy), (x2 - wx, y2 - wy), (x1 - wx, y1 - wy)] vertices.append(vertices[0]) # Close the path if inside is None: # Set up coordinate arrays yy, xx = np.indices(shape) points = np.vstack((xx.flatten(), yy.flatten())).T path = matplotlib.path.Path(vertices) inside = path.contains_points(points) else: # use logical_and for additional streaks path = matplotlib.path.Path(vertices) inside = np.logical_or(inside, path.contains_points(points)) nStreaks = nStreaks + 1 logger.info('Masked {:d} new streaks'.format(nStreaks)) # Make the list of masked pixels if inside is None: ymask, xmask = np.array(0, dtype=int), np.array(0, dtype=int) else: ymask, xmask = np.nonzero(inside.reshape(shape)) logger.info('Setting bits in MSK image for STREAK: {:d}'.format(parse_badpix_mask('STREAK'))) self.sci.mask[ymask, xmask] |= parse_badpix_mask('STREAK')
def fitscutter(filename, ra, dec, xsize=1.0, ysize=1.0, units='arcmin', prefix='DES', outdir=os.getcwd(), tilename=None, verb=False): """ Makes cutouts around ra, dec for a give xsize and ysize ra,dec can be scalars or lists/arrays """ # Check and fix inputs ra, dec, xsize, ysize = check_inputs(ra, dec, xsize, ysize) # Check for the units if units == 'arcsec': scale = 1 elif units == 'arcmin': scale = 60 elif units == 'degree': scale = 3600 else: sys.exit("ERROR: must define units as arcses/arcmin/degree only") # Get header/extensions/hdu header, hdunum = get_headers_hdus(filename) extnames = header.keys() # Now we add the tilename to the headers -- if not already present if tilename and 'TILENAME' not in header['SCI']: if verb: SOUT.write("Will add TILENAME keyword to header for file: %s\n" % filename) tile_rec = { 'name': 'TILENAME', 'value': tilename, 'comment': 'Name of DES parent TILENAME' } for EXTNAME in extnames: header[EXTNAME].add_record(tile_rec) # Get the pixel-scale of the input image pixelscale = astrometry.get_pixelscale(header['SCI'], units='arcsec') # Read in the WCS with wcsutil wcs = wcsutil.WCS(header['SCI']) # Extract the band/filter from the header if 'BAND' in header['SCI']: band = header['SCI']['BAND'].strip() elif 'FILTER' in header['SCI']: band = header['SCI']['FILTER'].strip() else: raise Exception( "ERROR: Cannot provide suitable BAND/FILTER from SCI header") # Intitialize the FITS object ifits = fitsio.FITS(filename, 'r') ###################################### # Loop over ra/dec and xsize,ysize for k in range(len(ra)): # Define the geometry of the thumbnail x0, y0 = wcs.sky2image(ra[k], dec[k]) yL = 10000 xL = 10000 x0 = round(x0) y0 = round(y0) dx = int(0.5 * xsize[k] * scale / pixelscale) dy = int(0.5 * ysize[k] * scale / pixelscale) naxis1 = 2 * dx #+1 naxis2 = 2 * dy #+1 y1 = y0 - dy y2 = y0 + dy x1 = x0 - dx x2 = x0 + dx if y1 < 0: y1 = 0 dy = y0 y2 = y0 + dy if y2 > yL: y2 = yL dy = yL - y0 y1 = y0 - dy if x1 < 0: x1 = 0 dx = x0 x2 = x0 + dx if x2 > xL: x2 = xL dx = xL - x0 x1 = x0 - dx im_section = OrderedDict() h_section = OrderedDict() for EXTNAME in extnames: # The hdunum for that extname HDUNUM = hdunum[EXTNAME] # Create a canvas im_section[EXTNAME] = numpy.zeros((naxis1, naxis2)) # Read in the image section we want for SCI/WGT im_section[EXTNAME] = ifits[HDUNUM][int(y1):int(y2), int(x1):int(x2)] # Correct NAXIS1 and NAXIS2 naxis1 = numpy.shape(im_section[EXTNAME])[1] naxis2 = numpy.shape(im_section[EXTNAME])[0] # Update the WCS in the headers and make a copy h_section[EXTNAME] = update_wcs_matrix(header[EXTNAME], x0, y0, naxis1, naxis2, ra[k], dec[k]) # Construct the name of the Thumbmail using BAND/FILTER/prefix/etc outname = get_thumbFitsName(ra[k], dec[k], band, prefix=prefix, outdir=outdir) # Write out the file ofits = fitsio.FITS(outname, 'rw', clobber=True) for EXTNAME in extnames: ofits.write(im_section[EXTNAME], extname=EXTNAME, header=h_section[EXTNAME]) ofits.close() if verb: SOUT.write("# Wrote: %s\n" % outname) return
def run(self): runstat = 0 list_file = self.objlist outfile = '' for line in open(list_file): # line = line.rstrip('\n') line = line.rstrip() print(' line=%s \n' % line) tokens = line.split(',') objname = tokens[0] self.RA = tokens[1] self.Dec = tokens[2] byband = {} byband['g'] = [] byband['r'] = [] byband['i'] = [] byband['z'] = [] byband['Y'] = [] byband['u'] = [] t0 = timeit.default_timer() if not os.path.exists('./' + objname): os.mkdir(objname) explist = self.makeExpList(self.RA, self.Dec) # print explist if len(explist) >= 1: query = self.makeQueryString(self.RA, self.Dec, explist) # print query try: t1 = timeit.default_timer() self.cur.execute(query) res = self.cur.fetchall() print res t2 = timeit.default_timer() print "Query time=%f \n" % (t2 - t1) if len(res) == 0: print " No records found for object %s \n" % line runstat = -999 continue " Create directory if not exists " imperobj = {} for row in res: filename = row[0] band = row[1] ccdnum = row[2] if ccdnum == 'c31': continue nite = row[3] teff = float(row[4]) expnum = row[5] rac2 = float(row[6]) rac4 = float(row[7]) decc2 = float(row[8]) decc4 = float(row[9]) naxis1 = int(row[10]) naxis2 = int(row[11]) ra_cent = float(row[12]) dec_cent = float(row[13]) print "naxis1=%d naxis2=%d ra_cent=%f dec_cent=%f rac2=%f rac4=%f decc2=%f decc4=%f \n" % ( naxis1, naxis2, ra_cent, dec_cent, rac2, rac4, decc2, decc4) # unpack filename tokens2 = filename.split('_') Dpart = tokens2[0] # D00233091 band = tokens2[1] ccdnum = tokens2[2] # c01 expnum = string.atoi(Dpart[1:]) Year = self.expToYear(expnum) revp = tokens2[3] # r1984p01 tokens3 = revp.split('p') rev = (tokens3[0])[1:] proc = "p" + tokens3[1] imrec = [ band, teff, ccdnum, nite, expnum, naxis1, naxis2, ra_cent, dec_cent, rev, proc, rac2, rac4, decc2, decc4 ] imperobj[filename] = imrec " We have all images in the dictionary lets select best" print imperobj selfiles = self.sortIm(imperobj) print 'Selected files \n' print selfiles for band in selfiles: if len(selfiles[band]) == 0: continue filename = selfiles[band][0] teff = float(selfiles[band][2]) ccdnum = selfiles[band][3] nite = selfiles[band][4] expnum = selfiles[band][5] rev = selfiles[band][10] proc = selfiles[band][11] revp = 'r' + rev + 'p' + proc Year = self.expToYear(expnum) ccdnum = selfiles[band][3] ra_cent = float(selfiles[band][8]) dec_cent = float(selfiles[band][9]) (filepath, comp) = self.makeFilePath(expnum, rev, proc, Year, ccdnum) print "band=%s filepath=%s comp=%s \n" % ( band, filepath, comp) infile = filepath + '/' + filename if comp == '.fz': infile += comp outfileZ = './' + objname + '/' + filename + '.fz' outfileF = './' + objname + '/' + filename outfile = outfileF # psfFile = Dpart + '_' + band + '_' + ccdnum + '_' + revp + '_psfexcat.psf' outfileP = './' + objname + '/' + psfFile copys = False if self.file_exists(infile): (mag_zero, sigma_mag_zero) = self.getZeropoint( ccdnum, expnum) " throw away bad exposures " if mag_zero == 0. or teff <= 0.3: print "file rejected by mag_zero \n" continue tag = objname + '_' + str( expnum) + '_' + ccdnum + '_' + str( nite ) + '_' + band + '_' + self.RA + '_' + self.Dec psfFile = filename.split( 'immask')[0] + 'psfexcat.psf' outfileP = './' + objname + '/' + psfFile psfpath = filepath.split('red')[0] + 'psf' psfinF = psfpath + '/' + psfFile print "start coping psf file %s \n" % psfinF print " psf path=%s \n" % psfpath stat = self.get_file(self.opener, psfinF, outfileP) print " got the file \n" print stat if comp == '.fz': outfile = outfileZ copys = self.get_file(self.opener, infile, outfileZ) if not copys: continue " Now uncompress the file as swarp do not work with .fz " # command = ['funpack', "%s" % outfileZ] try: subprocess.check_output(command) except subprocess.CalledProcessError as e: print "error %s" % e outfile = outfileF try: os.remove(outfileZ) except: print "remove failed on file %s \n" % outfileZ else: outfile = outfileF copys = self.get_file(infile, outfileF) if not copys: continue stampname = './' + objname + '/' + tag outPSF = './' + objname + '/' + tag + '_psf.fits' filelist = [outfile] self.MakeStamp(filelist, stampname, band, objname) # print "After MakeStamp \n" """ Now create PSF """ RAV = float(self.RA) DecV = float(self.Dec) print "Start conversion with file %s \n" % stampname + '.fits' fitsfile = os.path.normpath(stampname + '.fits') fits = fitsio.FITS(fitsfile) prihdr = fits[0].read_header() w = wcsutil.WCS(prihdr) # print "RA=%f Dec=%f \n" % (RAV,DecV) (objx, objy) = w.sky2image(RAV, DecV) print "objx=%f objy=%f \n" % (objx, objy) try: psfcatF = stampname + '_psfex.psf' print " psfcat.psf=%s outPSF=%s \n" % (psfcatF, outPSF) mkpsf = CreatePSFFile(outfileP, outPSF) mkpsf.setPSFAtt(objname, band) mkpsf.produce(objx, objy) try: # os.remove(outfileF) print "Now removing file %s \n" % outfileF except: print "remove failed on file %s \n" % outfileF except: print " Failed on psf file %s \n" % outPSF # self.clean(filelist,stampname) byband[band].append( (tag, mag_zero, sigma_mag_zero, teff)) else: continue except: print(' Failed on object %s \n' % objname) print("Unexpected error:", sys.exc_info()[0]) runstat += 1 continue else: print " No exposures found for Ra=%s Dec=%s \n" % (self.RA, self.Dec) " Now we have cutouts in ./objname subdir lets make fits file " if self.restype == '1': keys = byband.keys() fitsout = objname + '_cutouts.fits' if os.path.exists(fitsout): os.remove(fitsout) for key in keys: tags = byband.get(key) for t in tags: self.writeResFile(fitsout, objname, t, key) shutil.rmtree('./' + objname) t3 = timeit.default_timer() exectime = float((t3 - t0) / 60.) print " end work with line %s exectime=%.2f min \n" % (line, exectime) SystemExit(runstat)
def cross_RA_zero_center(self): """ Check if crossing the RA=0.0 by the min and max values for the centers of all CCDs. We loop over all of the wcs of the images... slower (a few secs) but safer in case some CCDs are missing. """ d2r = math.pi / 180. # degrees to radians shorthand DECam_width = 2.3 # approx width in degrees # Collect the centers of all CCDS first and store to compare later # Slowers than asking for particular CCDs, but safer print("# Figuring out edges of CCDs") t0 = time.time() ra0 = [] dec0 = [] for filename in self.imgfiles: hdr = fitsio.read_header(filename) wcs = wcsutil.WCS(hdr) nx = hdr['NAXIS1'] ny = hdr['NAXIS2'] ra, dec = wcs.image2sky(nx / 2.0, ny / 2.0) ra0.append(ra) dec0.append(dec) self.ra0 = numpy.array(ra0) self.dec0 = numpy.array(dec0) # Get min and max values dec1 = self.dec0.min() dec2 = self.dec0.max() ra1 = self.ra0.min() ra2 = self.ra0.max() print(f"# Cross RA examination: {elapsed_time(t0)}") dec0 = (dec1 + dec2) / 2.0 D_ra = abs(ra1 - ra2) D_dec = abs(dec1 - dec2) # in case ras near 360 have negative values # so, we need to check that both ras have the same sign if math.copysign(1, ra1) == math.copysign(1, ra2): same_sign = True else: same_sign = False # Define the tolerances for ra/dec tol_ra = 2.5 * DECam_width / math.cos(dec0 * d2r) tol_dec = 2.5 * DECam_width # Check WCS solution is sane, by making sure that the distance # between ra.min and ra.max or dec.min/dec.max in D > tol and # D < 360-tol (for RA) if (D_ra > tol_ra and D_ra < 360 - tol_ra) or D_dec > tol_dec: print( "# ***************************************************************" ) print( "# ** WARNING: Distance between CCDs greated that DECam FOV **" ) print( "# ** WARNING: Projection will not be performed -- bye **" ) print( "# ***************************************************************" ) sys.exit() # The tolerace for deciding when we crossed RA=0 is ~2x a # DECam FOV at the declination if D_ra > tol_ra or same_sign is False: self.crossRA = True print("# *****************************************") print("# ** WARNING: exposure crosses RA=0.0 **") print("# *****************************************") # Bring to zero the values near 360 idx = numpy.where(self.ra0 > tol_ra) self.ra0[idx] = self.ra0[idx] - 360 else: self.crossRA = False
def read_exposure_catalogs_files(self): """ Read the 62 exposure SEx catalogs""" # Define the output name self.pngfile_ell = f"{self.basename}_ell.png" if os.path.exists(self.pngfile_ell) and not self.force: print("# PNG/Ell file already exists") print("# Skipping PNG/Ell creation") return t0 = time.time() print(f"# Reading {self.pngfile}") image = Image.open(self.pngfile).convert("L") self.png_array = numpy.asarray(image) self.png_array = self.png_array[::-1, :] print(f"# Shape (ny,nx): {self.png_array.shape}") print(f"# Done in {time.time()-t0} sec.") # Figure out the size dpi = 90. (self.ny, self.nx) = self.png_array.shape x_size = float(self.nx) / float(dpi) y_size = float(self.ny) / float(dpi) pylab.figure(1, figsize=(x_size, y_size)) pylab.axes([0, 0, 1, 1], frameon=False) pylab.imshow(self.png_array, origin='lower', cmap='gray', interpolation='none') ec_ima1 = 'red' ec_ima2 = 'blue' pylab.axis('off') i = 0 t0 = time.time() for catfile in self.catlist: print(f"# Reading {catfile}") tbdata = fitsio.read(catfile, ext=2) # Store the Relevant information to draw ellipse if i == 0: ra = tbdata['ALPHA_J2000'] dec = tbdata['DELTA_J2000'] a_image = tbdata['A_IMAGE'] * tbdata['KRON_RADIUS'] b_image = tbdata['B_IMAGE'] * tbdata['KRON_RADIUS'] theta = tbdata['THETA_IMAGE'] imaflag_iso = tbdata['IMAFLAGS_ISO'] else: ra = numpy.append(ra, tbdata['ALPHA_J2000']) dec = numpy.append(dec, tbdata['DELTA_J2000']) a_image = numpy.append( a_image, tbdata['A_IMAGE'] * tbdata['KRON_RADIUS']) b_image = numpy.append( b_image, tbdata['B_IMAGE'] * tbdata['KRON_RADIUS']) theta = numpy.append(theta, tbdata['THETA_IMAGE']) imaflag_iso = numpy.append(imaflag_iso, tbdata['IMAFLAGS_ISO']) i = i + 1 print(f"# Read {i} SEx catalogs in time: {elapsed_time(t0)}") # Now let's put the positions on the projected image hdr = fitsio.read_header(self.swarp_outname) wcs = wcsutil.WCS(hdr) x, y = wcs.sky2image(ra, dec) # Draw all at once -- faster t1 = time.time() print(f"# Drawing ellipses for {len(x)} objects") # Drawing imaflags > 0 blue and the rest 'red' idx1 = numpy.where(imaflag_iso == 0) idx2 = numpy.where(imaflag_iso > 0) draw.PEllipse_multi((x[idx1], y[idx1]), (a_image[idx1], b_image[idx1]), resolution=60, angle=theta, facecolor='none', edgecolor=ec_ima1, linewidth=0.5) draw.PEllipse_multi((x[idx2], y[idx2]), (a_image[idx2], b_image[idx2]), resolution=60, angle=theta, facecolor='none', edgecolor=ec_ima2, linewidth=0.5) print(f"# Ellipses draw time: {elapsed_time(t1)}") print("# Saving PNG file with ellipses") pylab.savefig(self.pngfile_ell, dpi=dpi) print("# Done") pylab.close() return