Source code for astrophot.utils.initialize.variance

import numpy as np
from scipy.ndimage import gaussian_filter
from scipy.stats import binned_statistic
import torch
from ...errors import InvalidData
import matplotlib.pyplot as plt


[docs] def auto_variance(data, mask=None): if isinstance(data, torch.Tensor): data = data.detach().cpu().numpy() if isinstance(mask, torch.Tensor): mask = mask.detach().cpu().numpy() if mask is None: mask = np.zeros(data.shape, dtype=int) # Data too small for anything fancy var = np.var(data[np.logical_not(mask)]) if not np.isfinite(var) or var == 0: return np.ones_like(data) if min(data.shape) < 20: return np.ones_like(data) * var # Get approximation of noise in each pixel blur_data = gaussian_filter(data, 1.1)[4:-4, 4:-4] blur_mask = gaussian_filter(mask, 1.1)[4:-4, 4:-4] == 0 mask = np.logical_not(mask) residuals = (data[4:-4, 4:-4] - blur_data)[blur_mask] # Bin the residuals by flux, clip the tails clips = (np.quantile(data[mask], 0.01), np.quantile(data[mask], 0.99)) bins = np.linspace(clips[0], clips[1], 11) std, bins, _ = binned_statistic( data[4:-4, 4:-4][blur_mask].flatten(), residuals.flatten(), statistic="std", bins=bins, ) N = np.logical_not(np.isfinite(std)) if np.any(N): std[N] = np.sqrt(np.interp(bins[:-1][N], bins[:-1][~N], std[~N] ** 2)) # Fit a line to the variance p = np.polyfit(bins[:-3], std[:-2] ** 2, 1) # Check if the variance is increasing with flux if p[0] < 0: raise InvalidData( "Variance appears to be decreasing with flux! Cannot accurately estimate variance." ) # Compute the approximate variance map variance = np.clip(p[0] * data + p[1], np.min(std) ** 2, None) variance[np.logical_not(mask)] = np.inf return variance