File: lensmodels.org

package info (click to toggle)
mrcal 2.5.2-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,548 kB
  • sloc: python: 40,828; ansic: 15,809; cpp: 1,754; perl: 303; makefile: 163; sh: 99; lisp: 84
file content (371 lines) | stat: -rw-r--r-- 17,299 bytes parent folder | download | duplicates (2)
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]].