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:
$$\sigma \approx \frac{\text{mean(shape)}}{40 \cdot b}$$
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:
1import torch
2import matplotlib.pyplot as plt
3from model import UNetGeneratorWithMask # Ensure model.py is in your directory
4
5# 1. Load the pre-trained generator
6device = "cuda" if torch.cuda.is_available() else "cpu"
7model = UNetGeneratorWithMask.from_pretrained("UW-PMEAL-Lab/porous-image-reconstruction")
8model.to(device)
9model.eval()
10
11# 2. Prepare a dummy input (Batch Size 1, 2 Channels, 128x128)
12# In practice, replace this with your actual incomplete image and mask
13incomplete_image = torch.zeros((1, 1, 128, 128), device=device)
14mask = torch.zeros((1, 1, 128, 128), device=device)
15mask[:, :, 0:26, 0:26] = 1.0 # Example: 26x26 known region
16
17x_cond = torch.cat([incomplete_image, mask], dim=1)
18
19# 3. Generate a latent noise vector (z)
20# Change the seed or sample a new vector to get a different reconstruction
21z = torch.randn(1, 8, device=device)
22
23# 4. Generate the reconstruction
24with torch.no_grad():
25 # Get the raw grayscale/soft output
26 reconstruction_soft = model(x_cond, z=z)
27
28 # Convert to strict binary (0.0 or 1.0)
29 reconstruction_bin = (reconstruction_soft > 0.5).float()
30