Skip to content

Poisson-Gaussian Contamination Mixture Examples

This guide demonstrates how to model data affected by both count (Poisson/shot) noise and continuous (Gaussian/readout) noise using PoissonGaussianContaminationMixtureLoss and EnhancedPoissonGaussianContaminationMixtureLoss.

→ API: PoissonGaussianContaminationMixtureLoss.

# Reference
1 Foi, A., Trimeche, M., Katkovnik, V., & Egiazarian, K. (2008). Practical Poisson-Gaussian noise modeling and sensor calibration in single-image photometry. IEEE Transactions on Image Processing.
2 Luisier, F., Blu, T., & Unser, M. (2011). Image denoising in mixed Poisson–Gaussian noise. IEEE Transactions on Image Processing.

Mathematical Formulations

The data in this example are generated by the detector model \(Y = Z + E\) with \(Z \sim \text{Poisson}(\lambda)\) (shot noise) and \(E \sim \mathcal{N}(0, \sigma^2)\) (read noise). Its likelihood is the convolution \(\sum_k \text{Pois}(k;\lambda)\,\mathcal{N}(y; k, \sigma^2)\), implemented by PoissonGaussianLoss.

The two losses demonstrated below are two-component mixtures, not that convolution, and are biased at low counts (see the comparison).

Poisson-Gaussian Contamination Mixture Loss

\[L(y, \lambda) = -\log\big(w\,\text{Pois}(y; \lambda) + (1-w)\,\mathcal{N}(y; \lambda, \sigma^2)\big)\]

Enhanced Poisson-Gaussian Contamination Mixture Loss

With \(\mu = g\lambda + b\):

\[L(y, \lambda) = -\log\big(\tfrac12\,\text{Pois}(y; \mu) + \tfrac12\,\mathcal{N}(y; \mu, \sigma_1^2 + \sigma_2^2 \mu)\big)\]

where \(g\) is the gain, \(b\) the offset, \(\sigma_1^2\) a constant variance and \(\sigma_2^2\) a signal-dependent variance coefficient.


Code Example

Below is a complete example showing how to train a model predicting a latent rate parameter under mixed noise.

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torchregress.losses import PoissonGaussianContaminationMixtureLoss, EnhancedPoissonGaussianContaminationMixtureLoss

# Generate synthetic Poisson-Gaussian noise data
np.random.seed(42)
n_samples = 1000
sigma = 0.6

x = np.random.uniform(-1.5, 1.5, size=(n_samples, 1)).astype(np.float32)
true_rate = np.exp(0.8 * x + 1.2).astype(np.float32)

# Count shot noise + Gaussian readout noise
shot = np.random.poisson(true_rate).astype(np.float32)
readout = np.random.normal(0, sigma, size=shot.shape).astype(np.float32)
y = shot + readout

x_tensor = torch.from_numpy(x)
y_tensor = torch.from_numpy(y)

# Neural network predicting latent rate
class SignalModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(1, 32),
            nn.ReLU(),
            nn.Linear(32, 1)
        )
    def forward(self, x):
        return self.net(x)

# Train with learnable Gaussian variance
model = SignalModel()
optimizer = optim.Adam(model.parameters(), lr=0.01)
loss_fn = PoissonGaussianContaminationMixtureLoss(learn_variance=True, log_input=False)

for epoch in range(150):
    rate_pred = torch.exp(model(x_tensor))
    loss = loss_fn(rate_pred, y_tensor)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print("Poisson-Gaussian mixture model trained successfully.")