def test_noisy_background(self): #Use a fixed random state seed, so unit-test is reproducible: rstate = numpy.random.RandomState(42) pixel_noise = 0.5 self.mygauss += rstate.normal(scale=pixel_noise, size=len(self.mygauss.ravel())).reshape( self.mygauss.shape) self.fit = fitgaussian(self.mygauss, self.moments) self.longMessage = True #Show assertion fail values + given message # First, let's check we've converged to a reasonable fit in the # presence of noise: # print for param in FIT_PARAMS: # print param, getattr(self,param), self.fit[param] self.assertAlmostEqual( getattr(self, param), self.fit[param], places=1, msg=param + " misfit (bad random noise seed?)", ) # Now we run the full error-profiling routine and check the chi-sq # calculations: self.fit_w_errs, _ = source_profile_and_errors( data=self.mygauss, threshold=0., noise=pixel_noise, beam=(self.semimajor, self.semiminor, self.theta), ) npix = len(self.mygauss.ravel()) # print "CHISQ", npix, self.fit_w_errs.chisq # NB: this is the calculation for reduced chisq in presence of # independent pixels, i.e. uncorrelated noise. # For real data, we try to take the noise-correlation into account. # print "Reduced chisq", self.fit_w_errs.chisq / npix self.assertTrue(0.9 < self.fit_w_errs.chisq / npix < 1.1)
def test_noisy_background(self): #Use a fixed random state seed, so unit-test is reproducible: rstate = numpy.random.RandomState(42) pixel_noise = 0.5 self.mygauss += rstate.normal(scale = pixel_noise, size=len(self.mygauss.ravel())).reshape(self.mygauss.shape) self.fit = fitgaussian(self.mygauss, self.moments) self.longMessage = True #Show assertion fail values + given message # First, let's check we've converged to a reasonable fit in the # presence of noise: # print for param in FIT_PARAMS: # print param, getattr(self,param), self.fit[param] self.assertAlmostEqual(getattr(self,param), self.fit[param], places=1, msg = param +" misfit (bad random noise seed?)", ) # Now we run the full error-profiling routine and check the chi-sq # calculations: self.fit_w_errs, _ = source_profile_and_errors( data = self.mygauss, threshold=0., noise=pixel_noise, beam = (self.semimajor, self.semiminor, self.theta), ) npix = len(self.mygauss.ravel()) # print "CHISQ", npix, self.fit_w_errs.chisq # NB: this is the calculation for reduced chisq in presence of # independent pixels, i.e. uncorrelated noise. # For real data, we try to take the noise-correlation into account. # print "Reduced chisq", self.fit_w_errs.chisq / npix self.assertTrue( 0.9 < self.fit_w_errs.chisq / npix < 1.1)
def fit_to_point(self, x, y, boxsize, threshold, fixed): """Fit an elliptical Gaussian to a specified point on the image. The fit is carried on a square section of the image, of length *boxsize* & centred at pixel coordinates *x*, *y*. Any data below *threshold* * rmsmap is not used for fitting. If *fixed* is set to ``position``, then the pixel coordinates are fixed in the fit. Returns an instance of :class:`tkp.sourcefinder.extract.Detection`. """ if (( # Recent NumPy hasattr(numpy.ma.core, "MaskedConstant") and isinstance(self.rmsmap, numpy.ma.core.MaskedConstant) ) or ( # Old NumPy numpy.ma.is_masked(self.rmsmap[x, y]) )): logger.error("Background is masked: cannot fit") return None chunk = ImageData.box_slice_about_pixel(x, y, boxsize/2.0) if threshold is not None: # We'll mask out anything below threshold*self.rmsmap from the fit. labels, num = self.labels.setdefault( #Dictionary mapping threshold -> islands map threshold, ndimage.label( self.clip.setdefault( #Dictionary mapping threshold -> mask threshold, numpy.where( self.data_bgsubbed > threshold * self.rmsmap, 1, 0 ) ) ) ) mylabel = labels[x, y] if mylabel == 0: # 'Background' raise ValueError("Fit region is below specified threshold, fit aborted.") mask = numpy.where(labels[chunk] == mylabel, 0, 1) fitme = numpy.ma.array(self.data_bgsubbed[chunk], mask=mask) if len(fitme.compressed()) < 1: raise IndexError("Fit region too close to edge or too small") else: fitme = self.data_bgsubbed[chunk] if fitme.size < 1: raise IndexError("Fit region too close to edge or too small") if not len(fitme.compressed()): logger.error("All data is masked: cannot fit") return None # set argument for fixed parameters based on input string if fixed == 'position': fixed = {'xbar': boxsize/2.0, 'ybar': boxsize/2.0} elif fixed == 'position+shape': fixed = {'xbar': boxsize/2.0, 'ybar': boxsize/2.0, 'semimajor': self.beam[0], 'semiminor': self.beam[1], 'theta': self.beam[2]} elif fixed == None: fixed = {} else: raise TypeError("Unkown fixed parameter") if threshold is not None: threshold_at_pixel = threshold * self.rmsmap[x, y] else: threshold_at_pixel = None try: measurement, residuals = extract.source_profile_and_errors( fitme, threshold_at_pixel, self.rmsmap[x, y], self.beam, fixed=fixed ) except ValueError: # Fit failed to converge # Moments are not applicable when holding parameters fixed logger.error("Gaussian fit failed at %f, %f", x, y) return None try: assert(abs(measurement['xbar']) < boxsize) assert(abs(measurement['ybar']) < boxsize) except AssertionError: logger.warn('Fit falls outside of box.') measurement['xbar'] += x-boxsize/2.0 measurement['ybar'] += y-boxsize/2.0 measurement.sig = (fitme / self.rmsmap[chunk]).max() return extract.Detection(measurement, self)
def fit_to_point(self, x, y, boxsize, threshold, fixed): """Fit an elliptical Gaussian to a specified point on the image. The fit is carried on a square section of the image, of length *boxsize* & centred at pixel coordinates *x*, *y*. Any data below *threshold* * rmsmap is not used for fitting. If *fixed* is set to ``position``, then the pixel coordinates are fixed in the fit. Returns an instance of :class:`tkp.sourcefinder.extract.Detection`. """ logger.debug("Force-fitting pixel location ({},{})".format(x, y)) # First, check that x and y are actually valid semi-positive integers. # Otherwise, # If they are too high (positive), then indexing will fail # BUT, if they are negative, then we get wrap-around indexing # and the fit continues at the wrong position! if (x < 0 or x >self.xdim or y < 0 or y >self.ydim ): logger.warning("Dropping forced fit at ({},{}), " "pixel position outside image".format(x,y) ) return None # Next, check if any of the central pixels (in a 3x3 box about the # fitted pixel position) have been Masked # (e.g. if NaNs, or close to image edge) - reject if so. central_pixels_slice = ImageData.box_slice_about_pixel(x, y, 1) if self.data.mask[central_pixels_slice].any(): logger.warning( "Dropping forced fit at ({},{}), " "Masked pixel in central fitting region".format(x,y)) return None if (( # Recent NumPy hasattr(numpy.ma.core, "MaskedConstant") and isinstance(self.rmsmap, numpy.ma.core.MaskedConstant) ) or ( # Old NumPy numpy.ma.is_masked(self.rmsmap[int(x), int(y)]) )): logger.error("Background is masked: cannot fit") return None chunk = ImageData.box_slice_about_pixel(x, y, boxsize/2.0) if threshold is not None: # We'll mask out anything below threshold*self.rmsmap from the fit. labels, num = self.labels.setdefault( #Dictionary mapping threshold -> islands map threshold, ndimage.label( self.clip.setdefault( #Dictionary mapping threshold -> mask threshold, numpy.where( self.data_bgsubbed > threshold * self.rmsmap, 1, 0 ) ) ) ) mylabel = labels[int(x), int(y)] if mylabel == 0: # 'Background' raise ValueError("Fit region is below specified threshold, fit aborted.") mask = numpy.where(labels[chunk] == mylabel, 0, 1) fitme = numpy.ma.array(self.data_bgsubbed[chunk], mask=mask) if len(fitme.compressed()) < 1: raise IndexError("Fit region too close to edge or too small") else: fitme = self.data_bgsubbed[chunk] if fitme.size < 1: raise IndexError("Fit region too close to edge or too small") if not len(fitme.compressed()): logger.error("All data is masked: cannot fit") return None # set argument for fixed parameters based on input string if fixed == 'position': fixed = {'xbar': boxsize/2.0, 'ybar': boxsize/2.0} elif fixed == 'position+shape': fixed = {'xbar': boxsize/2.0, 'ybar': boxsize/2.0, 'semimajor': self.beam[0], 'semiminor': self.beam[1], 'theta': self.beam[2]} elif fixed == None: fixed = {} else: raise TypeError("Unkown fixed parameter") if threshold is not None: threshold_at_pixel = threshold * self.rmsmap[int(x), int(y)] else: threshold_at_pixel = None try: measurement, residuals = extract.source_profile_and_errors( fitme, threshold_at_pixel, self.rmsmap[int(x), int(y)], self.beam, fixed=fixed ) except ValueError: # Fit failed to converge # Moments are not applicable when holding parameters fixed logger.error("Gaussian fit failed at %f, %f", x, y) return None try: assert(abs(measurement['xbar']) < boxsize) assert(abs(measurement['ybar']) < boxsize) except AssertionError: logger.warn('Fit falls outside of box.') measurement['xbar'] += x-boxsize/2.0 measurement['ybar'] += y-boxsize/2.0 measurement.sig = (fitme / self.rmsmap[chunk]).max() return extract.Detection(measurement, self)
def fit_to_point(self, x, y, boxsize, threshold, fixed): """Fit an elliptical Gaussian to a specified point on the image. The fit is carried on a square section of the image, of length *boxsize* & centred at pixel coordinates *x*, *y*. Any data below *threshold* * rmsmap is not used for fitting. If *fixed* is set to ``position``, then the pixel coordinates are fixed in the fit. Returns an instance of :class:`tkp.sourcefinder.extract.Detection`. """ if (( # Recent NumPy hasattr(numpy.ma.core, "MaskedConstant") and isinstance(self.rmsmap, numpy.ma.core.MaskedConstant)) or ( # Old NumPy numpy.ma.is_masked(self.rmsmap[x, y]))): logger.error("Background is masked: cannot fit") return None chunk = ImageData.box_slice_about_pixel(x, y, boxsize / 2.0) if threshold is not None: # We'll mask out anything below threshold*self.rmsmap from the fit. labels, num = self.labels.setdefault( #Dictionary mapping threshold -> islands map threshold, ndimage.label( self.clip. setdefault( #Dictionary mapping threshold -> mask threshold, numpy.where( self.data_bgsubbed > threshold * self.rmsmap, 1, 0)))) mylabel = labels[x, y] if mylabel == 0: # 'Background' raise ValueError( "Fit region is below specified threshold, fit aborted.") mask = numpy.where(labels[chunk] == mylabel, 0, 1) fitme = numpy.ma.array(self.data_bgsubbed[chunk], mask=mask) if len(fitme.compressed()) < 1: raise IndexError("Fit region too close to edge or too small") else: fitme = self.data_bgsubbed[chunk] if fitme.size < 1: raise IndexError("Fit region too close to edge or too small") if not len(fitme.compressed()): logger.error("All data is masked: cannot fit") return None # set argument for fixed parameters based on input string if fixed == 'position': fixed = {'xbar': boxsize / 2.0, 'ybar': boxsize / 2.0} elif fixed == 'position+shape': fixed = { 'xbar': boxsize / 2.0, 'ybar': boxsize / 2.0, 'semimajor': self.beam[0], 'semiminor': self.beam[1], 'theta': self.beam[2] } elif fixed == None: fixed = {} else: raise TypeError("Unkown fixed parameter") if threshold is not None: threshold_at_pixel = threshold * self.rmsmap[x, y] else: threshold_at_pixel = None try: measurement, residuals = extract.source_profile_and_errors( fitme, threshold_at_pixel, self.rmsmap[x, y], self.beam, fixed=fixed) except ValueError: # Fit failed to converge # Moments are not applicable when holding parameters fixed logger.error("Gaussian fit failed at %f, %f", x, y) return None try: assert (abs(measurement['xbar']) < boxsize) assert (abs(measurement['ybar']) < boxsize) except AssertionError: logger.warn('Fit falls outside of box.') measurement['xbar'] += x - boxsize / 2.0 measurement['ybar'] += y - boxsize / 2.0 measurement.sig = (fitme / self.rmsmap[chunk]).max() return extract.Detection(measurement, self)
def fit_to_point(self, x, y, boxsize, threshold, fixed): """Fit an elliptical Gaussian to a specified point on the image. The fit is carried on a square section of the image, of length *boxsize* & centred at pixel coordinates *x*, *y*. Any data below *threshold* * rmsmap is not used for fitting. If *fixed* is set to ``position``, then the pixel coordinates are fixed in the fit. Returns an instance of :class:`tkp.sourcefinder.extract.Detection`. """ logger.debug("Force-fitting pixel location ({},{})".format(x, y)) # First, check that x and y are actually valid semi-positive integers. # Otherwise, # If they are too high (positive), then indexing will fail # BUT, if they are negative, then we get wrap-around indexing # and the fit continues at the wrong position! if (x < 0 or x > self.xdim or y < 0 or y > self.ydim): logger.warning("Dropping forced fit at ({},{}), " "pixel position outside image".format(x, y)) return None # Next, check if any of the central pixels (in a 3x3 box about the # fitted pixel position) have been Masked # (e.g. if NaNs, or close to image edge) - reject if so. central_pixels_slice = ImageData.box_slice_about_pixel(x, y, 1) if self.data.mask[central_pixels_slice].any(): logger.warning("Dropping forced fit at ({},{}), " "Masked pixel in central fitting region".format( x, y)) return None if (( # Recent NumPy hasattr(numpy.ma.core, "MaskedConstant") and isinstance(self.rmsmap, numpy.ma.core.MaskedConstant)) or ( # Old NumPy numpy.ma.is_masked(self.rmsmap[int(x), int(y)]))): logger.error("Background is masked: cannot fit") return None chunk = ImageData.box_slice_about_pixel(x, y, boxsize / 2.0) if threshold is not None: # We'll mask out anything below threshold*self.rmsmap from the fit. labels, num = self.labels.setdefault( #Dictionary mapping threshold -> islands map threshold, ndimage.label( self.clip. setdefault( #Dictionary mapping threshold -> mask threshold, numpy.where( self.data_bgsubbed > threshold * self.rmsmap, 1, 0)))) mylabel = labels[int(x), int(y)] if mylabel == 0: # 'Background' raise ValueError( "Fit region is below specified threshold, fit aborted.") mask = numpy.where(labels[chunk] == mylabel, 0, 1) fitme = numpy.ma.array(self.data_bgsubbed[chunk], mask=mask) if len(fitme.compressed()) < 1: raise IndexError("Fit region too close to edge or too small") else: fitme = self.data_bgsubbed[chunk] if fitme.size < 1: raise IndexError("Fit region too close to edge or too small") if not len(fitme.compressed()): logger.error("All data is masked: cannot fit") return None # set argument for fixed parameters based on input string if fixed == 'position': fixed = {'xbar': boxsize / 2.0, 'ybar': boxsize / 2.0} elif fixed == 'position+shape': fixed = { 'xbar': boxsize / 2.0, 'ybar': boxsize / 2.0, 'semimajor': self.beam[0], 'semiminor': self.beam[1], 'theta': self.beam[2] } elif fixed == None: fixed = {} else: raise TypeError("Unkown fixed parameter") if threshold is not None: threshold_at_pixel = threshold * self.rmsmap[int(x), int(y)] else: threshold_at_pixel = None try: measurement, residuals = extract.source_profile_and_errors( fitme, threshold_at_pixel, self.rmsmap[int(x), int(y)], self.beam, fixed=fixed) except ValueError: # Fit failed to converge # Moments are not applicable when holding parameters fixed logger.error("Gaussian fit failed at %f, %f", x, y) return None try: assert (abs(measurement['xbar']) < boxsize) assert (abs(measurement['ybar']) < boxsize) except AssertionError: logger.warn('Fit falls outside of box.') measurement['xbar'] += x - boxsize / 2.0 measurement['ybar'] += y - boxsize / 2.0 measurement.sig = (fitme / self.rmsmap[chunk]).max() return extract.Detection(measurement, self)