1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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
|
#+TITLE: mrcal lens models
#+OPTIONS: toc:t
mrcal supports a wide range of projection models. Some are intended to represent
physical lenses, while others are idealized, useful for processing. /All/ are
referred to as /lens/ models in the code and in the documentation. The
representation details and projection behaviors are described here.
* Representation
:PROPERTIES:
:CUSTOM_ID: representation
:END:
A =mrcal= /lens/ model represents a lens independent of its pose in space. A
lens model is fully specified by
- A model /family/ (or /type/). This is something like =LENSMODEL_PINHOLE= or
=LENSMODEL_SPLINED_STEREOGRAPHIC=
- /Configuration/ parameters. This is a set of key/value pairs, which is
required only by some model families. These values are /not/ subject to
optimization, and may affect how many optimization parameters are needed.
- Optimization parameters. These are the parameters that the optimization
routine controls as it runs
Each model family also has some /metadata/ key/value pairs associated with it.
These are inherent properties of a model family, and are not settable. At the
time of this writing there are 3 metadata keys:
- =has_core=: True if the first 4 optimization values are the "core": $f_x$,
$f_y$, $c_x$, $c_y$. As of mrcal 2.3 this is True for all models.
- =can_project_behind_camera=: True if this model is able to project vectors
from behind the camera. If it cannot, then [[file:mrcal-python-api-reference.html#-unproject][=mrcal.unproject()=]] will never
report =z= < 0. As of mrcal 2.3 this is True only for models based on a
stereographic projection, for instance [[#splined-stereographic-lens-model][=LENSMODEL_SPLINED_STEREOGRAPHIC=]]. It
is False for models based on a pinhole projection, for instance
=LENSMODEL_OPENCV...= and =LENSMODEL_CAHVOR...=.
- =has_gradients=: True if this model has gradients implemented. As of mrcal 2.3
all models have this.
- =noncentral=: True if [[file:formulation.org::#lens-behavior][projection is /not/ invariant to scaling; i.e. if every
observation ray does /not/ intersect at the same point]]. As of mrcal 2.3 only
[[#lensmodel-cahvore][=LENSMODEL_CAHVORE=]] does this.
In Python, the models are identified with a string =LENSMODEL_XXX= where the
=XXX= selects the specific model family and the configuration, if needed. A
sample model string with a configuration:
=LENSMODEL_SPLINED_STEREOGRAPHIC_order=3_Nx=30_Ny=20_fov_x_deg=170=. The
configuration is the pairs =order=3=, =Nx=30= and so on. At this time, model
families that accept a configuration /require/ it to be specified fully,
although optional configuration keys will be added soon. Today, calling Python
functions with =LENSMODEL_SPLINED_STEREOGRAPHIC= or
=LENSMODEL_SPLINED_STEREOGRAPHIC_order=3= will fail due to an incomplete
configuration. The [[file:mrcal-python-api-reference.html#-lensmodel_metadata_and_config][=mrcal.lensmodel_metadata_and_config()=]] function returns a
dict containing the metadata and configuration for a particular model string.
In C, the model family is selected with the [[https://www.github.com/dkogan/mrcal/blob/master/mrcal.h#mrcal_lensmodel_type_t][=mrcal_lensmodel_type_t=]] enum. The
elements are the same as the Python model names, but with =MRCAL_= prepended. So
the sample model from above has type =MRCAL_LENSMODEL_SPLINED_STEREOGRAPHIC=. In
C the [[https://www.github.com/dkogan/mrcal/blob/master/mrcal.h##mrcal_lensmodel_t][=mrcal_lensmodel_t=]] structure contains the type /and/ configuration. This
structure is thus an analogue the the model strings, as Python sees them. So a
number of C functions accepting [[https://www.github.com/dkogan/mrcal/blob/master/mrcal.h##mrcal_lensmodel_t][=mrcal_lensmodel_t=]] arguments are analogous to
Python functions taking model strings. For instance, the number of parameters
needed to fully describe a given model can be obtained by calling
[[file:mrcal-python-api-reference.html#-lensmodel_num_params][=mrcal.lensmodel_num_params()=]] in Python or [[https://www.github.com/dkogan/mrcal/blob/master/mrcal.h#mrcal_lensmodel_num_params][=mrcal_lensmodel_num_params()=]] in C.
Given a [[https://www.github.com/dkogan/mrcal/blob/master/mrcal.h##mrcal_lensmodel_t][=mrcal_lensmodel_t lensmodel=]] structure of type =XXX= (i.e. if
=lensmodel.type= is =MRCAL_LENSMODEL_XXX=) then the configuration is available
in =lensmodel.LENSMODEL_XXX__config=, which has type
=mrcal_LENSMODEL_XXX__config_t=. The metadata is requestable by calling this
function:
#+begin_src c
mrcal_lensmodel_metadata_t mrcal_lensmodel_metadata( const mrcal_lensmodel_t* lensmodel );
#+end_src
* Intrinsics core
:PROPERTIES:
:CUSTOM_ID: core
:END:
Most models contain an "intrinsics core". These are 4 values that appear at the
start of the parameter vector:
- $f_x$: the focal length in the horizontal direction, in pixels
- $f_y$: the focal length in the vertical direction, in pixels
- $c_x$: the horizontal projection center, in pixels
- $c_y$: the vertical projection center, in pixels
At this time all models contain a core.
* Models
Currently all models except [[#lensmodel-cahvore][=LENSMODEL_CAHVORE=]] represent a /central/
projection: all observation rays intersect at a single point (the camera
origin). So $k \vec v$ projects to the same $\vec q$ for all $k$. This isn't
strictly true for real-world lenses, so [[file:formulation.org::#lens-behavior][non-central projections]] will be
supported in a future release of mrcal.
** =LENSMODEL_PINHOLE=
:PROPERTIES:
:CUSTOM_ID: lensmodel-pinhole
:END:
This is the basic "pinhole" model with 4 parameters: the core. Projection of a
point $\vec p$ is defined as
\[\vec q = \left[ \begin{aligned} f_x \frac{p_x}{p_z} + c_x \\ f_y \frac{p_y}{p_z} + c_y \end{aligned} \right] \]
This model is defined only in front of the camera, and projects to infinity as
we approach 90 degrees off the optical axis ($p_z \rightarrow 0$). Straight
lines in space remain straight under this projection, and observations of the
same plane by two pinhole cameras define a [[https://en.wikipedia.org/wiki/Homography][homography]]. This model can be used
for [[file:stereo.org][stereo rectification]], although it only works well with long lenses. Longer
lenses tend to have roughly pinhole behavior, but no real-world lens follows
this projection, so this exists for data processing only.
** =LENSMODEL_STEREOGRAPHIC=
:PROPERTIES:
:CUSTOM_ID: lensmodel-stereographic
:END:
This is another trivial model that exists for data processing, and not to
represent real lenses. Like the pinhole model, this has just the 4 core
parameters.
To define the projection of a point $\vec p$, let's define the angle off the
optical axis:
\[ \theta \equiv \tan^{-1} \frac{\left| \vec p_{xy} \right|}{p_z} \]
then
\[ \vec u \equiv \frac{\vec p_{xy}}{\left| \vec p_{xy} \right|} 2 \tan\frac{\theta}{2} \]
and
\[\vec q = \left[ \begin{aligned} f_x u_x + c_x \\ f_y u_y + c_y \end{aligned} \right] \]
This model is able to project behind the camera, and has a single singularity:
directly opposite the optical axis. mrcal refers to $\vec u$ as the
/normalized/ stereographic projection; we get the projection $\vec q = \vec u$
when $f_x = f_y = 1$ and $c_x = c_y = 0$
Note that the pinhole model can be defined in the same way, except the pinhole
model has $\vec u \equiv \frac{\vec p_{xy}} {\left| \vec p_{xy} \right|} \tan
\theta$. And we can thus see that for long lenses the pinhole model and the
stereographic model function similarly: $\tan \theta \approx 2 \tan
\frac{\theta}{2}$ as $\theta \rightarrow 0$
** =LENSMODEL_LONLAT=
:PROPERTIES:
:CUSTOM_ID: lensmodel-lonlat
:END:
This is a standard [[https://en.wikipedia.org/wiki/Equirectangular_projection][equirectangular projection]]. It's a trivial model useful
not for representing lenses, but for describing the projection function of wide
panoramic images. This works just like latitude an longitude on a globe, with a
linear angular map on latitude and longitude. The 4 intrinsics core parameters
are used to linearly map latitude, longitude to pixel coordinates. The full
projection expression to map a camera-coordinate point $\vec p$ to an image
pixel $\vec q$:
\[
\vec q =
\left[ \begin{aligned}
f_x \, \mathrm{lon} + c_x \\
f_y \, \mathrm{lat} + c_y
\end{aligned} \right]
=
\left[ \begin{aligned}
f_x \tan^{-1}\left(\frac{p_x}{p_z}\right) + c_x \\
f_y \sin^{-1}\left(\frac{p_y}{\left|\vec p\right|}\right) + c_y
\end{aligned} \right]
\]
So $f_x$ and $f_y$ specify the angular resolution, in pixels/radian.
For normal lens models the optical axis is at $\vec p = \left[ \begin{aligned} 0
\\ 0 \\ 1 \end{aligned} \right]$, and projects to roughly the center of the
image, roughly at $\vec q = \left[ \begin{aligned} c_x \\ c_y \end{aligned}
\right]$. /This/ model has $\mathrm{lon} = \mathrm{lat} = 0$ at the optical
axis, which produces the same, usual $\vec q$. However, this projection doesn't
represent a lens and there is no "camera" or an "optical axis". The view may be
centered anywhere, so $c_x$ and $c_y$ could be anything, even negative.
The special case of $f_x = f_y = 1$ and $c_x = c_y = 0$ (the default values in
[[file:mrcal-python-api-reference.html#-project_lonlat][=mrcal.project_lonlat()=]]) produces a /normalized/ equirectangular projection:
\[
\vec q_\mathrm{normalized} =
\left[ \begin{aligned}
\mathrm{lon} \\\mathrm{lat}
\end{aligned} \right]
\]
This projection has a singularity at the poles, approached as $x \rightarrow 0$
and $z \rightarrow 0$.
** =LENSMODEL_LATLON=
:PROPERTIES:
:CUSTOM_ID: lensmodel-latlon
:END:
This is a "transverse equirectangular projection". It works just like
[[#lensmodel-lonlat][=LENSMODEL_LONLAT=]], but rotated 90 degrees. So instead of a globe oriented as
usual with a vertical North-South axis, this projection has a horizontal
North-South axis. The projected $x$ coordinate corresponds to the latitude, and
the projected $y$ coordinate corresponds to the longitude.
As with [[#lensmodel-lonlat][=LENSMODEL_LONLAT=]], lenses do not follow this model. It is useful as the
core of a [[file:stereo.org][rectified view used in stereo processing]]. The full projection
expression to map a camera-coordinate point $\vec p$ to an image pixel $\vec q$:
\[
\vec q =
\left[ \begin{aligned}
f_x \, \mathrm{lat} + c_x \\
f_y \, \mathrm{lon} + c_y
\end{aligned} \right]
=
\left[ \begin{aligned}
f_x \sin^{-1}\left(\frac{p_x}{\left|\vec p\right|}\right) + c_x \\
f_y \tan^{-1}\left(\frac{p_y}{p_z}\right) + c_y
\end{aligned} \right]
\]
As with [[#lensmodel-lonlat][=LENSMODEL_LONLAT=]], $f_x$ and $f_y$ specify the angular resolution, in
pixels/radian. And $c_x$ and $c_y$ specify the projection at the optical axis
$\vec p = \left[ \begin{aligned} 0 \\ 0 \\ 1 \end{aligned} \right]$.
The special case of $f_x = f_y = 1$ and $c_x = c_y = 0$ (the default values in
[[file:mrcal-python-api-reference.html#-project_latlon][=mrcal.project_latlon()=]]) produces a /normalized/ transverse equirectangular
projection:
\[
\vec q_\mathrm{normalized} =
\left[ \begin{aligned}
\mathrm{lat} \\\mathrm{lon}
\end{aligned} \right]
\]
This projection has a singularity at the poles, approached as $y \rightarrow 0$
and $z \rightarrow 0$.
** =LENSMODEL_OPENCV4=, =LENSMODEL_OPENCV5=, =LENSMODEL_OPENCV8=, =LENSMODEL_OPENCV12=
:PROPERTIES:
:CUSTOM_ID: lensmodel-opencv
:END:
These are simple parametric models that have the given number of "distortion"
parameters in addition to the 4 core parameters. The projection behavior is
described in the [[https://docs.opencv.org/4.5.0/d9/d0c/group__calib3d.html#details][OpenCV documentation]]. These do a reasonable job in representing
real-world lenses, /and/ they're compatible with many other tools. The
projection function is
\begin{align*}
\vec P &\equiv \frac{\vec p_{xy}}{p_z} \\
r &\equiv \left|\vec P\right| \\
\vec P_\mathrm{radial} &\equiv \frac{ 1 + k_0 r^2 + k_1 r^4 + k_4 r^6}{ 1 + k_5 r^2 + k_6 r^4 + k_7 r^6} \vec P \\
\vec P_\mathrm{tangential} &\equiv
\left[ \begin{aligned}
2 k_2 P_0 P_1 &+ k_3 \left(r^2 + 2 P_0^2 \right) \\
2 k_3 P_0 P_1 &+ k_2 \left(r^2 + 2 P_1^2 \right)
\end{aligned}\right] \\
\vec P_\mathrm{thinprism} &\equiv
\left[ \begin{aligned}
k_8 r^2 + k_9 r^4 \\
k_{10} r^2 + k_{11} r^4
\end{aligned}\right] \\
\vec q &= \vec f_{xy} \left( \vec P_\mathrm{radial} + \vec P_\mathrm{tangential} + \vec P_\mathrm{thinprism} \right) + \vec c_{xy}
\end{align*}
The parameters are $k_i$. For any N-parameter OpenCV model the higher-order
terms $k_i$ for $i \geq N$ are all 0. So the tangential distortion terms exist for
all the models, but the thin-prism terms exist only for =LENSMODEL_OPENCV12=.
The radial distortion is a polynomial in =LENSMODEL_OPENCV4= and
=LENSMODEL_OPENCV5=, but a rational for the higher-order models.
Practically-speaking =LENSMODEL_OPENCV8= works decently well for wide lenses.
For non-fisheye lenses, =LENSMODEL_OPENCV4= and =LENSMODEL_OPENCV5= work ok. I'm
sure scenarios where =LENSMODEL_OPENCV12= is beneficial exist, but I haven't
come across them.
** =LENSMODEL_CAHVOR=
:PROPERTIES:
:CUSTOM_ID: cahvor-lens-model
:END:
mrcal supports =LENSMODEL_CAHVOR=, a lens model used in a number of tools at
JPL. The =LENSMODEL_CAHVOR= model has 5 "distortion" parameters in addition to
the 4 core parameters. This support exists only for compatibility, and there's
no reason to use it otherwise. This model is described in:
[[https://trs.jpl.nasa.gov/bitstream/handle/2014/20686/98-1739.pdf][Gennery, D.B. 2001. Least-squares camera calibration including lens distortion
and automatic editing of calibration points. In Calibration and Orientation of
Cameras in Computer Vision, A. Gruen and T.S. Huang (Eds.), Springer-Verlag, pp.
123–136]]
** =LENSMODEL_CAHVORE_...=
:PROPERTIES:
:CUSTOM_ID: lensmodel-cahvore
:END:
This is an extended flavor of =LENSMODEL_CAHVOR= to support wider lenses. The
=LENSMODEL_CAHVORE= model has 8 "distortion" parameters in addition to the 4
core parameters. As with =LENSMODEL_CAHVOR= this support exists only for
compatibility, and there's no reason to use it otherwise.
CAHVORE is a [[file:formulation.org::#lens-behavior][noncentral projection]], so more infrastructure is required to make
=unproject()= do something reasonable in all cases. Thus projection uncertainty
and differencing don't work for this model, and throw an error.
*** Configuration
=LENSMODEL_CAHVORE= models have a single configuration parameter: =linearity=.
- =linearity= = 0 is a "fisheye" CAHVORE model. This is referred to as
=CAHVORE2= in JPL tools
- =linearity= = 1 is a "perspective" CAHVORE model. This is referred to as
=CAHVORE1= in JPL tools
- Any other =linearity= is a "general" CAHVORE model. This is referred to as
=CAHVORE3= in JPL tools
*** Some notes
- The last 3 intrinsic parameters of =LENSMODEL_CAHVORE= are the $\vec E$
vector. If these are 0, this becomes a central projection, and
[[file:mrcal-python-api-reference.html#-unproject][=mrcal.unproject()=]] is able to work normally. If you want to compute
differences you need [[file:mrcal-python-api-reference.html#-unproject][=mrcal.unproject()=]], so you must "centralize" the model
by setting =intrinsics[-3:] = 0=. This ignores any noncentral effects, which
could be significant close to the lens. Doing this also invalidates the
uncertainty logic. More general noncentral projection logic will be added in a
future release of mrcal, and this situation may improve then
- A =LENSMODEL_CAHVORE= projection with $\vec E = 0$ and =linearity= = 1 is
equivalent to a =LENSMODEL_CAHVOR= projection
This model is described in:
[[https://link.springer.com/article/10.1007/s11263-006-5168-1][Gennery, D.B. Generalized Camera Calibration Including Fish-Eye Lenses. Int J
Comput Vision 68, 239–266 (2006)]]
** =LENSMODEL_SPLINED_STEREOGRAPHIC_...=
:PROPERTIES:
:CUSTOM_ID: splined-stereographic-lens-model
:END:
This is a stereographic model with correction factors. It is mrcal's attempt to
model real-world lens behavior with more fidelity than the usual parametric
models make possible.
To compute a projection using this model, we first compute the normalized
stereographic projection $\vec u$ as in the [[#lensmodel-stereographic][=LENSMODEL_STEREOGRAPHIC=]] definition
above:
\[ \theta \equiv \tan^{-1} \frac{\left| \vec p_{xy} \right|}{p_z} \]
\[ \vec u \equiv \frac{\vec p_{xy}}{\left| \vec p_{xy} \right|} 2 \tan\frac{\theta}{2} \]
Then we use $\vec u$ to look-up a $\Delta \vec u$ using two separate splined
surfaces:
\[ \Delta \vec u \equiv
\left[ \begin{aligned}
\Delta u_x \left( \vec u \right) \\
\Delta u_y \left( \vec u \right)
\end{aligned} \right] \]
and we then define the rest of the projection function:
\[\vec q =
\left[ \begin{aligned}
f_x \left( u_x + \Delta u_x \right) + c_x \\
f_y \left( u_y + \Delta u_y \right) + c_y
\end{aligned} \right] \]
The $\Delta \vec u$ are the off-stereographic terms. If $\Delta \vec u = 0$, we
get a plain stereographic projection.
Much more detail about this model is available on the [[file:splined-models.org][splined models page]].
*** Configuration
There are several configuration parameters described on the [[file:splined-models.org::#splined-models-configuration-selection][splined models page]].
|