Hybrid Lens Design
Design and simulation of hybrid refractive-diffractive optical systems.
Overview
Hybrid lenses combine:
- Refractive elements: Traditional glass lenses
- Diffractive elements: DOEs, metasurfaces, diffractive optics
Benefits:
- Chromatic correction: Diffractive elements have opposite dispersion
- Compact form factor: Thin diffractive elements replace thick glass
- Novel functionalities: Wavefront shaping, multi-focal, etc.
Related Paper
Congli Wang, Ni Chen, and Wolfgang Heidrich, "dflens: A Differentiable Pipeline for Hybrid Refractive-Diffractive Lens Design," SIGGRAPH Asia 2024.
Basic Hybrid Lens
Creating a Hybrid System
import torch
from deeplens.optics import HybridLens
# HybridLens consists of a GeoLens (refractive) and a DOE (diffractive)
# The easiest way is to load from a JSON file that contains both:
lens = HybridLens(
filename='./datasets/lenses/hybrid/hybridlens_example.json',
device='cuda'
)
# The HybridLens has two main components:
# - lens.geolens: The refractive lens (GeoLens)
# - lens.doe: The diffractive optical element (DOE)
# For accurate wave optics simulation, use double precision
torch.set_default_dtype(torch.float64)
lens.double()
# Access refractive lens properties (via embedded GeoLens)
print(f"Focal length: {lens.geolens.foclen}")
print(f"Sensor size: {lens.geolens.sensor_size}")
# The DOE can be: Binary2, Pixel2D, Fresnel, or Zernike type
print(f"DOE type: {type(lens.doe)}")
Ray-Wave Hybrid Tracing
import torch
from deeplens.optics.config import PSF_KS, DEFAULT_WAVE, SPP_COHERENT
# Ensure double precision for accurate phase computation
torch.set_default_dtype(torch.float64)
# HybridLens uses a ray-wave model:
# 1. Coherent ray tracing through refractive elements to DOE plane
# 2. DOE phase modulation
# 3. Wave propagation (Angular Spectrum Method) to sensor
# Compute PSF using the psf() method
point = [0.0, 0.0, -10000.0] # Point source position [x, y, z]
psf = lens.psf(
points=point, # Point source location
ks=PSF_KS, # Kernel size (128 default)
wvln=DEFAULT_WAVE, # Wavelength (0.58756180 um)
spp=SPP_COHERENT # Rays for coherent tracing (~16.7M)
)
# PSF is normalized intensity distribution, shape [ks, ks]
print(f"PSF shape: {psf.shape}")
print(f"PSF sum: {psf.sum():.4f}") # Should be ~1.0
Metasurface Design
Pixelated Metasurface
from deeplens.optics.diffractive_surface import Pixel2D
# Pixel2D DOE allows arbitrary phase patterns
# Parameters are typically loaded from JSON or optimized during training
# Example: create a Pixel2D DOE with focusing phase
import torch
import math
res = 512 # DOE resolution
ps = 0.008 # Pixel size in mm (8 um)
wvln = 0.550 # Design wavelength in um
foclen = 50.0 # Focal length in mm
# Create focusing phase profile
x = torch.linspace(-res//2, res//2-1, res) * ps
y = torch.linspace(-res//2, res//2-1, res) * ps
xx, yy = torch.meshgrid(x, y, indexing='xy')
r2 = xx**2 + yy**2
# Focusing phase: φ = -π * r^2 / (λ * f)
phase = -math.pi * r2 / (wvln * 1e-3 * foclen)
phase = phase % (2 * math.pi) # Wrap to [0, 2π]
# Create DOE (see deeplens/optics/diffractive_surface/ for details)
# In practice, DOEs are defined in JSON files and loaded with HybridLens
Zernike-Based DOE
from deeplens.optics.diffractive_surface import Zernike
# Zernike DOE uses Zernike polynomial coefficients to define phase
# Useful for correcting specific aberrations
# DOE parameters are typically defined in JSON:
# {
# "param_model": "zernike",
# "d": 45.0, # DOE position (mm)
# "h": 4.0, # DOE height (mm)
# "w": 4.0, # DOE width (mm)
# "res": [512, 512], # DOE resolution
# "coef": [0, 0, 0, -1.0, 0, 0, 0, 0, 0, 0.5] # Zernike coefficients
# }
# In HybridLens, the DOE is accessed via lens.doe
# Zernike coefficients can be optimized during training
Achromatic Hybrid Lens
Combining Refractive and Diffractive
# HybridLens combines a GeoLens (refractive) with a DOE (diffractive)
# The DOE is placed at the end of the optical system
# Benefits of hybrid design:
# - DOE has opposite chromatic dispersion to glass
# - Can correct chromatic aberration with a single thin element
# - Enables compact achromatic designs
# Load a hybrid lens design
from deeplens.optics import HybridLens
lens = HybridLens(
filename='./datasets/lenses/hybrid/achromatic_hybrid.json',
device='cuda'
)
# Access components
geolens = lens.geolens # Refractive part (GeoLens)
doe = lens.doe # Diffractive part (Binary2, Pixel2D, Fresnel, or Zernike)
# The DOE compensates for chromatic aberration from refractive elements
Multi-Wavelength Optimization
import torch
import torch.optim as optim
from deeplens.optics.config import WAVE_RGB, SPP_COHERENT, PSF_KS
# Set double precision for coherent ray tracing
torch.set_default_dtype(torch.float64)
# Get optimizer for both GeoLens and DOE
optimizer = lens.get_optimizer(
doe_lr=1e-4, # DOE learning rate
lens_lr=[1e-4, 1e-4, 1e-2, 1e-5], # GeoLens [d, c, k, a]
lr_decay=0.01
)
wavelengths = WAVE_RGB # [0.656, 0.588, 0.486] um
point = [0.0, 0.0, -10000.0] # On-axis point source
for epoch in range(1000):
optimizer.zero_grad()
total_loss = 0.0
# Optimize for all wavelengths
for wvln in wavelengths:
# Compute PSF using ray-wave model
psf = lens.psf(
points=point,
ks=PSF_KS,
wvln=wvln,
spp=SPP_COHERENT
)
# Loss: minimize PSF spread (maximize peak)
loss = -psf.max() # Simple peak sharpening loss
total_loss += loss
total_loss.backward()
optimizer.step()
if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {total_loss.item():.6f}")
Multi-Focal Lens
Design lens with multiple focal points:
from deeplens.optics.diffractive_surface import Binary2
# Create binary phase pattern for two focal lengths
# f1 = 50mm, f2 = 100mm
H, W = 512, 512
phase_pattern = torch.zeros(H, W, dtype=torch.bool)
for i in range(H):
for j in range(W):
x = (i - H/2) * 0.01 # mm
y = (j - W/2) * 0.01
r2 = x**2 + y**2
# Combined phase for two focal lengths
phase1 = -torch.pi * r2 / (0.550e-3 * 50.0)
phase2 = -torch.pi * r2 / (0.550e-3 * 100.0)
# Binary encoding (simplified)
total_phase = phase1 + phase2
phase_pattern[i, j] = (total_phase % (2*torch.pi)) > torch.pi
# Add to lens
multi_focal_doe = Binary2(
phase_pattern=phase_pattern,
d=0.001,
wavelength=0.550
)
lens.surfaces.append(multi_focal_doe)
Extended Depth of Field
Using Cubic Phase Plate
# For extended depth of field (EDoF), use a GeoLens with Cubic surface
# Cubic phase creates a depth-invariant PSF (wavefront coding)
from deeplens import GeoLens
from deeplens.optics.config import DEPTH, PSF_KS, SPP_PSF
# Load lens with cubic phase element
# Cubic surfaces are defined in JSON with type "Cubic"
lens = GeoLens(
filename='./datasets/lenses/camera/edof_cubic.json',
device='cuda'
)
# Test PSF at multiple depths
import torch
depths = [-500.0, -1000.0, -2000.0, -5000.0]
psfs = []
for depth in depths:
point = torch.tensor([0.0, 0.0, depth])
psf = lens.psf(points=point, ks=PSF_KS, spp=SPP_PSF)
psfs.append(psf)
# Cubic phase creates similar PSF shapes across depth
# Post-processing with deconvolution recovers sharp images
Optimization for EDoF
import torch
from deeplens.optics.config import PSF_KS, SPP_PSF
# Optimize DOE for consistent PSF across depth range
depths = torch.linspace(-500, -5000, 5)
for epoch in range(500):
optimizer.zero_grad()
loss = 0.0
# Calculate PSFs at all depths
psfs = []
for depth in depths:
point = torch.tensor([0.0, 0.0, depth.item()])
psf = lens.psf(points=point, ks=PSF_KS, spp=SPP_PSF)
psfs.append(psf)
# Loss: Encourage similar PSFs across depths
psf_stack = torch.stack(psfs, dim=0)
psf_mean = psf_stack.mean(dim=0)
# Variance loss (minimize PSF variation)
variance_loss = ((psf_stack - psf_mean) ** 2).mean()
# Sharpness loss (maximize PSF peak)
sharpness_loss = -psf_mean.max()
loss = variance_loss + 0.1 * sharpness_loss
loss.backward()
optimizer.step()
Fabrication Considerations
Manufacturable DOE Design
# For manufacturability, consider:
# 1. Minimum feature size (typically > 0.5 μm for photolithography)
# 2. Maximum aspect ratio (height/width < 5 for standard processes)
# 3. Quantization levels (8 or 16 levels for multi-level DOE)
# Apply phase quantization for multi-level DOE
import torch
def quantize_phase(phase, num_levels=8):
"""Quantize continuous phase to discrete levels."""
phase_norm = (phase % (2 * torch.pi)) / (2 * torch.pi)
phase_quantized = torch.round(phase_norm * num_levels) / num_levels
return phase_quantized * 2 * torch.pi
# Access DOE phase map
wvln = 0.550
phase_map = lens.doe.get_phase_map(wvln)
# Quantize for fabrication
phase_quantized = quantize_phase(phase_map, num_levels=8)
Multi-Level DOE
import torch
# Quantize continuous phase to discrete levels for fabrication
num_levels = 8 # Common: 2 (binary), 4, 8, 16 levels
def quantize_phase(phase, num_levels):
"""Quantize phase to discrete levels."""
phase_norm = (phase % (2 * torch.pi)) / (2 * torch.pi)
phase_quantized = torch.round(phase_norm * num_levels) / num_levels
return phase_quantized * 2 * torch.pi
# Get DOE phase map and quantize
wvln = 0.550 # Design wavelength
phase_continuous = lens.doe.get_phase_map(wvln)
phase_multilevel = quantize_phase(phase_continuous, num_levels)
# Note: For training with quantization, use straight-through estimator
# to allow gradients to flow through the quantization operation
Running Example
Complete script available as 6_hybridlens_design.py:
Performance Comparison
| Metric | Pure Refractive | Hybrid Design |
|---|---|---|
| Chromatic Aberration | Requires multiple elements | Single DOE can correct |
| Weight | Heavy (multiple glass elements) | Light (thin DOEs) |
| Thickness | Thick lens stack | Compact design |
| Cost | High (precision glass) | Lower (planar fabrication) |
| Efficiency | High (>95%) | Moderate (70-90%) |
| Diffraction Orders | N/A | Manage higher orders |
Advantages and Limitations
Advantages:
- Compact and lightweight
- Excellent chromatic correction
- Novel functionalities (multi-focal, EDoF)
- Potential for lower cost
Limitations:
- Lower diffraction efficiency
- Wavelength-dependent performance
- More complex fabrication
- Stray light from higher orders
Tips and Best Practices
- Start with Refractive: Design refractive system first, add DOE for correction
- Wavelength Range: DOEs are wavelength-sensitive, design for target range
- Efficiency: Balance performance with diffraction efficiency
- Sampling: Use high SPP (>2048) for accurate diffraction simulation
- Fabrication: Consider manufacturing constraints early in design
- Validation: Prototype and test critical designs
See Also
- Automated Lens Design - Optimization techniques
- Optics API - Diffractive surfaces
- Tutorials - Step-by-step guides
- Paper: SIGGRAPH Asia 2024