def __init__(self, algoDir): """Initialize the Algorithm class. Algorithm used to solve the transport of intensity equation to get normal/ annular Zernike polynomials. Parameters ---------- algoDir : str Algorithm configuration directory. """ self.algoDir = algoDir self.algoParamFile = ParamReader() self._inst = Instrument("") # Show the calculation message based on this value # 0 means no message will be showed self.debugLevel = 0 # Image has the problem or not from the over-compensation self.caustic = False # Record the Zk coefficients in each outer-loop iteration # The actual total outer-loop iteration time is Num_of_outer_itr + 1 self.converge = np.array([]) # Current number of outer-loop iteration self.currentItr = 0 # Record the coefficients of normal/ annular Zernike polynomials after # z4 in unit of nm self.zer4UpNm = np.array([]) # Converged wavefront. self.wcomp = np.array([]) # Calculated wavefront in previous outer-loop iteration. self.West = np.array([]) # Converged Zk coefficients self.zcomp = np.array([]) # Calculated Zk coefficients in previous outer-loop iteration self.zc = np.array([]) # Padded mask for use at the offset planes self.pMask = None # Non-padded mask corresponding to aperture self.cMask = None # Change the dimension of mask for fft to use self.pMaskPad = None self.cMaskPad = None
def setUp(self): self.instDir = os.path.join(getConfigDir(), "cwfs", "instData") self.inst = Instrument(self.instDir) self.dimOfDonutOnSensor = 120 self.inst.config(CamType.LsstCam, self.dimOfDonutOnSensor, announcedDefocalDisInMm=1.5)
def __init__(self, instDir, algoDir): self.inst = Instrument(instDir) self.algo = Algorithm(algoDir) self.imgIntra = CompensableImage() self.imgExtra = CompensableImage() self.opticalModel = "" self.sizeInPix = 0
def __init__(self, algoDir): self.algoDir = algoDir self.algoParamFile = ParamReader() self._inst = Instrument("") # Show the calculation message based on this value # 0 means no message will be showed self.debugLevel = 0 # Image has the problem or not from the over-compensation self.caustic = False # Record the Zk coefficients in each outer-loop iteration # The actual total outer-loop iteration time is Num_of_outer_itr + 1 self.converge = np.array([]) # Current number of outer-loop iteration self.currentItr = 0 # Record the coefficients of normal/ annular Zernike polynomials after # z4 in unit of nm self.zer4UpNm = np.array([]) # Converged wavefront. self.wcomp = np.array([]) # Calculated wavefront in previous outer-loop iteration. self.West = np.array([]) # Converged Zk coefficients self.zcomp = np.array([]) # Calculated Zk coefficients in previous outer-loop iteration self.zc = np.array([]) # Padded mask for use at the offset planes self.mask_comp = None # Non-padded mask corresponding to aperture self.mask_pupil = None # Change the dimension of mask for fft to use self.mask_comp_pad = None self.mask_pupil_pad = None # Cache annular Zernike evaluations self._zk = None # Cache evaluations of X and Y annular Zernike gradients self._dzkdx = None self._dzkdy = None
def testDataAuxTelZWO(self): inst = Instrument(self.instDir) inst.config(CamType.AuxTelZWO, 160, announcedDefocalDisInMm=0.5) self.assertEqual(inst.getObscuration(), 0.3525) self.assertEqual(inst.getFocalLength(), 21.6) self.assertEqual(inst.getApertureDiameter(), 1.2) self.assertEqual(inst.getDefocalDisOffset(), 0.0205) self.assertEqual(inst.getCamPixelSize(), 15.2e-6) self.assertAlmostEqual(inst.calcSizeOfDonutExpected(), 74.92690058, places=7)
def setUp(self): # Get the path of module self.modulePath = getModulePath() # Define the instrument folder instruFolder = os.path.join(self.modulePath, "configData", "cwfs", "instruData") # Define the algorithm folder algoFolderPath = os.path.join(self.modulePath, "configData", "cwfs", "algo") # Define the instrument name instruName = "lsst" # Define the algorithm being used: "exp" or "fft" useAlgorithm = "fft" # Define the image folder and image names # Image data -- Don't know the final image format. # It is noted that image.readFile inuts is based on the txt file imageFolderPath = os.path.join(self.modulePath, "tests", "testData", "testImages", "LSST_NE_SN25") intra_image_name = "z11_0.25_intra.txt" extra_image_name = "z11_0.25_extra.txt" # Define fieldXY: [1.185, 1.185] or [0, 0] # This is the position of donut on the focal plane in degree fieldXY = [1.185, 1.185] # Define the optical model: "paraxial", "onAxis", "offAxis" self.opticalModel = "offAxis" # Image files Path intra_image_file = os.path.join(imageFolderPath, intra_image_name) extra_image_file = os.path.join(imageFolderPath, extra_image_name) # Theree is the difference between intra and extra images # I1: intra_focal images, I2: extra_focal Images # self.I1 = Image.Image() # self.I2 = Image.Image() self.I1 = CompensationImageDecorator() self.I2 = CompensationImageDecorator() self.I1.setImg(fieldXY, imageFile=intra_image_file, atype="intra") self.I2.setImg(fieldXY, imageFile=extra_image_file, atype="extra") self.inst = Instrument(instruFolder) self.inst.config(instruName, self.I1.sizeinPix)
def _runWep(self, imgIntraName, imgExtraName, offset, model): # Cut the donut image from input files centroidFindType = CentroidFindType.Otsu imgIntra = Image(centroidFindType=centroidFindType) imgExtra = Image(centroidFindType=centroidFindType) imgIntraPath = os.path.join(self.testImgDir, imgIntraName) imgExtraPath = os.path.join(self.testImgDir, imgExtraName) imgIntra.setImg(imageFile=imgIntraPath) imgExtra.setImg(imageFile=imgExtraPath) xIntra, yIntra, _ = imgIntra.getCenterAndR() imgIntraArray = imgIntra.getImg()[int(yIntra) - offset:int(yIntra) + offset, int(xIntra) - offset:int(xIntra) + offset, ] xExtra, yExtra, _ = imgExtra.getCenterAndR() imgExtraArray = imgExtra.getImg()[int(yExtra) - offset:int(yExtra) + offset, int(xExtra) - offset:int(xExtra) + offset, ] # Set the images fieldXY = (0, 0) imgCompIntra = CompensableImage(centroidFindType=centroidFindType) imgCompIntra.setImg(fieldXY, DefocalType.Intra, image=imgIntraArray) imgCompExtra = CompensableImage(centroidFindType=centroidFindType) imgCompExtra.setImg(fieldXY, DefocalType.Extra, image=imgExtraArray) # Calculate the wavefront error # Set the instrument instDir = os.path.join(getConfigDir(), "cwfs", "instData") instAuxTel = Instrument(instDir) instAuxTel.config(CamType.AuxTel, imgCompIntra.getImgSizeInPix(), announcedDefocalDisInMm=0.8) # Set the algorithm algoFolderPath = os.path.join(getConfigDir(), "cwfs", "algo") algoAuxTel = Algorithm(algoFolderPath) algoAuxTel.config("exp", instAuxTel) algoAuxTel.runIt(imgCompIntra, imgCompExtra, model) return algoAuxTel.getZer4UpInNm()
def setUp(self): # Get the path of module self.modulePath = getModulePath() # Define the image folder and image names # Image data -- Don't know the final image format. # It is noted that image.readFile inuts is based on the txt file imageFolderPath = os.path.join(self.modulePath, "tests", "testData", "testImages", "LSST_NE_SN25") intra_image_name = "z11_0.25_intra.txt" extra_image_name = "z11_0.25_extra.txt" # Define fieldXY: [1.185, 1.185] or [0, 0] # This is the position of donut on the focal plane in degree fieldXY = [1.185, 1.185] # Define the optical model: "paraxial", "onAxis", "offAxis" self.opticalModel = "offAxis" # Image files Path intra_image_file = os.path.join(imageFolderPath, intra_image_name) extra_image_file = os.path.join(imageFolderPath, extra_image_name) # Theree is the difference between intra and extra images # I1: intra_focal images, I2: extra_focal Images self.I1 = CompensableImage() self.I2 = CompensableImage() self.I1.setImg(fieldXY, DefocalType.Intra, imageFile=intra_image_file) self.I2.setImg(fieldXY, DefocalType.Extra, imageFile=extra_image_file) # Set up the instrument cwfsConfigDir = os.path.join(getConfigDir(), "cwfs") instDir = os.path.join(cwfsConfigDir, "instData") self.inst = Instrument(instDir) self.inst.config(CamType.LsstCam, self.I1.getImgSizeInPix(), announcedDefocalDisInMm=1.0) # Set up the algorithm algoDir = os.path.join(cwfsConfigDir, "algo") self.algoExp = Algorithm(algoDir) self.algoExp.config("exp", self.inst) self.algoFft = Algorithm(algoDir) self.algoFft.config("fft", self.inst)
def __init__(self, instruFolderPath, algoFolderPath): """ Initialize the wavefront estimator class. Arguments: instruFolderPath {[str]} -- Path to instrument directory. algoFolderPath {[str]} -- Path to algorithm directory. """ self.algo = Algorithm(algoFolderPath) self.inst = Instrument(instruFolderPath) self.ImgIntra = CompensationImageDecorator() self.ImgExtra = CompensationImageDecorator() self.opticalModel = "" self.sizeInPix = 0
def testMakeMasks(self): donutStamp = DonutStamp( self.testStamps[0], lsst.geom.SpherePoint(0.0, 0.0, lsst.geom.degrees), lsst.geom.Point2D(2047.5, 2001.5), DefocalType.Extra.value, "R22_S11", "LSSTCam", ) # Set up instrument instDataPath = os.path.join(getConfigDir(), "cwfs", "instData") inst = Instrument(instDataPath) donutWidth = 126 inst.config(CamType.LsstCam, donutWidth) # Check that masks are empty at start np.testing.assert_array_equal(np.empty(shape=(0, 0)), donutStamp.mask_comp.getArray()) np.testing.assert_array_equal(np.empty(shape=(0, 0)), donutStamp.mask_pupil.getArray()) # Check masks after creation donutStamp.makeMasks(inst, "offAxis", 0, 1) self.assertEqual(afwImage.MaskX, type(donutStamp.mask_comp)) self.assertEqual(afwImage.MaskX, type(donutStamp.mask_pupil)) self.assertDictEqual({ "BKGRD": 0, "DONUT": 1 }, donutStamp.mask_comp.getMaskPlaneDict()) self.assertDictEqual({ "BKGRD": 0, "DONUT": 1 }, donutStamp.mask_pupil.getMaskPlaneDict()) maskC = donutStamp.mask_comp.getArray() maskP = donutStamp.mask_pupil.getArray() # Donut should match self.assertEqual(np.shape(maskC), (126, 126)) self.assertEqual(np.shape(maskP), (126, 126)) # Make sure not just an empty array self.assertTrue(np.sum(maskC) > 0.0) self.assertTrue(np.sum(maskP) > 0.0) # Donut at center of focal plane should be symmetric np.testing.assert_array_equal(maskC[:63], maskC[-63:][::-1]) np.testing.assert_array_equal(maskP[:63], maskP[-63:][::-1])
def setUp(self): # Get the path of module modulePath = getModulePath() # Define the instrument folder instDir = os.path.join(getConfigDir(), "cwfs", "instData") # Define the instrument name dimOfDonutOnSensor = 120 self.inst = Instrument(instDir) self.inst.config(CamType.LsstCam, dimOfDonutOnSensor, announcedDefocalDisInMm=1.0) # Define the image folder and image names # Image data -- Don't know the final image format. # It is noted that image.readFile inuts is based on the txt file imageFolderPath = os.path.join(modulePath, "tests", "testData", "testImages", "LSST_NE_SN25") intra_image_name = "z11_0.25_intra.txt" extra_image_name = "z11_0.25_extra.txt" self.imgFilePathIntra = os.path.join(imageFolderPath, intra_image_name) self.imgFilePathExtra = os.path.join(imageFolderPath, extra_image_name) # This is the position of donut on the focal plane in degree self.fieldXY = (1.185, 1.185) # Define the optical model: "paraxial", "onAxis", "offAxis" self.opticalModel = "offAxis" # Get the true Zk zcAnsFilePath = os.path.join( modulePath, "tests", "testData", "testImages", "validation", "simulation", "LSST_NE_SN25_z11_0.25_exp.txt", ) self.zcCol = np.loadtxt(zcAnsFilePath) self.wfsImg = CompensableImage()
def __init__(self, instDir, algoDir): """Initialize the wavefront estimator class. Parameters ---------- instDir : str Path to instrument directory. algoDir : str Path to algorithm directory. """ self.inst = Instrument(instDir) self.algo = Algorithm(algoDir) self.imgIntra = CompensableImage() self.imgExtra = CompensableImage() self.opticalModel = "" self.sizeInPix = 0
def setUp(self): # Get the path of module modulePath = getModulePath() # Define the instrument folder instruFolder = os.path.join(modulePath, "configData", "cwfs", "instruData") # Define the instrument name instruName = "lsst" sensorSamples = 120 self.inst = Instrument(instruFolder) self.inst.config(instruName, sensorSamples) # Define the image folder and image names # Image data -- Don't know the final image format. # It is noted that image.readFile inuts is based on the txt file imageFolderPath = os.path.join(modulePath, "tests", "testData", "testImages", "LSST_NE_SN25") intra_image_name = "z11_0.25_intra.txt" extra_image_name = "z11_0.25_extra.txt" self.imgFilePathIntra = os.path.join(imageFolderPath, intra_image_name) self.imgFilePathExtra = os.path.join(imageFolderPath, extra_image_name) # Define fieldXY: [1.185, 1.185] or [0, 0] # This is the position of donut on the focal plane in degree self.fieldXY = [1.185, 1.185] # Define the optical model: "paraxial", "onAxis", "offAxis" self.opticalModel = "offAxis" # Get the true Zk zcAnsFilePath = os.path.join(modulePath, "tests", "testData", "testImages", "validation", "LSST_NE_SN25_z11_0.25_exp.txt") self.zcCol = np.loadtxt(zcAnsFilePath)
def _runWEP( self, instDir, algoFolderPath, useAlgorithm, imageFolderPath, intra_image_name, extra_image_name, fieldXY, opticalModel, showFig=False, ): # Image files Path intra_image_file = os.path.join(imageFolderPath, intra_image_name) extra_image_file = os.path.join(imageFolderPath, extra_image_name) # There is the difference between intra and extra images # I1: intra_focal images, I2: extra_focal Images I1 = CompensableImage() I2 = CompensableImage() I1.setImg(fieldXY, DefocalType.Intra, imageFile=intra_image_file) I2.setImg(fieldXY, DefocalType.Extra, imageFile=extra_image_file) # Set the instrument inst = Instrument(instDir) inst.config(CamType.LsstCam, I1.getImgSizeInPix(), announcedDefocalDisInMm=1.0) # Define the algorithm to be used. algo = Algorithm(algoFolderPath) algo.config(useAlgorithm, inst, debugLevel=0) # Plot the original wavefront images if showFig: plotImage(I1.image, title="intra image") plotImage(I2.image, title="extra image") # Run it algo.runIt(I1, I2, opticalModel, tol=1e-3) # Show the Zernikes Zn (n>=4) algo.outZer4Up(showPlot=False) # Plot the final conservated images and wavefront if showFig: plotImage(I1.image, title="Compensated intra image") plotImage(I2.image, title="Compensated extra image") # Plot the Wavefront plotImage(algo.wcomp, title="Final wavefront") plotImage( algo.wcomp, title="Final wavefront with pupil mask applied", mask=algo.pMask, ) # Return the Zernikes Zn (n>=4) return algo.getZer4UpInNm()
def calcWfErr( self, centroidFindType, fieldXY, camType, algoName, announcedDefocalDisInMm, opticalModel, imageIntra=None, imageExtra=None, imageFileIntra=None, imageFileExtra=None, ): """Calculate the wavefront error. Parameters ---------- centroidFindType : enum 'CentroidFindType' Algorithm to find the centroid of donut. fieldXY : tuple or list Position of donut on the focal plane in degree (field x, field y). camType : enum 'CamType' Camera type. algoName : str Algorithm configuration file to solve the Poisson's equation in the transport of intensity equation (TIE). It can be "fft" or "exp" here. announcedDefocalDisInMm : float Announced defocal distance in mm. It is noted that the defocal distance offset used in calculation might be different from this value. opticalModel : str Optical model. It can be "paraxial", "onAxis", or "offAxis". imageIntra : numpy.ndarray, optional Array of intra-focal image. (the default is None.) imageExtra : numpy.ndarray, optional Array of extra-focal image. (the default is None.) imageFileIntra : str, optional Path of intra-focal image file. (the default is None.) imageFileExtra : str, optional Path of extra-focal image file. (the default is None.) Returns ------- numpy.ndarray Zernike polynomials of z4-zn in nm. """ # Set the defocal images imgIntra = CompensableImage(centroidFindType=centroidFindType) imgExtra = CompensableImage(centroidFindType=centroidFindType) imgIntra.setImg( fieldXY, DefocalType.Intra, image=imageIntra, imageFile=imageFileIntra ) imgExtra.setImg( fieldXY, DefocalType.Extra, image=imageExtra, imageFile=imageFileExtra ) # Set the instrument instDir = os.path.join(getConfigDir(), "cwfs", "instData") inst = Instrument(instDir) inst.config( camType, imgIntra.getImgSizeInPix(), announcedDefocalDisInMm=announcedDefocalDisInMm, ) # Define the algorithm to be used. algoFolderPath = os.path.join(getConfigDir(), "cwfs", "algo") algo = Algorithm(algoFolderPath) algo.config(algoName, inst, debugLevel=0) # Run it algo.runIt(imgIntra, imgExtra, opticalModel, tol=1e-3) # Return the Zernikes Zn (n>=4) return algo.getZer4UpInNm()
def makeTemplate( self, sensorName, defocalType, imageSize, camType=CamType.LsstCam, opticalModel="offAxis", pixelScale=0.2, ): """Make the donut template image. Parameters ---------- sensorName : str The camera detector for which we want to make a template. Should be in "Rxx_Sxx" format. defocalType : enum 'DefocalType' The defocal state of the sensor. imageSize : int Size of template in pixels. The template will be a square. camType : enum 'CamType', optional Camera type. (Default is CamType.LsstCam) model : str, optional Optical model. It can be "paraxial", "onAxis", or "offAxis". (The default is "offAxis") pixelScale : float, optional The pixels to arcseconds conversion factor. (The default is 0.2) Returns ------- numpy.ndarray [int] The donut template as a binary image. """ configDir = getConfigDir() focalPlaneLayout = readPhoSimSettingData(configDir, "focalplanelayout.txt", "fieldCenter") pixelSizeInUm = float(focalPlaneLayout[sensorName][2]) sizeXinPixel = int(focalPlaneLayout[sensorName][3]) sensorXMicron, sensorYMicron = np.array( focalPlaneLayout[sensorName][:2], dtype=float) # Correction for wavefront sensors if sensorName in ("R44_S00_C0", "R00_S22_C1"): # Shift center to +x direction sensorXMicron = sensorXMicron + sizeXinPixel / 2 * pixelSizeInUm elif sensorName in ("R44_S00_C1", "R00_S22_C0"): # Shift center to -x direction sensorXMicron = sensorXMicron - sizeXinPixel / 2 * pixelSizeInUm elif sensorName in ("R04_S20_C1", "R40_S02_C0"): # Shift center to -y direction sensorYMicron = sensorYMicron - sizeXinPixel / 2 * pixelSizeInUm elif sensorName in ("R04_S20_C0", "R40_S02_C1"): # Shift center to +y direction sensorYMicron = sensorYMicron + sizeXinPixel / 2 * pixelSizeInUm # Load Instrument parameters instDir = os.path.join(configDir, "cwfs", "instData") inst = Instrument(instDir) inst.config(camType, imageSize) # Create image for mask img = CompensableImage() # Convert pixel locations to degrees sensorXPixel = float(sensorXMicron) / pixelSizeInUm sensorYPixel = float(sensorYMicron) / pixelSizeInUm # Multiply by pixelScale then divide by 3600 for arcsec -> deg conversion sensorXDeg = sensorXPixel * pixelScale / 3600 sensorYDeg = sensorYPixel * pixelScale / 3600 fieldXY = [sensorXDeg, sensorYDeg] # Define position of donut at center of current sensor in degrees boundaryT = 0 maskScalingFactorLocal = 1 img.setImg(fieldXY, defocalType, image=np.zeros((imageSize, imageSize))) img.makeMask(inst, opticalModel, boundaryT, maskScalingFactorLocal) return img.getNonPaddedMask()
def testInstrument(self): inst = Instrument(self.instruFolder) inst.config(self.instruName, 120) self.assertEqual(inst.parameter["sensorSamples"], 120)
def makeTemplate( self, sensorName, defocalType, imageSize, camType=CamType.LsstCam, opticalModel="offAxis", pixelScale=0.2, ): """Make the donut template image. Parameters ---------- sensorName : str The camera detector for which we want to make a template. Should be in "Rxx_Sxx" format. defocalType : enum 'DefocalType' The defocal state of the sensor. imageSize : int Size of template in pixels. The template will be a square. camType : enum 'CamType', optional Camera type. (The default is CamType.LsstCam) opticalModel : str, optional Optical model. It can be "paraxial", "onAxis", or "offAxis". (The default is "offAxis") pixelScale : float, optional The pixels to arcseconds conversion factor. (The default is 0.2) Returns ------- numpy.ndarray [int] The donut template as a binary image. Raises ------ ValueError Camera type is not supported. """ configDir = getConfigDir() # Load Instrument parameters instDir = os.path.join(configDir, "cwfs", "instData") inst = Instrument(instDir) if camType in (CamType.LsstCam, CamType.LsstFamCam, CamType.ComCam): inst.config(camType, imageSize) focalPlaneLayout = readPhoSimSettingData(configDir, "focalplanelayout.txt", "fieldCenter") pixelSizeInUm = float(focalPlaneLayout[sensorName][2]) sensorXMicron, sensorYMicron = np.array( focalPlaneLayout[sensorName][:2], dtype=float) elif camType == CamType.AuxTel: # AuxTel only works with onAxis sources if opticalModel != "onAxis": raise ValueError( str(f"Optical Model {opticalModel} not supported with AuxTel. " + "Must use 'onAxis'.")) # Defocal distance for Latiss in mm # for LsstCam can use the default # hence only need to set here announcedDefocalDisInMm = getDefocalDisInMm("auxTel") inst.config(camType, imageSize, announcedDefocalDisInMm) # load the info for auxTel pixelSizeInMeters = inst.getCamPixelSize() # pixel size in meters. pixelSizeInUm = pixelSizeInMeters * 1e6 camera = obs_lsst.Latiss.getCamera() sensorName = list( camera.getNameIter())[0] # only one detector in latiss detector = camera.get(sensorName) xp, yp = detector.getCenter( cameraGeom.FOCAL_PLANE) # center of CCD in mm # multiply by 1000 to for mm --> microns conversion sensorXMicron = yp * 1000 sensorYMicron = xp * 1000 else: raise ValueError("Camera type (%s) is not supported." % camType) # Create image for mask img = CompensableImage() # Convert pixel locations to degrees sensorXPixel = float(sensorXMicron) / pixelSizeInUm sensorYPixel = float(sensorYMicron) / pixelSizeInUm # Multiply by pixelScale then divide by 3600 for arcsec->deg conversion sensorXDeg = sensorXPixel * pixelScale / 3600 sensorYDeg = sensorYPixel * pixelScale / 3600 fieldXY = [sensorXDeg, sensorYDeg] # Define position of donut at center of current sensor in degrees boundaryT = 0 maskScalingFactorLocal = 1 img.setImg(fieldXY, defocalType, image=np.zeros((imageSize, imageSize))) img.makeMask(inst, opticalModel, boundaryT, maskScalingFactorLocal) return img.getNonPaddedMask()