Porous Media Image Reconstruction GAN
Model Description
This model is a conditional Generative Adversarial Network (GAN) designed to reconstruct 2D images of porous media. It utilizes a custom U-Net Generator with FiLM (Feature-wise Linear Modulation) conditioning to generate realistic 128x128 grayscale microstructures from incomplete or masked input data.
This model was developed by the Porous Materials Engineering & Analysis Lab (UW-PMEAL-Lab).
- Model type: Conditional GAN (U-Net Generator)
- Resolution: 128 x 128 pixels (Grayscale)
- Input: 2-channel tensor (Channel 0: Incomplete Image, Channel 1: Binary Mask)
- Latent Space: 8-dimensional noise vector (
z) for generating diverse reconstructions of the unknown regions.
Training Data: Synthetic Image Generation
The training and validation images used in this project were synthetically generated using the porespy.generators.blobs function. This function creates amorphous, continuous porous structures by applying a Gaussian blur to random noise fields, then thresholding to achieve a target porosity.
Parameter Selection Rationale
- Porosity (φ): The porosity of a single material generally varies only slightly between samples due to manufacturing or natural heterogeneity. For a realistic range, we sample porosity from a truncated normal distribution centered at the material’s measured mean, with a standard deviation of ±0.02 (≈ ±4 percentage points). This captures natural sample-to-sample variability without changing the material class.
- Blobiness (b): Blobiness in PoreSpy controls feature scale according to:
where a larger b produces smaller, more numerous blobs. For a single material, it is reasonable to vary b by about ±20–30 % (coefficient of variation ≈ 0.25), which changes local texture without altering the global morphology type.
Supporting Evidence
These parameter ranges were guided by the study:
Ávila et al., Evaluation of geometric tortuosity for 3D digitally generated porous media considering the pore size distribution and the A-star algorithm, Scientific Reports, 12, 19824 (2022). https://doi.org/10.1038/s41598-022-23643-6
That work systematically explored porosity = 0.45 – 0.95 and blobiness = 0.5 – 1.0 for digitally generated porous structures, establishing a broad morphology envelope. For this project, the ranges were narrowed to represent the intra-material variability of a single porous medium rather than cross-material diversity.
Summary of Generation Strategy
| Parameter | Distribution | Typical Range | Purpose |
|---|---|---|---|
| Porosity (φ) | Truncated Normal | Mean ± 0.02 | Controls pore fraction |
| Blobiness (b) | Lognormal (CV ≈ 0.25) | Center × [0.7 – 1.3] | Controls feature size / texture |
| Periodic | True | – | Enables seamless tiling |
| Shape | (128, 128) | – | Matches GAN input size |
These controlled variations produce realistic training and validation images that remain representative of a single porous material while providing enough diversity for model learning.
Intended Uses & Limitations
- Intended Use: Reconstructing missing or unobserved regions in 2D cross-sections of porous materials. Generating diverse potential structures for a single masked input to analyze uncertainty.
- Limitations: The model is specifically trained on 128x128 patches generated via the method described above. Inputs of drastically different sizes, real-world artifacts (like microscopy noise), or morphological properties not present in the training distribution may yield poor results.
How to Use
Because this model uses a custom architecture, you must include the model.py file from this repository in your project to load the weights.
First, download model.py from the Files tab, then run the following code:
import torch
import matplotlib.pyplot as plt
from model import UNetGeneratorWithMask # Ensure model.py is in your directory
# 1. Load the pre-trained generator
device = "cuda" if torch.cuda.is_available() else "cpu"
model = UNetGeneratorWithMask.from_pretrained("UW-PMEAL-Lab/porous-image-reconstruction")
model.to(device)
model.eval()
# 2. Prepare a dummy input (Batch Size 1, 2 Channels, 128x128)
# In practice, replace this with your actual incomplete image and mask
incomplete_image = torch.zeros((1, 1, 128, 128), device=device)
mask = torch.zeros((1, 1, 128, 128), device=device)
mask[:, :, 0:26, 0:26] = 1.0 # Example: 26x26 known region
x_cond = torch.cat([incomplete_image, mask], dim=1)
# 3. Generate a latent noise vector (z)
# Change the seed or sample a new vector to get a different reconstruction
z = torch.randn(1, 8, device=device)
# 4. Generate the reconstruction
with torch.no_grad():
# Get the raw grayscale/soft output
reconstruction_soft = model(x_cond, z=z)
# Convert to strict binary (0.0 or 1.0)
reconstruction_bin = (reconstruction_soft > 0.5).float()