LensSerious 0.1
Lens-correction mathematics as data, not as a library of callbacks
Loading...
Searching...
No Matches
lensserious.h File Reference

What this is, and what it deliberately is not. More...

#include <stddef.h>
#include "lensserious_eval.h"
+ Include dependency graph for lensserious.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ls_calib_dist_t
 
struct  ls_calib_tca_t
 
struct  ls_calib_vig_t
 
struct  ls_calib_real_focal_t
 
struct  ls_lens_t
 
struct  ls_knots_t
 A lens correction the camera maker measured and wrote into the file, as knots. More...
 
struct  ls_modifier_t
 A modifier: the lens resolved at one shooting configuration. More...
 

Macros

#define LS_ENABLE_DISTORTION   (1 << 0)
 
#define LS_ENABLE_TCA   (1 << 1)
 
#define LS_ENABLE_VIGNETTING   (1 << 2)
 
#define LS_ENABLE_SCALE   (1 << 3)
 
#define LS_ENABLE_GEOMETRY   (1 << 4)
 

Typedefs

typedef enum ls_dist_model_t ls_dist_model_t
 
typedef enum ls_tca_model_t ls_tca_model_t
 
typedef enum ls_vig_model_t ls_vig_model_t
 
typedef struct ls_calib_dist_t ls_calib_dist_t
 
typedef struct ls_calib_tca_t ls_calib_tca_t
 
typedef struct ls_calib_vig_t ls_calib_vig_t
 
typedef struct ls_calib_real_focal_t ls_calib_real_focal_t
 
typedef enum ls_lens_type_t ls_lens_type_t
 
typedef struct ls_lens_t ls_lens_t
 
typedef struct ls_knots_t ls_knots_t
 A lens correction the camera maker measured and wrote into the file, as knots.
 
typedef struct ls_modifier_t ls_modifier_t
 A modifier: the lens resolved at one shooting configuration.
 

Enumerations

enum  ls_dist_model_t {
  LS_DIST_NONE = 0 ,
  LS_DIST_POLY3 ,
  LS_DIST_POLY5 ,
  LS_DIST_PTLENS ,
  LS_DIST_KNOTS
}
 
enum  ls_tca_model_t {
  LS_TCA_NONE = 0 ,
  LS_TCA_LINEAR ,
  LS_TCA_POLY3
}
 
enum  ls_vig_model_t {
  LS_VIG_NONE = 0 ,
  LS_VIG_PA ,
  LS_VIG_KNOTS
}
 
enum  { LS_MAX_CALIB = 512 }
 
enum  ls_lens_type_t {
  LS_LENS_UNKNOWN = 0 ,
  LS_LENS_RECTILINEAR ,
  LS_LENS_FISHEYE ,
  LS_LENS_PANORAMIC ,
  LS_LENS_EQUIRECTANGULAR ,
  LS_LENS_FISHEYE_ORTHOGRAPHIC ,
  LS_LENS_FISHEYE_STEREOGRAPHIC ,
  LS_LENS_FISHEYE_EQUISOLID ,
  LS_LENS_FISHEYE_THOBY
}
 

Functions

int ls_modifier_init (ls_modifier_t *mod, const ls_lens_t *lens, float crop, int width, int height, float focal, float aperture, float distance, float scale, int target_type, int flags, int reverse)
 Resolve a lens at one (crop, geometry, focal, aperture, distance, scale).
 
int ls_modifier_init_knots (ls_modifier_t *mod, const ls_knots_t *knots, int width, int height, float scale, int flags, int reverse)
 Resolve a maker's embedded profile, in place of a database lens.
 
float ls_modifier_autoscale (const ls_modifier_t *mod)
 The scale that just removes the black borders a correction leaves behind.
 
int ls_modifier_set_projection (ls_modifier_t *mod, int from_type, int to_type, float focal_mm, float crop_factor)
 Add a projection change to an already-resolved modifier.
 
int ls_eval_from_modifier (const ls_modifier_t *mod, ls_eval_t *out)
 Flatten a resolved modifier into the scalar block a kernel can take by value.
 
int ls_eval_adopt_vignetting (ls_eval_t *dst, const ls_eval_t *src)
 Move src's vignetting into dst, leaving dst's geometry untouched.
 
int ls_modifier_apply_subpixel_geometry (const ls_modifier_t *mod, float xu, float yu, int width, int height, float *res)
 The geometry map: for count output pixels starting at (xu, yu), write 6 floats per pixel — source coordinates for R, G, B — in pixel space. Bit-for-bit the contract of lfModifier::ApplySubpixelGeometryDistortion(), so a caller can compare the two buffers element-wise.
 
int ls_modifier_apply_vignetting (const ls_modifier_t *mod, float xu, float yu, int width, int height, float *rgba, int row_stride_bytes)
 pa vignetting, multiplied in place over RGBA float rows. Contract of lfModifier::ApplyColorModification(LF_CR_4(RED,GREEN,BLUE,UNKNOWN), F32).
 

Detailed Description

What this is, and what it deliberately is not.

Lensfun's per-frame cost in a raw pipeline is not its database: it is that the displacement map can only be produced by its C++ callback machinery, single-threaded, on the CPU — measured at 278 ms per 24 Mpx frame — after which a GPU pipeline must upload six floats per pixel just to resample. Yet the mathematics is six small closed forms: a census of the complete lensfun database (version_1, 2026) finds distortion ptlens/poly3/poly5 (4810/875/5 entries), TCA poly3/linear (3355/6), vignetting pa (25269, the only model). LensSerious expresses those forms as plain C over a plain struct of coefficients, so the same evaluation can run vectorised on the CPU, inside an OpenCL kernel (opencl/lensserious.cl is the same math, textually), or anywhere a float goes.

The lensfun PROJECT is not being forked: its XML database remains the interchange format and its community remains the source of calibrations. What is replaced is the runtime. Every convention below is ported from lensfun 0.3.4 source with its quirks kept on purpose — the (width-1) sizing, the calibration-sensor aspect/crop correction, the Hermite interpolation with one-sided tangents — because a "cleaner" convention would silently shift every render. Parity with liblensfun is not assumed; it is asserted, by tests/parity_lensfun.c against the whole installed database, and by the side-by-side latch in Ansel's iop/lens.cc which logs the live deviation per frame.

Scope of this seed: distortion + TCA + linear scaling composed into the geometry map, and pa vignetting. Projection conversion between lens types (fisheye → rectilinear and friends) is not implemented yet: the modifier reports it via ls_modifier_t::geometry_unsupported so a caller can fall back, and the parity harness skips those lenses explicitly rather than pretending.

Definition in file lensserious.h.

Macro Definition Documentation

◆ LS_ENABLE_DISTORTION

#define LS_ENABLE_DISTORTION   (1 << 0)

◆ LS_ENABLE_GEOMETRY

#define LS_ENABLE_GEOMETRY   (1 << 4)

◆ LS_ENABLE_SCALE

#define LS_ENABLE_SCALE   (1 << 3)

Definition at line 134 of file lensserious.h.

Referenced by ls_modifier_autoscale(), ls_modifier_init(), and ls_modifier_init_knots().

◆ LS_ENABLE_TCA

◆ LS_ENABLE_VIGNETTING

#define LS_ENABLE_VIGNETTING   (1 << 2)

Typedef Documentation

◆ ls_calib_dist_t

typedef struct ls_calib_dist_t ls_calib_dist_t

One calibration entry, at one focal length (vignetting: one focal/aperture/distance).

◆ ls_calib_real_focal_t

typedef struct ls_calib_real_focal_t ls_calib_real_focal_t

One <real-focal-length> point: the focal engraved on the barrel, and the one the lens actually has there. They differ by up to a factor of two on fisheyes.

◆ ls_calib_tca_t

typedef struct ls_calib_tca_t ls_calib_tca_t

◆ ls_calib_vig_t

typedef struct ls_calib_vig_t ls_calib_vig_t

◆ ls_dist_model_t

◆ ls_knots_t

typedef struct ls_knots_t ls_knots_t

A lens correction the camera maker measured and wrote into the file, as knots.

The second source of correction data this library resolves, beside the lens database. Most mirrorless makers – Sony, Fujifilm, Olympus, and anyone writing DNG 1.3 opcodes – embed a profile of the lens that took the picture in its metadata, and they all express it the same way: a short list of radii and, at each, the factor the coordinate should be scaled by. Not a model with coefficients; the samples themselves, meant to be read back with straight-line interpolation between them.

Two things about the shape of it matter to everything downstream:

  • It is per CHANNEL. cor_rgb holds red, green and blue separately, so distortion and lateral chromatic aberration arrive measured together rather than as a geometry plus a correction on top of it. There is no TCA stage to run afterwards.
  • It is measured on the lens as shipped. There is no projection to change and no calibration sensor to convert from, which is why this needs none of the arguments ls_modifier_init() takes: no crop factor, no aperture, no subject distance, and no focal, because the maker already resolved the profile at the focal that was used.

radius, and vig_radius with it, are the distance from the IMAGE CENTRE over half the image diagonal – 1.0 at the far corner. That is the makers' own convention and this library adopts it wholesale rather than converting, so the numbers a decoder lifts out of the file go in unmodified.

◆ ls_lens_t

typedef struct ls_lens_t ls_lens_t

A lens as data: coefficients plus the calibration sensor's identity.

◆ ls_lens_type_t

◆ ls_modifier_t

typedef struct ls_modifier_t ls_modifier_t

A modifier: the lens resolved at one shooting configuration.

Everything here is a plain value: build it once per commit, hand copies to threads, embed the resolved terms in a kernel's argument block. Nothing is owned, nothing is freed, nothing is locked.

◆ ls_tca_model_t

◆ ls_vig_model_t

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
LS_MAX_CALIB 

Definition at line 92 of file lensserious.h.

◆ ls_dist_model_t

Enumerator
LS_DIST_NONE 
LS_DIST_POLY3 

Rd = Ru · (1 − k1 + k1·Ru²) terms: k1

LS_DIST_POLY5 

Rd = Ru · (1 + k1·Ru² + k2·Ru⁴) terms: k1 k2

LS_DIST_PTLENS 

Rd = Ru · (a·Ru³ + b·Ru² + c·Ru + d), d = 1−a−b−c terms: a b c

LS_DIST_KNOTS 

Rd = Ru · cor(Ru), cor read from a table. No terms; see ls_knots_t.

Definition at line 61 of file lensserious.h.

◆ ls_lens_type_t

Enumerator
LS_LENS_UNKNOWN 
LS_LENS_RECTILINEAR 
LS_LENS_FISHEYE 
LS_LENS_PANORAMIC 
LS_LENS_EQUIRECTANGULAR 
LS_LENS_FISHEYE_ORTHOGRAPHIC 
LS_LENS_FISHEYE_STEREOGRAPHIC 
LS_LENS_FISHEYE_EQUISOLID 
LS_LENS_FISHEYE_THOBY 

Definition at line 94 of file lensserious.h.

◆ ls_tca_model_t

Enumerator
LS_TCA_NONE 
LS_TCA_LINEAR 

Rd = Ru · k per channel terms: kr kb

LS_TCA_POLY3 

Rd = Ru · (b·Ru² + c·Ru + v) per channel terms: vr vb cr cb br bb

Definition at line 70 of file lensserious.h.

◆ ls_vig_model_t

Enumerator
LS_VIG_NONE 
LS_VIG_PA 

Cd = Cs · (1 + k1·r² + k2·r⁴ + k3·r⁶) terms: k1 k2 k3

LS_VIG_KNOTS 

Cd = Cs · v(r), v read from a table. No terms; see ls_knots_t.

Definition at line 77 of file lensserious.h.

Function Documentation

◆ ls_eval_adopt_vignetting()

int ls_eval_adopt_vignetting ( ls_eval_t * dst,
const ls_eval_t * src )

Move src's vignetting into dst, leaving dst's geometry untouched.

For a consumer whose axes do not all come from the same place – an Olympus body embeds distortion and lateral CA but no vignetting, so the falloff has to come from the database while the geometry comes from the file. Resolve one modifier per source, flatten both, and graft the vignetting across.

It is a graft rather than a merge because the two halves of ls_eval_t are genuinely independent: vignetting reads vig_scale, vig_center_x/y, vig_model and either vig_terms or the vignetting knot table, and NOTHING a coordinate transform touches. In particular each half carries its own normalization, which is what makes this safe across resolvers that do not share one – the table path measures radius against the half diagonal, the database path against lensfun's short side, and neither has to know about the other.

Cheaper than the alternative, too: a second block would double what a kernel receives by value, and ls_eval_t is already 632 bytes against the 1024 that OpenCL 1.2 guarantees for a kernel's whole argument list.

Returns
0 if either pointer is NULL, 1 otherwise.

Definition at line 818 of file lensserious.c.

References ls_eval_t::enabled, ls_eval_t::knot_v, ls_eval_t::knot_vn, ls_eval_t::knot_vr, LS_EVAL_ENABLE_VIGNETTING, LS_MAX_KNOTS, ls_eval_t::vig_center_x, ls_eval_t::vig_center_y, ls_eval_t::vig_model, ls_eval_t::vig_scale, and ls_eval_t::vig_terms.

◆ ls_eval_from_modifier()

int ls_eval_from_modifier ( const ls_modifier_t * mod,
ls_eval_t * out )

Flatten a resolved modifier into the scalar block a kernel can take by value.

This is the whole point of the exercise: a correction crosses to the GPU as one fixed 632-byte block that every work-item evaluates for itself, instead of a six-float-per-pixel map that the CPU builds single-threaded and then uploads (measured at 278 ms plus 576 MB of transfer for a 24 Mpx frame). Fixed regardless of which kind of correction it carries – a maker's table is a hundred-odd more floats, and the whole block is still smaller than one row of that map.

Returns
0 if either pointer is NULL, 1 otherwise.

Definition at line 745 of file lensserious.c.

References ls_modifier_t::aspect_ratio_correction, ls_eval_t::center_x, ls_modifier_t::center_x, ls_eval_t::center_y, ls_modifier_t::center_y, ls_modifier_t::dist, ls_eval_t::dist_model, ls_eval_t::dist_terms, ls_eval_t::enabled, ls_modifier_t::enabled, ls_eval_t::geom_focal, ls_modifier_t::geom_focal, ls_eval_t::geom_from, ls_modifier_t::geom_from, ls_eval_t::geom_to, ls_modifier_t::geom_to, ls_eval_t::knot_axes, ls_modifier_t::knot_axes, ls_eval_t::knot_c, ls_modifier_t::knot_c, ls_eval_t::knot_n, ls_modifier_t::knot_n, ls_eval_t::knot_r, ls_modifier_t::knot_r, ls_eval_t::knot_v, ls_modifier_t::knot_v, ls_eval_t::knot_vn, ls_modifier_t::knot_vn, ls_eval_t::knot_vr, ls_modifier_t::knot_vr, LS_ENABLE_TCA, LS_EVAL_TCA_LINEAR, ls_calib_dist_t::model, ls_calib_tca_t::model, ls_calib_vig_t::model, ls_eval_t::norm_scale, ls_modifier_t::norm_scale, ls_eval_t::norm_unscale, ls_modifier_t::norm_unscale, ls_eval_t::reverse, ls_modifier_t::reverse, ls_eval_t::scale, ls_modifier_t::scale, ls_modifier_t::tca, ls_eval_t::tca_model, ls_eval_t::tca_terms, ls_calib_dist_t::terms, ls_calib_tca_t::terms, ls_calib_vig_t::terms, ls_modifier_t::vig, ls_eval_t::vig_center_x, ls_eval_t::vig_center_y, ls_eval_t::vig_model, ls_eval_t::vig_scale, and ls_eval_t::vig_terms.

Referenced by ls_modifier_apply_subpixel_geometry(), ls_modifier_apply_vignetting(), and ls_modifier_autoscale().

◆ ls_modifier_apply_subpixel_geometry()

int ls_modifier_apply_subpixel_geometry ( const ls_modifier_t * mod,
float xu,
float yu,
int width,
int height,
float * res )

The geometry map: for count output pixels starting at (xu, yu), write 6 floats per pixel — source coordinates for R, G, B — in pixel space. Bit-for-bit the contract of lfModifier::ApplySubpixelGeometryDistortion(), so a caller can compare the two buffers element-wise.

Definition at line 844 of file lensserious.c.

References ls_eval_from_modifier(), and ls_eval_map().

◆ ls_modifier_apply_vignetting()

int ls_modifier_apply_vignetting ( const ls_modifier_t * mod,
float xu,
float yu,
int width,
int height,
float * rgba,
int row_stride_bytes )

pa vignetting, multiplied in place over RGBA float rows. Contract of lfModifier::ApplyColorModification(LF_CR_4(RED,GREEN,BLUE,UNKNOWN), F32).

Definition at line 871 of file lensserious.c.

References ls_modifier_t::enabled, LS_ENABLE_VIGNETTING, ls_eval_from_modifier(), ls_eval_vignette_from_r2(), LS_RESTRICT, ls_eval_t::vig_center_x, ls_eval_t::vig_center_y, and ls_eval_t::vig_scale.

◆ ls_modifier_autoscale()

float ls_modifier_autoscale ( const ls_modifier_t * mod)

The scale that just removes the black borders a correction leaves behind.

Parameters
moda resolved modifier. Its own scale factor, if it has one, is part of the transform being measured – exactly as upstream measures whatever callbacks are registered at the time.
Returns
the linear factor to feed back as ls_modifier_init()'s scale, or 1.0 when the modifier transforms no coordinates at all.

Ported from lfModifier::GetAutoScale(). Eight points around the frame – the four edge midpoints and the four corners – are pushed through the coordinate chain, and the largest ratio of where a point should be to where it landed is the scale. It is a measurement, not a formula: there is no closed form for "where does the corner of a distorted, reprojected frame end up", so each point is found by Newton iteration on the chain itself with a numeric derivative.

Two upstream constants are kept rather than tidied, because they are what its renders were produced with: a flat 1.001 margin ("1 permille is our limit of accuracy"), and a second 1.001 when TCA is active, since the per-channel radii extend slightly past the green one that was measured.

Note
Where a point cannot be found – ultrawide fisheye corners extending to infinity – upstream lets the iteration fail and the point simply loses the max(). Same here.

Definition at line 662 of file lensserious.c.

References _autoscale_distance(), ls_modifier_t::enabled, ls_modifier_t::height, LS_ENABLE_DISTORTION, LS_ENABLE_GEOMETRY, LS_ENABLE_SCALE, LS_ENABLE_TCA, ls_eval_from_modifier(), ls_modifier_t::norm_scale, ls_modifier_t::reverse, and ls_modifier_t::width.

◆ ls_modifier_init()

int ls_modifier_init ( ls_modifier_t * mod,
const ls_lens_t * lens,
float crop,
int width,
int height,
float focal,
float aperture,
float distance,
float scale,
int target_type,
int flags,
int reverse )

Resolve a lens at one (crop, geometry, focal, aperture, distance, scale).

Parameters
modfilled in by this call; nothing in it is owned or must be freed.
lensthe lens, as data.
cropthe crop factor of the sensor the picture was TAKEN with.
width,heightthe image dimensions, in pixels.
focal,aperture,distancethe shooting configuration.
scalea linear scaling factor; 1.0 for none.
target_typethe projection the output should be in, as ls_lens_type_t. Pass the lens's own type (or LS_LENS_UNKNOWN) for no projection change; LS_ENABLE_GEOMETRY is only raised when it actually differs and the pair is radially expressible.
flagswhich LS_ENABLE_* axes to attempt.
reversenon-zero to resolve the REVERSE direction – the transform that takes a corrected coordinate back to where it came from in the source image, which is what a consumer needs to place masks and drawn shapes on an image it is correcting. It is not the same chain read backwards: upstream registers different callbacks at different priorities, so the composition order changes too (see ls_eval_t::reverse). An axis whose model cannot be inverted at these coefficients – poly3 distortion with k1 = 0, linear TCA with a zero term – is dropped from the returned flags exactly as upstream drops it.
Returns
the LS_ENABLE_* flags actually in effect, mirroring lensfun's oflags.

Definition at line 268 of file lensserious.c.

References _hugin_focal_correction(), _interp_dist(), _interp_real_focal(), _interp_tca(), _interp_vig(), ls_lens_t::aspect_ratio, ls_modifier_t::aspect_ratio_correction, ls_lens_t::center_x, ls_modifier_t::center_x, ls_lens_t::center_y, ls_modifier_t::center_y, ls_lens_t::crop_factor, ls_modifier_t::dist, ls_modifier_t::enabled, ls_modifier_t::geom_focal, ls_modifier_t::geom_from, ls_modifier_t::geom_to, ls_modifier_t::geometry_unsupported, ls_modifier_t::height, LS_DIST_POLY3, LS_ENABLE_DISTORTION, LS_ENABLE_GEOMETRY, LS_ENABLE_SCALE, LS_ENABLE_TCA, LS_ENABLE_VIGNETTING, LS_EVAL_FULL_FRAME_HALF_DIAG_MM, LS_LENS_EQUIRECTANGULAR, LS_LENS_PANORAMIC, LS_LENS_UNKNOWN, LS_TCA_LINEAR, ls_calib_dist_t::model, ls_calib_tca_t::model, ls_modifier_t::norm_scale, ls_modifier_t::norm_unscale, ls_modifier_t::reverse, ls_modifier_t::scale, ls_modifier_t::tca, ls_calib_dist_t::terms, ls_calib_tca_t::terms, ls_lens_t::type, ls_modifier_t::vig, and ls_modifier_t::width.

◆ ls_modifier_init_knots()

int ls_modifier_init_knots ( ls_modifier_t * mod,
const ls_knots_t * knots,
int width,
int height,
float scale,
int flags,
int reverse )

Resolve a maker's embedded profile, in place of a database lens.

Parameters
modfilled in by this call; nothing in it is owned or must be freed.
knotsthe table, as lifted from the file's metadata. Copied, not retained.
width,heightthe image dimensions, in pixels.
scalea linear scaling factor; 1.0 for none.
flagswhich LS_ENABLE_* axes to attempt. LS_ENABLE_TCA has no meaning here and is ignored: a maker's table is already per channel, so asking for distortion asks for the chromatic part of it too. LS_ENABLE_GEOMETRY likewise – there is no projection to change.
reversenon-zero for the reverse direction, as in ls_modifier_init().
Returns
the LS_ENABLE_* flags actually in effect. An axis with no knots is dropped.

The counterpart to ls_modifier_init(), and deliberately the only difference between the two paths: what comes out is an ls_modifier_t like any other, which ls_eval_from_modifier() flattens like any other, which the same evaluator and the same kernels consume like any other. A consumer that already corrects lenses from the database gains embedded profiles by choosing a different resolver – not by growing a second pixel path, and not by touching the one it has.

That is possible because the models agree on what a correction IS. Every one of them, polynomial or tabulated, answers the same question: at this radius, by what factor is the coordinate scaled. So the table takes the distortion slot in the chain and nothing else moves – scale still composes around it in priority order, and the caller can still ask ls_modifier_autoscale() what scale removes the borders, because that measures the chain rather than the model.

Note
reverse costs nothing per pixel, unlike the polynomial models. Inverting r -> r*cor(r) has no closed form for a polynomial, so those models pay a Newton iteration at every pixel; a piecewise-linear curve inverts by reading the same points the other way round – (r*cor(r), 1/cor(r)) – once, at this call, into a table the same size.

That inverse is exact AT the knots and second-order between them, since a segment that is straight going forwards is not straight coming back. Measured on a nine-knot profile with 2% of distortion at the corner, over a 6000x4000 frame: correcting a point and then un-correcting it returns it to within 0.13 px at worst, and the worst case sits at r = 0.94, mid-segment, exactly where the reasoning says it should. For scale, that is a sixth of the 0.8 px by which liblensfun's own vectorised row walk differs from liblensfun's own scalar answer. tests/knots.c is where the number comes from and will fail if it grows past a quarter of a pixel.

Note
The three channels' inverses land on three different sets of radii, which is why ls_eval_t carries a radius axis per channel where this input carries one shared. Forcing them back onto a common axis would mean resampling two of the three, and there is nothing to gain by it – the tables are already resident in the block either way.
flags decides which axes the table serves, and the distortion/TCA pair is not free. Pass both and the three per-channel curves are evaluated as measured. Pass DISTORTION alone and every channel follows the GREEN curve – the aberration is stripped exactly, since green IS the geometry the maker measured and the other two are defined relative to it – leaving another TCA model free to run after.

Passing TCA ALONE is accepted but does nothing useful, and deliberately so: there is no separate aberration in the table to apply. It would have to be the ratio of each channel's curve to green, resampled onto whatever radius axis the other resolver normalizes against – a different convention again (see ls_modifier_set_projection()) – and it would be the maker's departure from a geometry that is not theirs. A consumer wanting that combination should be prepared to answer the physical objection before the mechanical one.

Definition at line 540 of file lensserious.c.

References _knot_axis_ascends(), ls_modifier_t::aspect_ratio_correction, ls_modifier_t::center_x, ls_modifier_t::center_y, ls_knots_t::cor_rgb, ls_modifier_t::dist, ls_modifier_t::enabled, ls_modifier_t::geom_focal, ls_modifier_t::geom_from, ls_modifier_t::geom_to, ls_modifier_t::height, ls_modifier_t::knot_axes, ls_modifier_t::knot_c, ls_modifier_t::knot_n, ls_modifier_t::knot_r, ls_modifier_t::knot_v, ls_modifier_t::knot_vn, ls_modifier_t::knot_vr, LS_DIST_KNOTS, LS_ENABLE_DISTORTION, LS_ENABLE_SCALE, LS_ENABLE_TCA, LS_ENABLE_VIGNETTING, LS_LENS_UNKNOWN, LS_MAX_KNOTS, LS_VIG_KNOTS, ls_calib_dist_t::model, ls_calib_vig_t::model, ls_knots_t::n, ls_modifier_t::norm_scale, ls_modifier_t::norm_unscale, ls_knots_t::radius, ls_modifier_t::reverse, ls_modifier_t::scale, ls_knots_t::vig, ls_modifier_t::vig, ls_knots_t::vig_radius, ls_knots_t::vn, and ls_modifier_t::width.

◆ ls_modifier_set_projection()

int ls_modifier_set_projection ( ls_modifier_t * mod,
int from_type,
int to_type,
float focal_mm,
float crop_factor )

Add a projection change to an already-resolved modifier.

Parameters
moda modifier from either resolver. Its coordinate system is used as-is.
from_typethe projection the LENS produces, as ls_lens_type_t.
to_typethe projection wanted, as ls_lens_type_t.
focal_mmthe focal the projection runs on. For a lens carrying <real-focal-length> this is NOT the engraved focal – see ls_modifier_init(), which derives it and would pass the derived value.
crop_factorthe crop of the sensor whose HALF DIAGONAL this modifier's coordinates are normalized against. For a table-resolved modifier that is the camera that took the picture; for a database-resolved one it is the sensor the lens was CALIBRATED on, because the shooting crop is already folded into its norm_scale.
Returns
non-zero if a projection change was enabled.

ls_modifier_init() does this itself; this is for the other resolver. A maker's embedded table describes the lens in the projection it shipped with and says nothing about any other, so ls_modifier_init_knots() leaves both endpoints UNKNOWN – but a consumer that ALSO knows the lens's type, typically from a database entry matched alongside, can put the projection back and get the two composed.

That composes because the projection stage reads only the working radius and ls_modifier_t::geom_focal, and the one formula covers both coordinate systems:

geom_focal = focal_mm * crop * aspect_ratio_correction / (half of a 36x24 diagonal)

The aspect_ratio_correction in it is exactly what converts half-diagonal units into the half-short-side ones ls_modifier_init() normalizes to; a table-resolved modifier stores 1.0 there because it already measures radius against the half diagonal, so the same expression lands in the right units without a special case.

Note
Panoramic and equirectangular map x and y differently on either side, so a change involving one is declined rather than approximated: ls_modifier_t::geometry_unsupported is raised, the stage stays off, and this returns 0.

Definition at line 712 of file lensserious.c.

References ls_modifier_t::aspect_ratio_correction, ls_modifier_t::enabled, ls_modifier_t::geom_focal, ls_modifier_t::geom_from, ls_modifier_t::geom_to, ls_modifier_t::geometry_unsupported, LS_ENABLE_GEOMETRY, LS_EVAL_FULL_FRAME_HALF_DIAG_MM, LS_LENS_EQUIRECTANGULAR, LS_LENS_PANORAMIC, and LS_LENS_UNKNOWN.