def get_flux_in_half_light_radius(self, fileName, hlr, detector, camera, obs, epoch=2000.0): """ Read in a FITS image. Return the total flux in that image as well as the flux contained within a specified radius of the maximum pixel of the image. @param [in] fileName is the name of the FITS file to be read in @param [in] hlr is the half light radius to be tested (in arc seconds) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. @param [out] totalFlux is the total number of counts in the images @param [out] measuredHalfFlux is the measured flux within hlr of the maximum pixel """ im = afwImage.ImageF(fileName).getArray() totalFlux = im.sum() _maxPixel = numpy.array([im.argmax()/im.shape[1], im.argmax()%im.shape[1]]) maxPixel = numpy.array([_maxPixel[1], _maxPixel[0]]) raMax, decMax = _raDecFromPixelCoords(maxPixel[0:1], maxPixel[1:2], [detector.getName()], camera=camera, obs_metadata=obs, epoch=epoch) activePoints = numpy.where(im>1.0e-10) self.assertGreater(len(activePoints), 0) xPixList = activePoints[1] # this looks backwards, but remember: the way numpy handles yPixList = activePoints[0] # arrays, the first index indicates what row it is in (the y coordinate) chipNameList = [detector.getName()]*len(xPixList) raList, decList = _raDecFromPixelCoords(xPixList, yPixList, chipNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians(haversine(raList, decList, raMax[0], decMax[0])) dexContained = [ix for ix, dd in enumerate(distanceList) if dd<=hlr] measuredHalfFlux = numpy.array([im[yPixList[dex]][xPixList[dex]] for dex in dexContained]).sum() return totalFlux, measuredHalfFlux
def _radec(self, x, y): """ This is a method required by the GalSim WCS API Convert pixel coordinates into ra, dec coordinates. x and y already have crpix1 and crpix2 subtracted from them. Return ra, dec in radians. """ chipNameList = [self.afwDetector.getName()] if type(x) is np.ndarray: chipNameList = chipNameList * len(x) ra, dec = _raDecFromPixelCoords(x + self.afw_crpix1, y + self.afw_crpix2, chipNameList, camera=self.afwCamera, obs_metadata=self.obs_metadata, epoch=self.epoch) if type(x) is np.ndarray: return (ra, dec) else: return (ra[0], dec[0])
def testTanSipWcs(self): """ Test that tanSipWcsFromDetector works by fitting a TAN WCS and a TAN-SIP WCS to a detector with distortions and verifying that the TAN-SIP WCS better approximates the truth. """ tanWcs = tanWcsFromDetector(self.detector, self.camera, self.obs, self.epoch) tanSipWcs = tanSipWcsFromDetector(self.detector, self.camera, self.obs, self.epoch) tanWcsRa = [] tanWcsDec = [] tanSipWcsRa = [] tanSipWcsDec = [] xPixList = [] yPixList = [] for xx in numpy.arange(0.0, 4001.0, 100.0): for yy in numpy.arange(0.0, 4001.0, 100.0): xPixList.append(xx) yPixList.append(yy) pt = afwGeom.Point2D(xx ,yy) skyPt = tanWcs.pixelToSky(pt).getPosition() tanWcsRa.append(skyPt.getX()) tanWcsDec.append(skyPt.getY()) skyPt = tanSipWcs.pixelToSky(pt).getPosition() tanSipWcsRa.append(skyPt.getX()) tanSipWcsDec.append(skyPt.getY()) tanWcsRa = numpy.radians(numpy.array(tanWcsRa)) tanWcsDec = numpy.radians(numpy.array(tanWcsDec)) tanSipWcsRa = numpy.radians(numpy.array(tanSipWcsRa)) tanSipWcsDec = numpy.radians(numpy.array(tanSipWcsDec)) xPixList = numpy.array(xPixList) yPixList = numpy.array(yPixList) raTest, decTest = _raDecFromPixelCoords(xPixList, yPixList, [self.detector.getName()]*len(xPixList), camera=self.camera, obs_metadata=self.obs, epoch=self.epoch) tanDistanceList = arcsecFromRadians(haversine(raTest, decTest, tanWcsRa, tanWcsDec)) tanSipDistanceList = arcsecFromRadians(haversine(raTest, decTest, tanSipWcsRa, tanSipWcsDec)) maxDistanceTan = tanDistanceList.max() maxDistanceTanSip = tanSipDistanceList.max() msg = 'max error in TAN WCS %e arcsec; in TAN-SIP %e arcsec' % (maxDistanceTan, maxDistanceTanSip) self.assertLess(maxDistanceTanSip, 0.01, msg=msg) self.assertGreater(maxDistanceTan-maxDistanceTanSip, 1.0e-10, msg=msg)
def testTanSipWcs(self): """ Test that tanSipWcsFromDetector works by fitting a TAN WCS and a TAN-SIP WCS to a detector with distortions and verifying that the TAN-SIP WCS better approximates the truth. """ tanWcs = tanWcsFromDetector(self.detector, self.camera, self.obs, self.epoch) tanSipWcs = tanSipWcsFromDetector(self.detector, self.camera, self.obs, self.epoch) tanWcsRa = [] tanWcsDec = [] tanSipWcsRa = [] tanSipWcsDec = [] xPixList = [] yPixList = [] for xx in np.arange(0.0, 4001.0, 100.0): for yy in np.arange(0.0, 4001.0, 100.0): xPixList.append(xx) yPixList.append(yy) pt = afwGeom.Point2D(xx, yy) skyPt = tanWcs.pixelToSky(pt).getPosition() tanWcsRa.append(skyPt.getX()) tanWcsDec.append(skyPt.getY()) skyPt = tanSipWcs.pixelToSky(pt).getPosition() tanSipWcsRa.append(skyPt.getX()) tanSipWcsDec.append(skyPt.getY()) tanWcsRa = np.radians(np.array(tanWcsRa)) tanWcsDec = np.radians(np.array(tanWcsDec)) tanSipWcsRa = np.radians(np.array(tanSipWcsRa)) tanSipWcsDec = np.radians(np.array(tanSipWcsDec)) xPixList = np.array(xPixList) yPixList = np.array(yPixList) raTest, decTest = _raDecFromPixelCoords(xPixList, yPixList, [self.detector.getName()]*len(xPixList), camera=self.camera, obs_metadata=self.obs, epoch=self.epoch) tanDistanceList = arcsecFromRadians(haversine(raTest, decTest, tanWcsRa, tanWcsDec)) tanSipDistanceList = arcsecFromRadians(haversine(raTest, decTest, tanSipWcsRa, tanSipWcsDec)) maxDistanceTan = tanDistanceList.max() maxDistanceTanSip = tanSipDistanceList.max() msg = 'max error in TAN WCS %e arcsec; in TAN-SIP %e arcsec' % (maxDistanceTan, maxDistanceTanSip) self.assertLess(maxDistanceTanSip, 0.01, msg=msg) self.assertGreater(maxDistanceTan-maxDistanceTanSip, 1.0e-10, msg=msg)
def _raDecFromPixelCoords(self, xPix, yPix, chipName, obs_metadata, epoch=2000.0, includeDistortion=True): """ Convert pixel coordinates into RA, Dec Parameters ---------- xPix is the x pixel coordinate. It can be either a float or a numpy array. yPix is the y pixel coordinate. It can be either a float or a numpy array. chipName is the name of the chip(s) on which the pixel coordinates are defined. This can be a list (in which case there should be one chip name for each (xPix, yPix) coordinate pair), or a single value (in which case, all of the (xPix, yPix) points will be reckoned on that chip). obs_metadata is an ObservationMetaData defining the pointing epoch is the mean epoch in years of the celestial coordinate system. Default is 2000. includeDistortion is a boolean. If True (default), then this method will expect the true pixel coordinates with optical distortion included. If False, this method will expect TAN_PIXEL coordinates, which are the pixel coordinates with estimated optical distortion removed. See the documentation in afw.cameraGeom for more details. Returns ------- a 2-D numpy array in which the first row is the RA coordinate and the second row is the Dec coordinate (both in radians; in the International Celestial Reference System) WARNING: This method does not account for apparent motion due to parallax. This method is only useful for mapping positions on a theoretical focal plane to positions on the celestial sphere. """ return coordUtils._raDecFromPixelCoords( xPix, yPix, chipName, camera=self._camera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=includeDistortion)
def testContainsRaDec(self): """ Test whether or not the method containsRaDec correctly identifies RA and Dec that fall inside and outside the detector """ photParams = PhotometricParameters() gsdet = GalSimDetector(self.camera[0].getName(), GalSimCameraWrapper(self.camera), self.obs, self.epoch, photParams=photParams) xxList = [gsdet.xMinPix, gsdet.xMaxPix] yyList = [gsdet.yMinPix, gsdet.yMaxPix] dxList = [-1.0, 1.0] dyList = [-1.0, 1.0] xPixList = [] yPixList = [] correctAnswer = [] for xx, yy, dx, dy in zip(xxList, yyList, dxList, dyList): xPixList.append(xx) yPixList.append(yy) correctAnswer.append(True) xPixList.append(xx + dx) yPixList.append(yy) correctAnswer.append(False) xPixList.append(xx) yPixList.append(yy + dy) correctAnswer.append(False) nameList = [gsdet.name] * len(xPixList) xPixList = np.array(xPixList) yPixList = np.array(yPixList) raList, decList = _raDecFromPixelCoords(xPixList, yPixList, nameList, camera=self.camera, obs_metadata=self.obs, epoch=self.epoch) testAnswer = gsdet.containsRaDec(raList, decList) for c, t in zip(correctAnswer, testAnswer): self.assertIs(c, t)
def testContainsRaDec(self): """ Test whether or not the method containsRaDec correctly identifies RA and Dec that fall inside and outside the detector """ photParams = PhotometricParameters() gsdet = GalSimDetector(self.camera[0], self.camera, \ self.obs, self.epoch, photParams=photParams) xxList = [gsdet.xMinPix, gsdet.xMaxPix] yyList = [gsdet.yMinPix, gsdet.yMaxPix] dxList = [-1.0, 1.0] dyList = [-1.0, 1.0] xPixList = [] yPixList = [] correctAnswer = [] for xx, yy, dx, dy in zip(xxList, yyList, dxList, dyList): xPixList.append(xx) yPixList.append(yy) correctAnswer.append(True) xPixList.append(xx+dx) yPixList.append(yy) correctAnswer.append(False) xPixList.append(xx) yPixList.append(yy+dy) correctAnswer.append(False) nameList = [gsdet.name]*len(xPixList) xPixList = numpy.array(xPixList) yPixList = numpy.array(yPixList) raList, decList = _raDecFromPixelCoords(xPixList, yPixList, nameList, camera=self.camera, obs_metadata=self.obs, epoch=self.epoch) testAnswer = gsdet.containsRaDec(raList, decList) for c, t in zip(correctAnswer, testAnswer): self.assertTrue(c is t)
def testTanWcs(self): """ Test method to return a Tan WCS by generating a bunch of pixel coordinates in the undistorted TAN-PIXELS coordinate system. Then, use sims_coordUtils to convert those pixel coordinates into RA and Dec. Compare these to the RA and Dec returned by the WCS. Demand agreement to witin 0.001 arcseconds. Note: if you use a bigger camera, it is possible to have disagreements of order a few milliarcseconds. """ detector = self.camera[0] xPixList = [] yPixList = [] tanWcs = tanWcsFromDetector(detector, self.camera, self.obs, self.epoch) wcsRa = [] wcsDec = [] for xx in numpy.arange(0.0, 4001.0, 1000.0): for yy in numpy.arange(0.0, 4001.0, 1000.0): xPixList.append(xx) yPixList.append(yy) pt = afwGeom.Point2D(xx ,yy) skyPt = tanWcs.pixelToSky(pt).getPosition() wcsRa.append(skyPt.getX()) wcsDec.append(skyPt.getY()) wcsRa = numpy.radians(numpy.array(wcsRa)) wcsDec = numpy.radians(numpy.array(wcsDec)) xPixList = numpy.array(xPixList) yPixList = numpy.array(yPixList) raTest, decTest = _raDecFromPixelCoords(xPixList, yPixList, [detector.getName()]*len(xPixList), camera=self.camera, obs_metadata=self.obs, epoch=self.epoch) distanceList = arcsecFromRadians(haversine(raTest, decTest, wcsRa, wcsDec)) maxDistance = distanceList.max() msg = 'maxError in tanWcs was %e ' % maxDistance self.assertTrue(maxDistance<0.001, msg=msg)
def _radec(self, x, y): """ This is a method required by the GalSim WCS API Convert pixel coordinates into ra, dec coordinates. x and y already have crpix1 and crpix2 subtracted from them. Return ra, dec in radians. """ chipNameList = [self.afwDetector.getName()] if type(x) is numpy.ndarray: chipNameList = chipNameList * len(x) ra, dec = _raDecFromPixelCoords(x + self.afw_crpix1, y + self.afw_crpix2, chipNameList, camera=self.afwCamera, obs_metadata=self.obs_metadata, epoch=self.epoch) if type(x) is numpy.ndarray: return (ra, dec) else: return (ra[0], dec[0])
def get_position_angle(self, imageName, afwCamera, afwDetector, obs_metadata, epoch): """ Read in a FITS image containing one extended object. Determine its north and east axes by examining how RA and Dec change with pixel position. Determine the semi-major axis of the object by treating the distribution of flux as a covariance matrix and finding its eigen vectors. Return the angle between the semi-major axis and the north axis of the image @param [in] imageName is the name of the FITS image to be read @param [in] afwCamera is an afw.cameraGeom.Camera @param [in] afwDetector is an afw.cameraGeom.Detector @param [in] obs_metadata is an ObservationMetaData describing the pointing of the telescope @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured @param [out] the position angle of the object in the image in degrees """ im = afwImage.ImageF(imageName).getArray() activePixels = np.where(im > 1.0e-10) self.assertGreater(len(activePixels), 0) xPixList = activePixels[1] yPixList = activePixels[0] xCenterPix = np.array([im.shape[1] / 2]) yCenterPix = np.array([im.shape[0] / 2]) raCenter, decCenter = _raDecFromPixelCoords(xCenterPix, yCenterPix, [afwDetector.getName()], camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch) xCenterP1 = xCenterPix + 1 yCenterP1 = yCenterPix + 1 raCenterP1, decCenterP1 = _raDecFromPixelCoords( xCenterP1, yCenterP1, [afwDetector.getName()], camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch) # find the angle between the (1,1) vector in pixel space and the # north axis of the image theta = np.arctan2((raCenterP1[0] - raCenter[0]), decCenterP1[0] - decCenter[0]) # rotate the (1,1) vector in pixel space so that it is pointing # along the north axis north = np.array( [np.cos(theta) - np.sin(theta), np.cos(theta) + np.sin(theta)]) north = north / np.sqrt(north[0] * north[0] + north[1] * north[1]) # find the east axis of the image east = np.array([north[1], -1.0 * north[0]]) # now find the covariance matrix of the x, y pixel space distribution # of flux on the image maxPixel = np.array( [im.argmax() % im.shape[1], im.argmax() / im.shape[1]]) xx = np.array([ im[iy][ix] * np.power(ix - maxPixel[0], 2) for ix, iy in zip(xPixList, yPixList) ]).sum() xy = np.array([ im[iy][ix] * (ix - maxPixel[0]) * (iy - maxPixel[1]) for ix, iy in zip(xPixList, yPixList) ]).sum() yy = np.array([ im[iy][ix] * (iy - maxPixel[1]) * (iy - maxPixel[1]) for ix, iy in zip(xPixList, yPixList) ]).sum() covar = np.array([[xx, xy], [xy, yy]]) # find the eigen vectors of this covarinace matrix; # treat the one with the largest eigen value as the # semi-major axis of the object eigenVals, eigenVecs = np.linalg.eig(covar) iMax = eigenVals.argmax() majorAxis = eigenVecs[:, iMax] majorAxis = majorAxis / np.sqrt(majorAxis[0] * majorAxis[0] + majorAxis[1] * majorAxis[1]) # return the angle between the north axis of the image # and the semi-major axis of the object cosTheta = np.dot(majorAxis, north) sinTheta = np.dot(majorAxis, east) theta = np.arctan2(sinTheta, cosTheta) return np.degrees(theta)
def get_position_angle(self, imageName, afwCamera, afwDetector, \ obs_metadata, epoch): """ Read in a FITS image containing one extended object. Determine its north and east axes by examining how RA and Dec change with pixel position. Determin the semi-major axis of the object by treating the distribution of flux as a covariance matrix and finding its eigen vectors. Return the angle between the semi-major axis and the north axis of the image @param [in] imageName is the name of the FITS image to be read @param [in] afwCamera is an afw.cameraGeom.Camera @param [in] afwDetector is an afw.cameraGeom.Detector @param [in] obs_metadata is an ObservationMetaData describing the pointing of the telescope @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured @param [out] the position angle of the object in the image in degrees """ im = afwImage.ImageF(imageName).getArray() activePixels = numpy.where(im>1.0e-10) self.assertGreater(len(activePixels), 0) xPixList = activePixels[1] yPixList = activePixels[0] xCenterPix = numpy.array([im.shape[1]/2]) yCenterPix = numpy.array([im.shape[0]/2]) raCenter, decCenter = _raDecFromPixelCoords(xCenterPix, yCenterPix, [afwDetector.getName()], camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch) xCenterP1 = xCenterPix+1 yCenterP1 = yCenterPix+1 raCenterP1,decCenterP1 = _raDecFromPixelCoords(xCenterP1, yCenterP1, [afwDetector.getName()], camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch) # find the angle between the (1,1) vector in pixel space and the # north axis of the image theta = numpy.arctan2(-1.0*(raCenterP1[0]-raCenter[0]), decCenterP1[0]-decCenter[0]) # rotate the (1,1) vector in pixel space so that it is pointing # along the north axis north = numpy.array([numpy.cos(theta)-numpy.sin(theta), numpy.cos(theta)+numpy.sin(theta)]) north = north/numpy.sqrt(north[0]*north[0]+north[1]*north[1]) # find the west axis of the image west = numpy.array([north[1], -1.0*north[0]]) # now find the covariance matrix of the x, y pixel space distribution # of flux on the image maxPixel = numpy.array([im.argmax()%im.shape[1], im.argmax()/im.shape[1]]) xx = numpy.array([im[iy][ix]*numpy.power(ix-maxPixel[0],2) \ for ix, iy in zip(xPixList, yPixList)]).sum() xy = numpy.array([im[iy][ix]*(ix-maxPixel[0])*(iy-maxPixel[1]) \ for ix, iy in zip(xPixList, yPixList)]).sum() yy = numpy.array([im[iy][ix]*(iy-maxPixel[1])*(iy-maxPixel[1]) \ for ix, iy in zip(xPixList, yPixList)]).sum() covar = numpy.array([[xx, xy],[xy, yy]]) # find the eigen vectors of this covarinace matrix; # treat the one with the largest eigen value as the # semi-major axis of the object eigenVals, eigenVecs = numpy.linalg.eig(covar) iMax = eigenVals.argmax() majorAxis = eigenVecs[:,iMax] majorAxis = majorAxis/numpy.sqrt(majorAxis[0]*majorAxis[0]+majorAxis[1]*majorAxis[1]) # return the angle between the north axis of the image # and the semi-major axis of the object cosTheta = numpy.dot(majorAxis, north) sinTheta = numpy.dot(majorAxis, west) theta = numpy.arctan2(sinTheta, cosTheta) return numpy.degrees(theta)
def testOutputWcsOfImage(self): """ Test that, when GalSim generates an image, in encodes the WCS in a way afw can read. This is done by creating an image,then reading it back in, getting its WCS, and comparing the pixel-to-sky conversion both for the read WCS and the original afw.cameraGeom.detector. Raise an exception if the median difference between the two is greater than 0.01 arcseconds. """ scratchDir = os.path.join(getPackageDir('sims_GalSimInterface'), 'tests', 'scratchSpace') catName = os.path.join(scratchDir, 'outputWcs_test_Catalog.dat') imageRoot = os.path.join(scratchDir, 'outputWcs_test_Image') dbFileName = os.path.join(scratchDir, 'outputWcs_test_InputCatalog.dat') baseDir = os.path.join(getPackageDir('sims_GalSimInterface'), 'tests', 'cameraData') camera = ReturnCamera(baseDir) detector = camera[0] detName = detector.getName() imageName = '%s_%s_u.fits' % (imageRoot, detName) nSamples = 3 rng = np.random.RandomState(42) pointingRaList = rng.random_sample(nSamples) * 360.0 pointingDecList = rng.random_sample(nSamples) * 180.0 - 90.0 rotSkyPosList = rng.random_sample(nSamples) * 360.0 for raPointing, decPointing, rotSkyPos in \ zip(pointingRaList, pointingDecList, rotSkyPosList): obs = ObservationMetaData(pointingRA=raPointing, pointingDec=decPointing, boundType='circle', boundLength=4.0, rotSkyPos=rotSkyPos, mjd=49250.0) fwhm = 0.7 create_text_catalog(obs, dbFileName, np.array([3.0]), np.array([1.0])) db = outputWcsFileDBObj(dbFileName, runtable='test') cat = outputWcsCat(db, obs_metadata=obs) cat.camera = camera psf = SNRdocumentPSF(fwhm=fwhm) cat.setPSF(psf) cat.write_catalog(catName) cat.write_images(nameRoot=imageRoot) # 20 March 2017 # the 'try' block is how it worked in SWIG; # the 'except' block is how it works in pybind11 try: exposure = afwImage.ExposureD_readFits(imageName) except AttributeError: exposure = afwImage.ExposureD.readFits(imageName) wcs = exposure.getWcs() xxTestList = [] yyTestList = [] raImage = [] decImage = [] for xx in np.arange(0.0, 4001.0, 100.0): for yy in np.arange(0.0, 4001.0, 100.0): xxTestList.append(xx) yyTestList.append(yy) pt = afwGeom.Point2D(xx, yy) skyPt = wcs.pixelToSky(pt).getPosition() raImage.append(skyPt.getX()) decImage.append(skyPt.getY()) xxTestList = np.array(xxTestList) yyTestList = np.array(yyTestList) raImage = np.radians(np.array(raImage)) decImage = np.radians(np.array(decImage)) raControl, \ decControl = _raDecFromPixelCoords(xxTestList, yyTestList, [detector.getName()]*len(xxTestList), camera=camera, obs_metadata=obs, epoch=2000.0) errorList = arcsecFromRadians( haversine(raControl, decControl, raImage, decImage)) medianError = np.median(errorList) msg = 'medianError was %e' % medianError self.assertLess(medianError, 0.01, msg=msg) if os.path.exists(catName): os.unlink(catName) if os.path.exists(dbFileName): os.unlink(dbFileName) if os.path.exists(imageName): os.unlink(imageName)
def check_placement(self, imageName, raList, decList, fwhmList, countList, gain, detector, camera, obs, epoch=2000.0): """ Read in a FITS image and a list of objects meant to be on that image. Verify that the objects were placed at the correct pixel by counting up all of the flux within 2 fwhm of each object's expected location and verifying it with the counts expected for that object. @param [in] imageName is the name of the FITS file to be read in @param [in] raList is a numpy array of the RA coordinates of the objects in the image (in radians) @param [in] decList is a numpy array of the Dec coordinates of the objects in the image (in radians) @param [in] fwhmList is a list of the Full Width at Half Maximum of each object in arcseconds @param [in] countList is a list of the counts expected for each object @param [in] gain is the gain of the detector (electrons per ADU) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. Raises an exception of the counts detected for each object differs from the expected amount by more than 3 sigma. """ im = afwImage.ImageF(imageName).getArray() activePixels = np.where(im > 1.0e-10) # I know this seems backwards, but the way numpy handles arrays, # the first index is the row (i.e. the y coordinate) imXList = activePixels[1] imYList = activePixels[0] nameList = [detector.getName()]*len(raList) xPixList, yPixList = _pixelCoordsFromRaDec(raList, decList, chipName=nameList, camera=camera, obs_metadata=obs, epoch=epoch) for rr, dd, xx, yy, fwhm, cc in \ zip(raList, decList, xPixList, yPixList, fwhmList, countList): countSigma = np.sqrt(cc/gain) imNameList = [detector.getName()]*len(imXList) raImList, decImList = _raDecFromPixelCoords(imXList, imYList, imNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians(haversine(raImList, decImList, rr, dd)) fluxArray = np.array([im[imYList[ix]][imXList[ix]] for ix in range(len(distanceList)) if distanceList[ix] < 2.0*fwhm]) totalFlux = fluxArray.sum() msg = 'totalFlux %e should be %e diff/sigma %e' \ % (totalFlux, cc, np.abs(totalFlux-cc)/countSigma) self.assertLess(np.abs(totalFlux-cc), 3.0*countSigma, msg=msg)
def get_flux_in_half_light_radius(self, fileName, hlr, detector, camera, obs, epoch=2000.0): """ Read in a FITS image. Return the total flux in that image as well as the flux contained within a specified radius of the maximum pixel of the image. @param [in] fileName is the name of the FITS file to be read in @param [in] hlr is the half light radius to be tested (in arc seconds) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. @param [out] totalFlux is the total number of counts in the images @param [out] measuredHalfFlux is the measured flux within hlr of the maximum pixel """ im = afwImage.ImageF(fileName).getArray() totalFlux = im.sum() _maxPixel = np.array( [im.argmax() / im.shape[1], im.argmax() % im.shape[1]]) maxPixel = np.array([_maxPixel[1], _maxPixel[0]]) raMax, decMax = _raDecFromPixelCoords(maxPixel[0:1], maxPixel[1:2], [detector.getName()], camera=camera, obs_metadata=obs, epoch=epoch) activePoints = np.where(im > 1.0e-10) self.assertGreater(len(activePoints), 0) xPixList = activePoints[ 1] # this looks backwards, but remember: the way numpy handles yPixList = activePoints[ 0] # arrays, the first index indicates what row it is in (the y coordinate) chipNameList = [detector.getName()] * len(xPixList) raList, decList = _raDecFromPixelCoords(xPixList, yPixList, chipNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians( haversine(raList, decList, raMax[0], decMax[0])) dexContained = [ix for ix, dd in enumerate(distanceList) if dd <= hlr] measuredHalfFlux = np.array( [im[yPixList[dex]][xPixList[dex]] for dex in dexContained]).sum() return totalFlux, measuredHalfFlux
def verify_fwhm(self, fileName, fwhm, detector, camera, obs, epoch=2000.0): """ Read in a FITS image with one object on it and verify that that object has the expected Full Width at Half Maximum. This is done by finding the brightest pixel in the image, and then drawing 1-dimensional profiles of the object centered on that pixel (but at different angles relative to the axes of the image). The code then walks along those profiles and keeps track of the distance between the two points at which the flux is half of the maximum. @param [in] fileName is the name of the FITS image @param [in] fwhm is the expected Full Width at Half Maximum in arcseconds @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. This method will raise an exception if the measured Full Width at Half Maximum deviates from the expected value by more than ten percent. """ im = afwImage.ImageF(fileName).getArray() maxFlux = im.max() # this looks backwards, but remember: the way numpy handles # arrays, the first index indicates what row it is in (the y coordinate) _maxPixel = numpy.array([im.argmax() / im.shape[1], im.argmax() % im.shape[1]]) maxPixel = numpy.array([_maxPixel[1], _maxPixel[0]]) raMax, decMax = _raDecFromPixelCoords( maxPixel[:1], maxPixel[1:2], [detector.getName()], camera=camera, obs_metadata=obs, epoch=epoch ) half_flux = 0.5 * maxFlux for theta in numpy.arange(0.0, 2.0 * numpy.pi, 0.21 * numpy.pi): slope = numpy.tan(theta) if numpy.abs(slope < 1.0): xPixList = numpy.array( [ ix for ix in range(0, im.shape[1]) if int(slope * (ix - maxPixel[0]) + maxPixel[1]) >= 0 and int(slope * (ix - maxPixel[0]) + maxPixel[1]) < im.shape[0] ] ) yPixList = numpy.array([int(slope * (ix - maxPixel[0]) + maxPixel[1]) for ix in xPixList]) else: yPixList = numpy.array( [ iy for iy in range(0, im.shape[0]) if int((iy - maxPixel[1]) / slope + maxPixel[0]) >= 0 and int((iy - maxPixel[1]) / slope + maxPixel[0]) < im.shape[1] ] ) xPixList = numpy.array([int((iy - maxPixel[1]) / slope + maxPixel[0]) for iy in yPixList]) chipNameList = [detector.getName()] * len(xPixList) raList, decList = _raDecFromPixelCoords( xPixList, yPixList, chipNameList, camera=camera, obs_metadata=obs, epoch=epoch ) distanceList = arcsecFromRadians(haversine(raList, decList, raMax[0], decMax[0])) fluxList = numpy.array([im[iy][ix] for ix, iy in zip(xPixList, yPixList)]) distanceToLeft = None distanceToRight = None for ix in range(1, len(xPixList)): if fluxList[ix] < half_flux and fluxList[ix + 1] >= half_flux: break newOrigin = ix + 1 mm = (distanceList[ix] - distanceList[ix + 1]) / (fluxList[ix] - fluxList[ix + 1]) bb = distanceList[ix] - mm * fluxList[ix] distanceToLeft = mm * half_flux + bb for ix in range(newOrigin, len(xPixList) - 1): if fluxList[ix] >= half_flux and fluxList[ix + 1] < half_flux: break mm = (distanceList[ix] - distanceList[ix + 1]) / (fluxList[ix] - fluxList[ix + 1]) bb = distanceList[ix] - mm * fluxList[ix] distanceToRight = mm * half_flux + bb self.assertTrue(numpy.abs(distanceToLeft + distanceToRight - fwhm) < 0.1 * fwhm)
def test_generic_camera_wrapper(self): """ Test that GalSimCameraWrapper wraps its methods as expected. This is mostly to catch changes in afw API. """ camera = camTestUtils.CameraWrapper().camera camera_wrapper = GalSimCameraWrapper(camera) obs_mjd = ObservationMetaData(mjd=60000.0) ra, dec = raDecFromAltAz(35.0, 112.0, obs_mjd) obs = ObservationMetaData(pointingRA=ra, pointingDec=dec, mjd=obs_mjd.mjd, rotSkyPos=22.4) rng = np.random.RandomState(8124) for detector in camera: name = detector.getName() bbox = camera[name].getBBox() bbox_wrapper = camera_wrapper.getBBox(name) self.assertEqual(bbox.getMinX(), bbox_wrapper.getMinX()) self.assertEqual(bbox.getMaxX(), bbox_wrapper.getMaxX()) self.assertEqual(bbox.getMinY(), bbox_wrapper.getMinY()) self.assertEqual(bbox.getMaxY(), bbox_wrapper.getMaxY()) center_point = camera[name].getCenter(FOCAL_PLANE) pixel_system = camera[name].makeCameraSys(PIXELS) center_pix = camera.transform(center_point, FOCAL_PLANE, pixel_system) center_pix_wrapper = camera_wrapper.getCenterPixel(name) self.assertEqual(center_pix.getX(), center_pix_wrapper.getX()) self.assertEqual(center_pix.getY(), center_pix_wrapper.getY()) pupil_system = camera[name].makeCameraSys(FIELD_ANGLE) center_pupil = camera.transform(center_point, FOCAL_PLANE, pupil_system) center_pupil_wrapper = camera_wrapper.getCenterPupil(name) self.assertEqual(center_pupil.getX(), center_pupil_wrapper.getX()) self.assertEqual(center_pupil.getY(), center_pupil_wrapper.getY()) corner_pupil_wrapper = camera_wrapper.getCornerPupilList(name) corner_point_list = camera[name].getCorners(FOCAL_PLANE) for point in corner_point_list: point_pupil = camera.transform(point, FOCAL_PLANE, pupil_system) dd_min = 1.0e10 for wrapper_point in corner_pupil_wrapper: dd = np.sqrt( (point_pupil.getX() - wrapper_point.getX())**2 + (point_pupil.getY() - wrapper_point.getY())**2) if dd < dd_min: dd_min = dd self.assertLess(dd_min, 1.0e-20) xpix_min = None xpix_max = None ypix_min = None ypix_max = None focal_to_tan_pix = camera[name].getTransform( FOCAL_PLANE, TAN_PIXELS) for point in corner_point_list: pixel_point = focal_to_tan_pix.applyForward(point) xx = pixel_point.getX() yy = pixel_point.getY() if xpix_min is None or xx < xpix_min: xpix_min = xx if ypix_min is None or yy < ypix_min: ypix_min = yy if xpix_max is None or xx > xpix_max: xpix_max = xx if ypix_max is None or yy > ypix_max: ypix_max = yy pix_bounds_wrapper = camera_wrapper.getTanPixelBounds(name) self.assertEqual(pix_bounds_wrapper[0], xpix_min) self.assertEqual(pix_bounds_wrapper[1], xpix_max) self.assertEqual(pix_bounds_wrapper[2], ypix_min) self.assertEqual(pix_bounds_wrapper[3], ypix_max) x_pup = rng.random_sample(10) * 0.005 - 0.01 y_pup = rng.random_sample(10) * 0.005 - 0.01 x_pix, y_pix = pixelCoordsFromPupilCoords(x_pup, y_pup, chipName=name, camera=camera) (x_pix_wrapper, y_pix_wrapper) = camera_wrapper.pixelCoordsFromPupilCoords( x_pup, y_pup, name, obs) nan_x = np.where(np.isnan(x_pix)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pix, x_pix_wrapper) np.testing.assert_array_equal(y_pix, y_pix_wrapper) x_pix = rng.random_sample(10) * 100.0 - 200.0 y_pix = rng.random_sample(10) * 100.0 - 200.0 x_pup, y_pup = pupilCoordsFromPixelCoords(x_pix, y_pix, chipName=name, camera=camera) (x_pup_wrapper, y_pup_wrapper) = camera_wrapper.pupilCoordsFromPixelCoords( x_pix, y_pix, name, obs) nan_x = np.where(np.isnan(x_pup)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pup, x_pup_wrapper) np.testing.assert_array_equal(y_pup, y_pup_wrapper) ra, dec = raDecFromPixelCoords(x_pix, y_pix, name, camera=camera, obs_metadata=obs) (ra_wrapper, dec_wrapper) = camera_wrapper.raDecFromPixelCoords( x_pix, y_pix, name, obs) nan_ra = np.where(np.isnan(ra)) self.assertEqual(len(nan_ra[0]), 0) np.testing.assert_array_equal(ra, ra_wrapper) np.testing.assert_array_equal(dec, dec_wrapper) ra, dec = _raDecFromPixelCoords(x_pix, y_pix, name, camera=camera, obs_metadata=obs) (ra_wrapper, dec_wrapper) = camera_wrapper._raDecFromPixelCoords( x_pix, y_pix, name, obs) nan_ra = np.where(np.isnan(ra)) self.assertEqual(len(nan_ra[0]), 0) np.testing.assert_array_equal(ra, ra_wrapper) np.testing.assert_array_equal(dec, dec_wrapper) ra = obs.pointingRA + (rng.random_sample(10) * 150.0 - 100.0) / 160.0 dec = obs.pointingDec + (rng.random_sample(10) * 150.0 - 100.0) / 160.0 x_pix, y_pix = pixelCoordsFromRaDec(ra, dec, chipName=name, camera=camera, obs_metadata=obs) (x_pix_wrapper, y_pix_wrapper) = camera_wrapper.pixelCoordsFromRaDec( ra, dec, chipName=name, obs_metadata=obs) nan_x = np.where(np.isnan(x_pix)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pix, x_pix_wrapper) np.testing.assert_array_equal(y_pix, y_pix_wrapper) ra = np.radians(ra) dec = np.radians(dec) x_pix, y_pix = _pixelCoordsFromRaDec(ra, dec, chipName=name, camera=camera, obs_metadata=obs) (x_pix_wrapper, y_pix_wrapper) = camera_wrapper._pixelCoordsFromRaDec( ra, dec, chipName=name, obs_metadata=obs) nan_x = np.where(np.isnan(x_pix)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pix, x_pix_wrapper) np.testing.assert_array_equal(y_pix, y_pix_wrapper) del camera
def check_placement(self, imageName, raList, decList, fwhmList, countList, gain, detector, camera, obs, epoch=2000.0): """ Read in a FITS image and a list of objects meant to be on that image. Verify that the objects were placed at the correct pixel by counting up all of the flux within 2 fwhm of each object's expected location and verifying it with the counts expected for that object. @param [in] imageName is the name of the FITS file to be read in @param [in] raList is a numpy array of the RA coordinates of the objects in the image (in radians) @param [in] decList is a numpy array of the Dec coordinates of the objects in the image (in radians) @param [in] fwhmList is a list of the Full Width at Half Maximum of each object in arcseconds @param [in] countList is a list of the counts expected for each object @param [in] gain is the gain of the detector (electrons per ADU) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. Raises an exception of the counts detected for each object differs from the expected amount by more than 3 sigma. """ im = afwImage.ImageF(imageName).getArray() activePixels = numpy.where(im>1.0e-10) # I know this seems backwards, but the way numpy handles arrays, # the first index is the row (i.e. the y coordinate) imXList = activePixels[1] imYList = activePixels[0] nameList = [detector.getName()]*len(raList) xPixList, yPixList = _pixelCoordsFromRaDec(raList, decList, chipNames=nameList, camera=camera, obs_metadata=obs, epoch=epoch) for rr, dd, xx, yy, fwhm, cc in \ zip(raList, decList, xPixList, yPixList, fwhmList, countList): countSigma = numpy.sqrt(cc/gain) imNameList = [detector.getName()]*len(imXList) raImList, decImList = _raDecFromPixelCoords(imXList, imYList, imNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians(haversine(raImList, decImList, rr, dd)) fluxArray = numpy.array( [im[imYList[ix]][imXList[ix]] \ for ix in range(len(distanceList)) \ if distanceList[ix]<2.0*fwhm] ) totalFlux = fluxArray.sum() self.assertTrue(numpy.abs(totalFlux-cc)<3.0*countSigma)
def tanWcsFromDetector(afwDetector, afwCamera, obs_metadata, epoch): """ Take an afw.cameraGeom detector and return a WCS which approximates the focal plane as perfectly flat (i.e. it ignores optical distortions that the telescope may impose on the image) @param [in] afwDetector is an instantiation of afw.cameraGeom's Detector class which characterizes the detector for which you wish to return th WCS @param [in] afwCamera is an instantiation of afw.cameraGeom's Camera class which characterizes the camera containing afwDetector @param [in] obs_metadata is an instantiation of ObservationMetaData characterizing the telescope's current pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured @param [out] tanWcs is an instantiation of afw.image's TanWcs class representing the WCS of the detector as if there were no optical distortions imposed by the telescope. """ xTanPixMin, xTanPixMax, \ yTanPixMin, yTanPixMax = _getTanPixelBounds(afwDetector, afwCamera) xPixList = [] yPixList = [] nameList = [] # dx and dy are set somewhat heuristically # setting them eqal to 0.1(max-min) lead to errors # on the order of 0.7 arcsec in the WCS dx = 0.5 * (xTanPixMax - xTanPixMin) dy = 0.5 * (yTanPixMax - yTanPixMin) for xx in np.arange(xTanPixMin, xTanPixMax + 0.5 * dx, dx): for yy in np.arange(yTanPixMin, yTanPixMax + 0.5 * dy, dy): xPixList.append(xx) yPixList.append(yy) nameList.append(afwDetector.getName()) raList, decList = _raDecFromPixelCoords(np.array(xPixList), np.array(yPixList), nameList, camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) crPix1, crPix2 = _pixelCoordsFromRaDec(obs_metadata._pointingRA, obs_metadata._pointingDec, chipName=afwDetector.getName(), camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) lonList, latList = _nativeLonLatFromPointing(raList, decList, obs_metadata._pointingRA, obs_metadata._pointingDec) # convert from native longitude and latitude to intermediate world coordinates # according to equations (12), (13), (54) and (55) of # # Calabretta and Greisen (2002), A&A 395, p. 1077 # radiusList = 180.0 / (np.tan(latList) * np.pi) uList = radiusList * np.sin(lonList) vList = -radiusList * np.cos(lonList) delta_xList = xPixList - crPix1 delta_yList = yPixList - crPix2 bVector = np.array([ (delta_xList * uList).sum(), (delta_yList * uList).sum(), (delta_xList * vList).sum(), (delta_yList * vList).sum() ]) offDiag = (delta_yList * delta_xList).sum() xsq = np.power(delta_xList, 2).sum() ysq = np.power(delta_yList, 2).sum() aMatrix = np.array([[xsq, offDiag, 0.0, 0.0], [offDiag, ysq, 0.0, 0.0], [0.0, 0.0, xsq, offDiag], [0.0, 0.0, offDiag, ysq]]) coeffs = np.linalg.solve(aMatrix, bVector) fitsHeader = dafBase.PropertyList() fitsHeader.set("RADESYS", "ICRS") fitsHeader.set("EQUINOX", epoch) fitsHeader.set("CRVAL1", obs_metadata.pointingRA) fitsHeader.set("CRVAL2", obs_metadata.pointingDec) fitsHeader.set("CRPIX1", crPix1 + 1) # the +1 is because LSST uses 0-indexed images fitsHeader.set("CRPIX2", crPix2 + 1) # FITS files use 1-indexed images fitsHeader.set("CTYPE1", "RA---TAN") fitsHeader.set("CTYPE2", "DEC--TAN") fitsHeader.setDouble("CD1_1", coeffs[0]) fitsHeader.setDouble("CD1_2", coeffs[1]) fitsHeader.setDouble("CD2_1", coeffs[2]) fitsHeader.setDouble("CD2_2", coeffs[3]) # 20 March 2017 # the 'try' block is required by the SWIG stack; # the 'except' block is required by the pybind11 stack. try: tanWcs = afwImage.cast_TanWcs(afwImage.makeWcs(fitsHeader)) except AttributeError: tanWcs = afwImage.makeWcs(fitsHeader) return tanWcs
def tanWcsFromDetector(afwDetector, afwCamera, obs_metadata, epoch): """ Take an afw.cameraGeom detector and return a WCS which approximates the focal plane as perfectly flat (i.e. it ignores optical distortions that the telescope may impose on the image) @param [in] afwDetector is an instantiation of afw.cameraGeom's Detector class which characterizes the detector for which you wish to return th WCS @param [in] afwCamera is an instantiation of afw.cameraGeom's Camera class which characterizes the camera containing afwDetector @param [in] obs_metadata is an instantiation of ObservationMetaData characterizing the telescope's current pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured @param [out] tanWcs is an instantiation of afw.image's TanWcs class representing the WCS of the detector as if there were no optical distortions imposed by the telescope. """ xTanPixMin, xTanPixMax, \ yTanPixMin, yTanPixMax = _getTanPixelBounds(afwDetector, afwCamera) xPixList = [] yPixList = [] nameList = [] # dx and dy are set somewhat heuristically # setting them eqal to 0.1(max-min) lead to errors # on the order of 0.7 arcsec in the WCS dx = 0.5*(xTanPixMax-xTanPixMin) dy = 0.5*(yTanPixMax-yTanPixMin) for xx in np.arange(xTanPixMin, xTanPixMax+0.5*dx, dx): for yy in np.arange(yTanPixMin, yTanPixMax+0.5*dy, dy): xPixList.append(xx) yPixList.append(yy) nameList.append(afwDetector.getName()) raList, decList = _raDecFromPixelCoords(np.array(xPixList), np.array(yPixList), nameList, camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) crPix1, crPix2 = _pixelCoordsFromRaDec(obs_metadata._pointingRA, obs_metadata._pointingDec, chipName=afwDetector.getName(), camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) lonList, latList = _nativeLonLatFromPointing(raList, decList, obs_metadata._pointingRA, obs_metadata._pointingDec) # convert from native longitude and latitude to intermediate world coordinates # according to equations (12), (13), (54) and (55) of # # Calabretta and Greisen (2002), A&A 395, p. 1077 # radiusList = 180.0/(np.tan(latList)*np.pi) uList = radiusList*np.sin(lonList) vList = -radiusList*np.cos(lonList) delta_xList = xPixList - crPix1 delta_yList = yPixList - crPix2 bVector = np.array([ (delta_xList*uList).sum(), (delta_yList*uList).sum(), (delta_xList*vList).sum(), (delta_yList*vList).sum() ]) offDiag = (delta_yList*delta_xList).sum() xsq = np.power(delta_xList, 2).sum() ysq = np.power(delta_yList, 2).sum() aMatrix = np.array([ [xsq, offDiag, 0.0, 0.0], [offDiag, ysq, 0.0, 0.0], [0.0, 0.0, xsq, offDiag], [0.0, 0.0, offDiag, ysq] ]) coeffs = np.linalg.solve(aMatrix, bVector) fitsHeader = dafBase.PropertyList() fitsHeader.set("RADESYS", "ICRS") fitsHeader.set("EQUINOX", epoch) fitsHeader.set("CRVAL1", obs_metadata.pointingRA) fitsHeader.set("CRVAL2", obs_metadata.pointingDec) fitsHeader.set("CRPIX1", crPix1+1) # the +1 is because LSST uses 0-indexed images fitsHeader.set("CRPIX2", crPix2+1) # FITS files use 1-indexed images fitsHeader.set("CTYPE1", "RA---TAN") fitsHeader.set("CTYPE2", "DEC--TAN") fitsHeader.setDouble("CD1_1", coeffs[0]) fitsHeader.setDouble("CD1_2", coeffs[1]) fitsHeader.setDouble("CD2_1", coeffs[2]) fitsHeader.setDouble("CD2_2", coeffs[3]) tanWcs = afwImage.cast_TanWcs(afwImage.makeWcs(fitsHeader)) return tanWcs
def testOutputWcsOfImage(self): """ Test that, when GalSim generates an image, in encodes the WCS in a way afw can read. This is done by creating an image,then reading it back in, getting its WCS, and comparing the pixel-to-sky conversion both for the read WCS and the original afw.cameraGeom.detector. Raise an exception if the median difference between the two is greater than 0.01 arcseconds. """ scratchDir = os.path.join(getPackageDir('sims_GalSimInterface'), 'tests', 'scratchSpace') catName = os.path.join(scratchDir, 'outputWcs_test_Catalog.dat') imageRoot = os.path.join(scratchDir, 'outputWcs_test_Image') dbFileName = os.path.join(scratchDir, 'outputWcs_test_InputCatalog.dat') baseDir = os.path.join(getPackageDir('sims_GalSimInterface'), 'tests', 'cameraData') camera = ReturnCamera(baseDir) detector = camera[0] detName = detector.getName() imageName = '%s_%s_u.fits' % (imageRoot, detName) nSamples = 3 rng = np.random.RandomState(42) pointingRaList = rng.random_sample(nSamples)*360.0 pointingDecList = rng.random_sample(nSamples)*180.0 - 90.0 rotSkyPosList = rng.random_sample(nSamples)*360.0 for raPointing, decPointing, rotSkyPos in \ zip(pointingRaList, pointingDecList, rotSkyPosList): obs = ObservationMetaData(pointingRA = raPointing, pointingDec = decPointing, boundType = 'circle', boundLength = 4.0, rotSkyPos = rotSkyPos, mjd = 49250.0) fwhm = 0.7 create_text_catalog(obs, dbFileName, np.array([3.0]), np.array([1.0])) db = outputWcsFileDBObj(dbFileName, runtable='test') cat = outputWcsCat(db, obs_metadata=obs) cat.camera = camera psf = SNRdocumentPSF(fwhm=fwhm) cat.setPSF(psf) cat.write_catalog(catName) cat.write_images(nameRoot=imageRoot) exposure = afwImage.ExposureD_readFits(imageName) wcs = exposure.getWcs() xxTestList = [] yyTestList = [] raImage = [] decImage = [] for xx in np.arange(0.0, 4001.0, 100.0): for yy in np.arange(0.0, 4001.0, 100.0): xxTestList.append(xx) yyTestList.append(yy) pt = afwGeom.Point2D(xx, yy) skyPt = wcs.pixelToSky(pt).getPosition() raImage.append(skyPt.getX()) decImage.append(skyPt.getY()) xxTestList = np.array(xxTestList) yyTestList = np.array(yyTestList) raImage = np.radians(np.array(raImage)) decImage = np.radians(np.array(decImage)) raControl, \ decControl = _raDecFromPixelCoords(xxTestList, yyTestList, [detector.getName()]*len(xxTestList), camera=camera, obs_metadata=obs, epoch=2000.0) errorList = arcsecFromRadians(haversine(raControl, decControl, raImage, decImage)) medianError = np.median(errorList) msg = 'medianError was %e' % medianError self.assertLess(medianError, 0.01, msg=msg) if os.path.exists(catName): os.unlink(catName) if os.path.exists(dbFileName): os.unlink(dbFileName) if os.path.exists(imageName): os.unlink(imageName)
def verify_fwhm(self, fileName, fwhm, detector, camera, obs, epoch=2000.0): """ Read in a FITS image with one object on it and verify that that object has the expected Full Width at Half Maximum. This is done by finding the brightest pixel in the image, and then drawing 1-dimensional profiles of the object centered on that pixel (but at different angles relative to the axes of the image). The code then walks along those profiles and keeps track of the distance between the two points at which the flux is half of the maximum. @param [in] fileName is the name of the FITS image @param [in] fwhm is the expected Full Width at Half Maximum in arcseconds @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. This method will raise an exception if the measured Full Width at Half Maximum deviates from the expected value by more than ten percent. """ im = afwImage.ImageF(fileName).getArray() maxFlux = im.max() self.assertGreater(maxFlux, 100.0) # make sure the image is not blank valid = np.where(im > 0.25*maxFlux) x_center = np.median(valid[1]) y_center = np.median(valid[0]) raMax, decMax = _raDecFromPixelCoords(x_center, y_center, [detector.getName()], camera=camera, obs_metadata=obs, epoch=epoch) half_flux = 0.5*maxFlux # only need to consider orientations between 0 and pi because the objects # will be circularly symmetric (and FWHM is a circularly symmetric measure, anyway) for theta in np.arange(0.0, np.pi, 0.3*np.pi): slope = np.tan(theta) if np.abs(slope < 1.0): xPixList = np.array([ix for ix in range(0, im.shape[1]) if int(slope*(ix-x_center) + y_center) >= 0 and int(slope*(ix-x_center)+y_center) < im.shape[0]]) yPixList = np.array([int(slope*(ix-x_center)+y_center) for ix in xPixList]) else: yPixList = np.array([iy for iy in range(0, im.shape[0]) if int((iy-y_center)/slope + x_center) >= 0 and int((iy-y_center)/slope + x_center) < im.shape[1]]) xPixList = np.array([int((iy-y_center)/slope + x_center) for iy in yPixList]) chipNameList = [detector.getName()]*len(xPixList) raList, decList = _raDecFromPixelCoords(xPixList, yPixList, chipNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians(haversine(raList, decList, raMax, decMax)) fluxList = np.array([im[iy][ix] for ix, iy in zip(xPixList, yPixList)]) distanceToLeft = None distanceToRight = None for ix in range(1, len(xPixList)): if fluxList[ix] < half_flux and fluxList[ix+1] >= half_flux: break newOrigin = ix+1 mm = (distanceList[ix]-distanceList[ix+1])/(fluxList[ix]-fluxList[ix+1]) bb = distanceList[ix] - mm * fluxList[ix] distanceToLeft = mm*half_flux + bb for ix in range(newOrigin, len(xPixList)-1): if fluxList[ix] >= half_flux and fluxList[ix+1] < half_flux: break mm = (distanceList[ix]-distanceList[ix+1])/(fluxList[ix]-fluxList[ix+1]) bb = distanceList[ix] - mm * fluxList[ix] distanceToRight = mm*half_flux + bb msg = "measured fwhm %e; expected fwhm %e; maxFlux %e; orientation %e pi\n" % \ (distanceToLeft+distanceToRight, fwhm, maxFlux, theta/np.pi) self.assertLess(np.abs(distanceToLeft+distanceToRight-fwhm), 0.1*fwhm, msg=msg)