Lens
Abstract base class for every lens model in DeepLens. Lens defines the shared
interface — psf(), psf_rgb(), render(), sensor configuration, and file
I/O — that GeoLens, HybridLens,
DiffractiveLens, PSFNetLens, and
DefocusLens all inherit.
deeplens.Lens
Lens(
dtype=torch.float32,
device=None,
primary_wvln=DEFAULT_WAVE,
wvln_rgb=WAVE_RGB,
obj_depth=DEPTH,
)
Bases: DeepObj
Abstract base class for all lens models in DeepLens.
Lens defines the shared interface — PSF computation (psf, psf_rgb),
image rendering (render), sensor configuration, and JSON file I/O — that
GeoLens, HybridLens, DiffractiveLens, PSFNetLens, and DefocusLens
all inherit. Subclasses override the core optical methods (e.g. psf) with
their own differentiable implementations.
Initialize a lens class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dtype
|
dtype
|
Data type. Defaults to torch.float32. |
float32
|
device
|
str
|
Device to run the lens. Defaults to None. |
None
|
primary_wvln
|
float
|
Primary design wavelength [µm].
Used as fallback when a method is called without an explicit
|
DEFAULT_WAVE
|
wvln_rgb
|
sequence of float
|
Three wavelengths used for
RGB (polychromatic) computations, ordered |
WAVE_RGB
|
obj_depth
|
float
|
Default object depth [mm] used as
fallback when a method is called without an explicit
|
DEPTH
|
Source code in deeplens-src/deeplens/lens.py
read_lens_json
Read the lens from a JSON file. Must be overridden by subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
Path to the JSON lens file. |
required |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
This base implementation must be overridden. |
Source code in deeplens-src/deeplens/lens.py
write_lens_json
Write the lens to a JSON file. Must be overridden by subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
Destination path for the JSON lens file. |
required |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
This base implementation must be overridden. |
Source code in deeplens-src/deeplens/lens.py
set_sensor
Set sensor size and resolution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sensor_size
|
tuple
|
Sensor size (w, h) in [mm]. |
required |
sensor_res
|
tuple
|
Sensor resolution (W, H) in [pixels]. |
required |
Source code in deeplens-src/deeplens/lens.py
set_sensor_res
Set sensor resolution (and aspect ratio) while keeping sensor radius unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sensor_res
|
tuple
|
Sensor resolution (W, H) in [pixels]. |
required |
Source code in deeplens-src/deeplens/lens.py
create_dummy_sensor
Fill in any sensor geometry the lens file did not provide.
A lens file may omit part of the sensor description. The sensor radius
is derived from sensor_size when absent, and defaults are supplied for
sensor_size and sensor_res so the lens is usable without an explicit
set_sensor() call.
Raises:
| Type | Description |
|---|---|
ValueError
|
If neither |
Source code in deeplens-src/deeplens/lens.py
calc_fov
Compute FoV (radian) of the lens.
Reference
[1] https://en.wikipedia.org/wiki/Angle_of_view_(photography)
Source code in deeplens-src/deeplens/lens.py
psf
Compute the monochromatic PSF for one or more point sources.
Subclasses must override this method with a differentiable implementation. Three computation models are common in practice: geometric ray binning, coherent ray-wave, and Huygens spherical-wave integration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
Tensor
|
Point source coordinates, shape |
required |
wvln
|
float
|
Wavelength in micrometers. When |
None
|
ks
|
int
|
Output PSF kernel size in pixels. Defaults
to |
PSF_KS
|
**kwargs
|
Additional keyword arguments forwarded to the underlying
PSF computation (e.g. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
psf |
Tensor
|
PSF intensity map, shape |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
This base implementation must be overridden. |
Note
The method is differentiable with respect to all optimisable lens parameters so it can be used directly inside a training loop.
Examples:
point = torch.tensor([0.0, 0.0, -10000.0])
psf = lens.psf(points=point, ks=64, model="geometric")
print(psf.shape) # torch.Size([64, 64])
Source code in deeplens-src/deeplens/lens.py
psf_rgb
Compute the RGB (tri-chromatic) PSF by stacking three wavelength calls.
Calls psf three times for the RGB primary wavelengths stored
in self.wvln_rgb and stacks the results along the channel axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
Tensor
|
Point source coordinates, shape |
required |
ks
|
int
|
PSF kernel size. Defaults to |
PSF_KS
|
**kwargs
|
Forwarded to |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
psf_rgb |
Tensor
|
RGB PSF, shape |
Source code in deeplens-src/deeplens/lens.py
point_source_grid
Generate a grid of point sources for PSF calculation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
float
|
Depth (z-coordinate) of the point sources [mm] (negative, in front of the lens). |
required |
grid
|
tuple
|
Grid size (grid_w, grid_h). Defaults to (9, 9), meaning a 9x9 grid. |
(9, 9)
|
normalized
|
bool
|
If True, return normalized object-space xy coordinates in [-1, 1]; if False, scale to physical positions [mm]. Defaults to True. |
True
|
quater
|
bool
|
If True, return only one quarter of the grid to save memory. Defaults to False. |
False
|
center
|
bool
|
If True, place points at the center of each patch; otherwise sample to the field corners. Defaults to True. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
point_source |
Tensor
|
Object source coordinates, shape
[grid_h, grid_w, 3] with the last dim ordered (x, y, z). When
|
Source code in deeplens-src/deeplens/lens.py
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | |
psf_map
Compute monochrome PSF map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
tuple
|
Grid size (grid_w, grid_h). Defaults to (5, 5), meaning 5x5 grid. |
(5, 5)
|
wvln
|
float
|
Wavelength in µm. When |
None
|
depth
|
float
|
Depth of the object. When |
None
|
ks
|
int
|
Kernel size. Defaults to PSF_KS. |
PSF_KS
|
Returns:
| Name | Type | Description |
|---|---|---|
psf_map |
Tensor
|
Monochrome PSF map, shape [grid_h, grid_w, 1, ks, ks]. |
Source code in deeplens-src/deeplens/lens.py
psf_map_rgb
Compute RGB PSF map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
tuple
|
Grid size (grid_w, grid_h). Defaults to (5, 5), meaning 5x5 grid. |
(5, 5)
|
ks
|
int
|
Kernel size. Defaults to PSF_KS, meaning PSF_KS x PSF_KS kernel size. |
PSF_KS
|
depth
|
float
|
Depth of the object. When |
None
|
**kwargs
|
Additional arguments for psf_map(). |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
psf_map |
Tensor
|
Shape of [grid_h, grid_w, 3, ks, ks]. |
Source code in deeplens-src/deeplens/lens.py
draw_psf_map
draw_psf_map(
grid=(7, 7),
ks=PSF_KS,
depth=None,
log_scale=False,
save_name="./psf_map.png",
show=False,
)
Draw the RGB PSF map of the lens and save it (or return the figure).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
tuple
|
Grid size (grid_w, grid_h). Defaults to (7, 7). |
(7, 7)
|
ks
|
int
|
PSF kernel size in pixels. Defaults to PSF_KS. |
PSF_KS
|
depth
|
float
|
Object depth [mm]. When None (default),
falls back to |
None
|
log_scale
|
bool
|
If True, normalize each PSF in log scale for better visualization. Defaults to False. |
False
|
save_name
|
str
|
Output image path. Defaults to "./psf_map.png". |
'./psf_map.png'
|
show
|
bool
|
If True, return (fig, ax) instead of saving. Defaults to False. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
result |
tuple or None
|
(fig, ax) if |
Source code in deeplens-src/deeplens/lens.py
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | |
point_source_radial
Generate radial point sources from center to edge of the field.
Produces grid evenly-spaced points along a chosen radial direction
(diagonal, meridional, or sagittal) in normalized or physical object-space
coordinates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
float
|
Object depth (z-coordinate) in mm. |
required |
grid
|
int
|
Number of sample points. Defaults to 9. |
9
|
center
|
bool
|
If |
False
|
direction
|
str
|
Sampling direction —
|
'diagonal'
|
normalized
|
bool
|
If |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
point_source |
Tensor
|
Point source positions, shape |
Source code in deeplens-src/deeplens/lens.py
draw_psf_radial
Draw a radial (45 deg, diagonal) sequence of RGB PSFs and save it.
Draws M PSFs evenly spaced from the field center to the corner, each of size ks x ks, arranged in a single row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
M
|
int
|
Number of PSFs to draw. Defaults to 3. |
3
|
depth
|
float
|
Object depth [mm]. When None (default),
falls back to |
None
|
ks
|
int
|
PSF kernel size in pixels. Defaults to PSF_KS. |
PSF_KS
|
log_scale
|
bool
|
If True, normalize each PSF in log scale for better visualization. Defaults to False. |
False
|
save_name
|
str
|
Output image path. Defaults to "./psf_radial.png". |
'./psf_radial.png'
|
Source code in deeplens-src/deeplens/lens.py
render
Differentiable image simulation for a 2D (flat) scene.
Performs only the optical component of image simulation and is fully differentiable.
For incoherent imaging the intensity PSF is convolved with the object-space image. For coherent imaging the complex PSF is convolved with the complex object image before squaring for intensity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img_obj
|
Tensor
|
Input image in linear (raw) space,
shape |
required |
depth
|
float
|
Object depth in mm (negative value).
When |
None
|
method
|
str
|
Rendering method. When
|
None
|
**kwargs
|
Method-specific keyword arguments:
|
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
img_render |
Tensor
|
Rendered image, shape |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If method is |
Exception
|
If method is not recognised. |
Reference
[1] "Optical Aberration Correction in Postprocessing using Imaging Simulation", TOG 2021. [2] "Efficient depth- and spatially-varying image simulation for defocus deblur", ICCVW 2025.
Examples:
img_rendered = lens.render(
img, depth=-10000.0, method="psf_patch",
patch_center=(0.3, 0.0), psf_ks=64,
)
Source code in deeplens-src/deeplens/lens.py
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 | |
render_psf
Render an image patch using PSF convolution (deprecated alias).
Thin wrapper around render_psf_patch. Prefer calling render_psf_patch
directly to avoid confusion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img_obj
|
Tensor
|
Input image in raw space, shape [B, C, H, W]. |
required |
depth
|
float
|
Object depth [mm]. When None (default),
falls back to |
None
|
patch_center
|
tuple
|
Patch center (x, y) in normalized object coordinates. Defaults to (0, 0). |
(0, 0)
|
psf_ks
|
int
|
PSF kernel size in pixels. Defaults to PSF_KS. |
PSF_KS
|
Returns:
| Name | Type | Description |
|---|---|---|
img_render |
Tensor
|
Rendered image, shape [B, C, H, W]. |
Source code in deeplens-src/deeplens/lens.py
render_psf_patch
Render an image patch using a single PSF evaluated at the patch center.
Computes the RGB PSF at patch_center and convolves it with the input
image. All pixels in the patch share the same PSF (valid for a small,
roughly isoplanatic patch).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img_obj
|
Tensor
|
Input image in raw space, shape [B, C, H, W]. |
required |
depth
|
float
|
Object depth [mm]. When None (default),
falls back to |
None
|
patch_center
|
tuple or Tensor
|
Patch center (x, y) in normalized object coordinates, shape [2] or [B, 2]. |
(0, 0)
|
psf_ks
|
int
|
PSF kernel size in pixels. Defaults to PSF_KS. |
PSF_KS
|
method
|
str
|
Convolution backend, |
'conv'
|
Returns:
| Name | Type | Description |
|---|---|---|
img_render |
Tensor
|
Rendered image, shape [B, C, H, W]. |
Source code in deeplens-src/deeplens/lens.py
render_psf_map
Render a full-resolution image using spatially-varying PSF block convolution.
Note
Larger psf_grid and psf_ks give more accurate rendering but are slower.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img_obj
|
Tensor
|
Input image in raw space, shape [B, C, H, W]. |
required |
depth
|
float
|
Object depth [mm]. When None (default),
falls back to |
None
|
psf_grid
|
int or tuple
|
PSF grid size. Defaults to 7. |
7
|
psf_ks
|
int
|
PSF kernel size in pixels. Defaults to PSF_KS. |
PSF_KS
|
psf_spp
|
int
|
Samples per point for PSF computation. Defaults to SPP_PSF. |
SPP_PSF
|
Returns:
| Name | Type | Description |
|---|---|---|
img_render |
Tensor
|
Rendered image, shape [B, C, H, W]. |
Source code in deeplens-src/deeplens/lens.py
render_rgbd
Render RGBD image.
TODO: add obstruction-aware image simulation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img_obj
|
Tensor
|
Object image, shape [B, C, H, W]. |
required |
depth_map
|
Tensor
|
Depth map [mm], shape [B, 1, H, W] (also accepts [B, H, W]). Values should be positive. |
required |
method
|
str
|
Image simulation method, one of "psf_patch", "psf_map", or "psf_pixel". Defaults to "psf_patch". |
'psf_patch'
|
**kwargs
|
Method-specific keyword arguments, e.g. interp_mode (str): "depth" or "disparity", defaults to "disparity"; num_layers (int): number of depth layers, defaults to 16. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
img_render |
Tensor
|
Rendered image, shape [B, C, H, W]. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If depth_map contains negative values. |
Exception
|
If method is not recognised. |
Reference
[1] "Aberration-Aware Depth-from-Focus", TPAMI 2023. [2] "Efficient Depth- and Spatially-Varying Image Simulation for Defocus Deblur", ICCVW 2025.
Source code in deeplens-src/deeplens/lens.py
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 | |
activate_grad
Activate (or deactivate) gradients for each surface.
Must be overridden by subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
activate
|
bool
|
Whether to enable gradients. Defaults to True. |
True
|
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
This base implementation must be overridden. |
Source code in deeplens-src/deeplens/lens.py
get_optimizer_params
Build per-parameter-group optimizer params. Must be overridden.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lr
|
list
|
Per-group learning rates for the different lens parameters. Defaults to [1e-4, 1e-4, 1e-1, 1e-3]. |
[0.0001, 0.0001, 0.1, 0.001]
|
Returns:
| Name | Type | Description |
|---|---|---|
params |
list
|
List of parameter-group dicts for a torch optimizer. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
This base implementation must be overridden. |
Source code in deeplens-src/deeplens/lens.py
get_optimizer
Build an Adam optimizer over the lens parameter groups.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lr
|
list
|
Per-group learning rates passed to
|
[0.0001, 0.0001, 0, 0.001]
|
Returns:
| Name | Type | Description |
|---|---|---|
optimizer |
Adam
|
Configured Adam optimizer. |