Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ build/
dist/
wheels/
*.egg-info
**/__marimo__/
site/

# Virtual environments
.venv

# Mac system files
**/.DS_Store

.cache

**/*.so
Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"--tweaks=-Wall",
"--tweaks=-Wextra",
"--clang-tidy",
"--query-driver=/usr/bin/c++,/usr/bin/g++,/usr/bin/g++-*,/usr/bin/x86_64-linux-gnu-g++*",
"-j=5"
],
"search.useIgnoreFiles": false,
"files.associations": {
"*.py": "python",
"*.rmd": "markdown",
Expand Down
44 changes: 44 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Don't go too deep without exposing and sanity checking your approach with the user.
Reveal your thinking and processing as much as possible!

Don't worry about linting errors if `ruff check --fix` and `ruff format` will fix them.
But do not run any of the linter checks on the terminal yourself, I'll handle that.

# Docstrings

Write docstrings for public functions/methods.

For functions, they should be on this format

```
"""<short description>

<optional long description>

Args:
arg1: <description>
arg2: <description>

Returns:
<description>
<description>
"""
```

Never say types in descriptions, as they are already annotated. Mention the shape of all numpy arrays.
Try not to duplicate information inside the docstring too much, and don't just repeat the variable names.

# Stubs

Never modify the stubs. They are autogenerated, and your changes will just be overwritten.

# Comments

Keep comments minimal, only add comments when the code needs explaining why it's doing something.

# Plots

Follow the color scheme of the existing plots in the plots.py file.
Making the type checker happy is less important in the plots if the plotting library APIs make it hard to make the types work. In this case the usage of `type: ignore` is fine.

All plots should be exported, and if applicable, have a matching method on the CalibrationResult object in calibrate.py.
45 changes: 1 addition & 44 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,44 +1 @@
Don't go too deep without exposing and sanity checking your approach with the user.
Reveal your thinking and processing as much as possible!

Don't worry about linting errors if `ruff check --fix` and `ruff format` will fix them.
But do not run any of the linter checks on the terminal yourself, I'll handle that.

# Docstrings

Write docstrings for public functions/methods.

For functions, they should be on this format

```
"""<short description>

<optional long description>

Args:
arg1: <description>
arg2: <description>

Returns:
<description>
<description>
"""
```

Never say types in descriptions, as they are already annotated. Mention the shape of all numpy arrays.
Try not to duplicate information inside the docstring too much, and don't just repeat the variable names.

# Stubs

Never modify the stubs. They are autogenerated, and your changes will just be overwritten.

# Comments

Keep comments minimal, only add comments when the code needs explaining why it's doing something.

# Plots

Follow the color scheme of the existing plots in the plots.py file.
Making the type checker happy is less important in the plots if the plotting library APIs make it hard to make the types work. In this case the usage of `type: ignore` is fine.

All plots should be exported, and if applicable, have a matching method on the CalibrationResult object in calibrate.py.
@AGENTS.md
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ find_package(Eigen3 REQUIRED)

find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(OpenMP REQUIRED)


pybind11_add_module(
Expand All @@ -44,6 +45,8 @@ pybind11_add_module(
cpp_src/matching_spline_model.cpp
cpp_src/pinhole_splined_fine_tune.cpp
cpp_src/normalize_pinhole_splined.cpp
cpp_src/seeded_normalize.cpp
cpp_src/lut_max_cell_error.cpp
)

target_compile_definitions(lensboy_bindings
Expand All @@ -59,6 +62,7 @@ target_link_libraries(
Eigen3::Eigen
fmt::fmt
spdlog::spdlog
OpenMP::OpenMP_CXX
)

if(APPLE)
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,30 @@ pip install lensboy
Spline models use B-spline grids instead of polynomial coefficients, so they can fit lenses that OpenCV's model can't. This approach is inspired by [mrcal](https://mrcal.secretsauce.net/).

The calibrated model converts to a pinhole model with undistortion maps, so you can use it with any standard pinhole pipeline.

## Runtime unproject LUTs

Iterative unprojection can be too slow for some applications.
`UnprojectLUT` caches `normalize_points()` on a regular pixel grid so
that per-pixel queries reduce to a bicubic interpolation. The cache is
saved as a directory of `metadata.json` + `xy_grid.npy`, loadable from
Python or from a small standalone C++ runtime in
[`cpp_runtime/`](cpp_runtime/).

```python
import lensboy as lb
from lensboy.analysis import compute_lut_error_heatmap

model = lb.OpenCV.load("camera.json")
lut = model.get_unproject_lut(pixel_stride=32)
lut.save("camera_lut/")

runtime_lut = lb.UnprojectLUT.load("camera_lut/")
rays, valid_mask = runtime_lut.normalize_points(pixel_coords)

heatmap = compute_lut_error_heatmap(runtime_lut, model)
```

See the [unproject LUT guide](https://robertleoj.github.io/lensboy/unproject_lut.html)
for sizing, interpolation modes, the file format, and the C++ runtime.
There is also a runnable [Jupyter notebook](examples/unproject_lut.ipynb).
Loading
Loading