|
LensSerious 0.1
Lens-correction mathematics as data, not as a library of callbacks
|
What this is, and what it deliberately is not. More...
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). | |
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.
| #define LS_ENABLE_DISTORTION (1 << 0) |
Definition at line 131 of file lensserious.h.
Referenced by _dng_axes(), _fuji_axes(), _olympus_axes(), _sony_axes(), ls_modifier_autoscale(), ls_modifier_init(), and ls_modifier_init_knots().
| #define LS_ENABLE_GEOMETRY (1 << 4) |
Definition at line 135 of file lensserious.h.
Referenced by ls_modifier_autoscale(), ls_modifier_init(), and ls_modifier_set_projection().
| #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().
| #define LS_ENABLE_TCA (1 << 1) |
Definition at line 132 of file lensserious.h.
Referenced by _dng_axes(), _fuji_axes(), _olympus_axes(), _sony_axes(), ls_eval_from_modifier(), ls_modifier_autoscale(), ls_modifier_init(), and ls_modifier_init_knots().
| #define LS_ENABLE_VIGNETTING (1 << 2) |
Definition at line 133 of file lensserious.h.
Referenced by _dng_axes(), _fuji_axes(), _sony_axes(), ls_modifier_apply_vignetting(), ls_modifier_init(), ls_modifier_init_knots(), and ls_vendor_resolve().
| typedef struct ls_calib_dist_t ls_calib_dist_t |
One calibration entry, at one focal length (vignetting: one focal/aperture/distance).
| 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.
| typedef struct ls_calib_tca_t ls_calib_tca_t |
| typedef struct ls_calib_vig_t ls_calib_vig_t |
| typedef enum ls_dist_model_t ls_dist_model_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:
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.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.
| typedef struct ls_lens_t ls_lens_t |
A lens as data: coefficients plus the calibration sensor's identity.
| typedef enum ls_lens_type_t ls_lens_type_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.
| typedef enum ls_tca_model_t ls_tca_model_t |
| typedef enum ls_vig_model_t ls_vig_model_t |
| anonymous enum |
| Enumerator | |
|---|---|
| LS_MAX_CALIB | |
Definition at line 92 of file lensserious.h.
| enum 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.
| enum ls_lens_type_t |
Definition at line 94 of file lensserious.h.
| enum 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.
| enum 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.
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.
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.
| 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.
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().
| 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().
| 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.
| float ls_modifier_autoscale | ( | const ls_modifier_t * | mod | ) |
The scale that just removes the black borders a correction leaves behind.
| mod | a 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. |
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.
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.
| 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).
| mod | filled in by this call; nothing in it is owned or must be freed. |
| lens | the lens, as data. |
| crop | the crop factor of the sensor the picture was TAKEN with. |
| width,height | the image dimensions, in pixels. |
| focal,aperture,distance | the shooting configuration. |
| scale | a linear scaling factor; 1.0 for none. |
| target_type | the 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. |
| flags | which LS_ENABLE_* axes to attempt. |
| reverse | non-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. |
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.
| 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.
| mod | filled in by this call; nothing in it is owned or must be freed. |
| knots | the table, as lifted from the file's metadata. Copied, not retained. |
| width,height | the image dimensions, in pixels. |
| scale | a linear scaling factor; 1.0 for none. |
| flags | which 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. |
| reverse | non-zero for the reverse direction, as in ls_modifier_init(). |
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.
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.
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.
| 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.
| mod | a modifier from either resolver. Its coordinate system is used as-is. |
| from_type | the projection the LENS produces, as ls_lens_type_t. |
| to_type | the projection wanted, as ls_lens_type_t. |
| focal_mm | the 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_factor | the 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. |
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.
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.