|
LensSerious 0.1
Lens-correction mathematics as data, not as a library of callbacks
|
#include "lensserious_db.h"#include <sqlite3.h>#include <stdio.h>#include <stdlib.h>#include <string.h>
Include dependency graph for lensserious_db.c:Go to the source code of this file.
Data Structures | |
| struct | ls_db_t |
| struct | ls_tokens_t |
| A tokenised name, with everything the comparison needs precomputed. More... | |
Macros | |
| #define | LS_DB_SCHEMA_VERSION 4 |
Enumerations | |
| enum | { LS_MAX_TOKENS = 32 , LS_TOKEN_LEN = 48 } |
Functions | |
| static void | _db_err (ls_db_t *db, const char *what) |
| static char * | _db_build_uri (const char *path) |
| Build the URI this library insists on, whatever the caller passed. | |
| ls_db_t * | ls_db_open (const char *path) |
| Open a database for reading. | |
| void | ls_db_close (ls_db_t *db) |
| Release a handle. Safe on NULL. | |
| const char * | ls_db_error (const ls_db_t *db) |
The last error on db, as a string owned by db, or NULL. | |
| int | ls_db_schema_version (const ls_db_t *db) |
| Schema version of the open file, or -1. Bumped when the layout changes. | |
| size_t | ls_db_normalize (const char *in, char *out, size_t out_size) |
| Case-fold ASCII, drop punctuation, collapse whitespace. | |
| static int | _fill_calibrations (ls_db_t *db, long long lens_id, ls_lens_t *out) |
| int | ls_db_lens_by_id (ls_db_t *db, long long lens_id, ls_lens_t *out) |
| Load a lens by its database id, for a caller that already resolved one. | |
| static long long | _find_lens_id (ls_db_t *db, const char *maker, const char *model, float crop) |
The lens id whose names match, preferring the calibration crop nearest crop. | |
| int | ls_db_find_lens (ls_db_t *db, const char *maker, const char *model, float crop, ls_lens_t *out) |
Find a lens by maker and model, and fill out with its coefficients. | |
| int | ls_db_find_camera (ls_db_t *db, const char *maker, const char *model, ls_camera_t *out) |
| Find a camera by maker and model. | |
| int | ls_db_lens_fits_mount (ls_db_t *db, long long lens_id, long long mount_id) |
| Does a lens fit a camera's mount, upstream's compatibility table included? | |
| int | ls_db_list_lenses (ls_db_t *db, long long *out_ids, int max) |
| Enumerate lens ids, oldest-inserted first, for tests and for a GUI's lens picker. | |
| int | ls_db_lens_name (ls_db_t *db, long long lens_id, char *maker, size_t maker_size, char *model, size_t model_size) |
| The lens's maker/model, as stored. | |
| int | ls_db_lens_range (ls_db_t *db, long long lens_id, float *min_focal, float *max_focal, float *min_aperture, float *max_aperture) |
| The lens's focal and aperture range, for a picker that lists it. | |
| int | ls_db_lens_mounts (ls_db_t *db, long long lens_id, char *out, size_t out_size) |
| The mounts a lens is made for, joined with ", ". | |
| int | ls_db_list_cameras (ls_db_t *db, long long *out_ids, int max) |
| Enumerate camera ids, for a GUI's camera picker. | |
| int | ls_db_camera_name (ls_db_t *db, long long camera_id, char *maker, size_t maker_size, char *model, size_t model_size, char *variant, size_t variant_size) |
| A camera's maker, model and variant, as stored. | |
| int | ls_db_camera_by_id (ls_db_t *db, long long camera_id, ls_camera_t *out) |
| Load a camera by its database id, for a caller that already resolved one. | |
| int | ls_db_mount_name (ls_db_t *db, long long mount_id, char *out, size_t out_size) |
| A mount's name. | |
| int | ls_db_lenses_for_mount (ls_db_t *db, long long mount_id, long long *out_ids, int max) |
| The lenses made for one mount. | |
| int | ls_db_meta (ls_db_t *db, const char *key, char *out, size_t out_size) |
A meta value by key (built_utc, source, lensfun_db_version, ...). | |
| int | ls_db_tokenize (const char *norm, char *out_tokens, int max, int stride) |
| Split a normalised name on spaces, and split letter/digit runs apart. | |
| unsigned | ls_db_token_hash (const char *token) |
| FNV-1a of a token, the hash the stored digest and the matcher both use. | |
| size_t | ls_db_token_digest (const char *norm, unsigned char *out, size_t out_size) |
Pack a normalised name's tokens into the digest stored in lens_name.tokens. | |
| static int | _digest_load (const unsigned char *blob, int bytes, ls_tokens_t *out) |
| Read a digest back into the arrays _score_tokens() compares. | |
| static void | _tokenize (const char *norm, ls_tokens_t *out) |
| static float | _score_tokens (const ls_tokens_t *pat, const ls_tokens_t *cand) |
How well pat's tokens are covered by cand's, 0..100. | |
| static void | _parse_focal_range (const char *name, float *minf, float *maxf) |
| Pull the focal range out of a lens NAME, the way upstream does. | |
| static int | _compare_num (const float a, const float b) |
| Upstream's _lf_compare_num(): a numeric field as a filter, not a score. | |
| static float | _crop_score (const float cam, const float calib) |
| Upstream's crop-factor rule, as a bonus ADDED to a candidate's score. | |
| int | ls_db_match_lens (ls_db_t *db, const char *maker, const char *model, long long mount_id, float crop, ls_db_match_t *out, int max) |
| Find the lenses a free-text name most likely refers to. | |
| #define LS_DB_SCHEMA_VERSION 4 |
Definition at line 19 of file lensserious_db.c.
Referenced by ls_db_open().
| anonymous enum |
| Enumerator | |
|---|---|
| LS_MAX_TOKENS | |
| LS_TOKEN_LEN | |
Definition at line 730 of file lensserious_db.c.
|
static |
Upstream's _lf_compare_num(): a numeric field as a filter, not a score.
Definition at line 999 of file lensserious_db.c.
Referenced by ls_db_match_lens().
|
static |
Upstream's crop-factor rule, as a bonus ADDED to a candidate's score.
| cam | the camera's crop factor; 0 to ignore the rule entirely. |
| calib | the crop factor of the sensor the lens was CALIBRATED on. |
Ported from _lf_lens_compare_score() (lens.cpp), including its numbers. A calibration measured on a sensor more than 4% larger than the camera's is rejected: it does not cover the frame, and no amount of name similarity makes it the right answer. What survives is graded, and the grading is not monotonic in the obvious direction – the peak is a camera slightly SMALLER than the calibration sensor (1.01x to 1.11x), not an exact match, because a calibration measured on a marginally wider frame covers the whole of this one.
ADDED, not multiplied, and that distinction was measured rather than assumed. Upstream's crop term is 2 to 10 against a total in the tens, so it breaks ties between similarly named lenses without overriding the name. Scaling a 0..100 token score by it instead lets it dominate: a lens whose name matched 90% on a 1.534 sensor beat an EXACT name match on a 1.611 one, 90 against 50, and the whole-database check fell to 94.1%. Upstream's own numbers, on the same side of the operator upstream puts them, are the right port.
Definition at line 1028 of file lensserious_db.c.
Referenced by ls_db_match_lens().
|
static |
Build the URI this library insists on, whatever the caller passed.
mode=ro and immutable=1 are not the caller's to choose: every promise in the header rests on them. A caller's own parameters are kept, so a path on a network share can still carry e.g. nolock=1.
A bare path has to be percent-encoded before it can become a URI – '?' and '#' in a directory name would otherwise be read as the start of the query, and SQLite would open some other file, or none.
Definition at line 46 of file lensserious_db.c.
Referenced by ls_db_open().
|
static |
Definition at line 28 of file lensserious_db.c.
References ls_db_t::error, and ls_db_t::sql.
Referenced by _fill_calibrations(), _find_lens_id(), ls_db_camera_by_id(), ls_db_camera_name(), ls_db_find_camera(), ls_db_lens_by_id(), ls_db_lens_fits_mount(), ls_db_lens_mounts(), ls_db_lens_name(), ls_db_lens_range(), ls_db_lenses_for_mount(), ls_db_list_cameras(), ls_db_list_lenses(), ls_db_match_lens(), ls_db_meta(), and ls_db_mount_name().
|
static |
Read a digest back into the arrays _score_tokens() compares.
Definition at line 817 of file lensserious_db.c.
References ls_tokens_t::bloom, ls_tokens_t::h, ls_tokens_t::len, LS_MAX_TOKENS, and ls_tokens_t::n.
Referenced by ls_db_match_lens().
Definition at line 189 of file lensserious_db.c.
References _db_err(), ls_calib_vig_t::aperture, ls_lens_t::dist, ls_calib_vig_t::distance, ls_db_t::error, ls_calib_dist_t::focal, ls_calib_real_focal_t::focal, ls_calib_tca_t::focal, ls_calib_vig_t::focal, LS_MAX_CALIB, ls_calib_dist_t::model, ls_calib_tca_t::model, ls_calib_vig_t::model, ls_lens_t::n_dist, ls_lens_t::n_real_focal, ls_lens_t::n_tca, ls_lens_t::n_vig, ls_calib_real_focal_t::real_focal, ls_lens_t::real_focal, ls_db_t::sql, ls_lens_t::tca, ls_calib_dist_t::terms, ls_calib_tca_t::terms, ls_calib_vig_t::terms, and ls_lens_t::vig.
Referenced by ls_db_lens_by_id().
|
static |
The lens id whose names match, preferring the calibration crop nearest crop.
Definition at line 332 of file lensserious_db.c.
References _db_err(), ls_db_normalize(), and ls_db_t::sql.
Referenced by ls_db_find_lens().
|
static |
Pull the focal range out of a lens NAME, the way upstream does.
| name | the RAW name, as the caller supplied it. Not the normalised form: that collapses punctuation, and the dash in "16-35mm" is load-bearing here. |
| minf,maxf | filled with the focal range in mm, or left at 0 when the name carries none. |
lensfun's FindLenses() runs GuessParameters() on the query string, which regexes the focal length and aperture out of it and then uses them as HARD FILTERS: a candidate whose focal range differs by more than 1% is rejected outright (_lf_compare_num returns -1 and the score becomes 0). Without this, a query is only a bag of tokens, and "Sigma 50mm f/1.4 DG HSM" can resolve to a Sigma 20mm f/1.4 DG HSM – measured, on a real image, with the calibration silently wrong by a factor of 2.5 in focal length.
Hand-scanned rather than regexed on purpose: <regex.h> is POSIX and this has to build with MSVC. The rule implemented is upstream's first and by far most common pattern – a number, optionally a dash and a second number, then "mm" – which is what real EXIF lens names carry. Names it cannot read leave the range at 0, which the filter treats as "no opinion", exactly as upstream's neutral case does. Agreement is measured over the whole database by tests/match_lensfun.c rather than argued from the regex.
Definition at line 934 of file lensserious_db.c.
Referenced by ls_db_match_lens().
|
static |
How well pat's tokens are covered by cand's, 0..100.
Asymmetric on purpose. The pattern is what the camera wrote and the candidate is the catalogue entry, so every pattern token ought to appear in the candidate – missing one is evidence against the match. The reverse is not true: the catalogue is more verbose than any EXIF field, and penalising it for that would favour the shortest name in the database for every query.
Unmatched candidate tokens are still worth a small penalty, or "35mm" would score the same against every 35mm lens ever made; it is just much smaller than the forward one.
Definition at line 865 of file lensserious_db.c.
References ls_tokens_t::h, ls_tokens_t::len, LS_MAX_TOKENS, and ls_tokens_t::n.
Referenced by ls_db_match_lens().
|
static |
Definition at line 840 of file lensserious_db.c.
References ls_tokens_t::bloom, ls_tokens_t::h, ls_tokens_t::len, ls_db_token_hash(), ls_db_tokenize(), LS_MAX_TOKENS, LS_TOKEN_LEN, ls_tokens_t::n, and ls_tokens_t::t.
Referenced by ls_db_match_lens().
| int ls_db_camera_by_id | ( | ls_db_t * | db, |
| long long | camera_id, | ||
| ls_camera_t * | out ) |
Load a camera by its database id, for a caller that already resolved one.
Definition at line 616 of file lensserious_db.c.
References _db_err(), ls_camera_t::crop_factor, ls_camera_t::id, ls_camera_t::mount_id, and ls_db_t::sql.
| int ls_db_camera_name | ( | ls_db_t * | db, |
| long long | camera_id, | ||
| char * | maker, | ||
| size_t | maker_size, | ||
| char * | model, | ||
| size_t | model_size, | ||
| char * | variant, | ||
| size_t | variant_size ) |
A camera's maker, model and variant, as stored.
| db | an open database. |
| camera_id | the camera. |
| maker,model,variant | caller's buffers; any may be NULL. variant is set to the empty string for the cameras that have none, which is most of them. |
| maker_size,model_size,variant_size | their sizes. |
Definition at line 585 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| void ls_db_close | ( | ls_db_t * | db | ) |
Release a handle. Safe on NULL.
Definition at line 126 of file lensserious_db.c.
References ls_db_t::sql.
Referenced by ls_db_open().
| const char * ls_db_error | ( | const ls_db_t * | db | ) |
The last error on db, as a string owned by db, or NULL.
Definition at line 133 of file lensserious_db.c.
References ls_db_t::error.
| int ls_db_find_camera | ( | ls_db_t * | db, |
| const char * | maker, | ||
| const char * | model, | ||
| ls_camera_t * | out ) |
Find a camera by maker and model.
Matching is exact on the normalised form of each name – case-folded, with punctuation and runs of whitespace collapsed – against every spelling the database holds for that camera, translations included. maker may be NULL to search on the model alone.
This is NOT lensfun's fuzzy scorer, which also tolerates missing words, reordered tokens and vendor prefixes. Porting it is separate work; until then a caller that needs that behaviour should treat a miss here as "not found" rather than as "no such camera".
Definition at line 374 of file lensserious_db.c.
References _db_err(), ls_camera_t::crop_factor, ls_camera_t::id, ls_db_normalize(), ls_camera_t::mount_id, and ls_db_t::sql.
| int ls_db_find_lens | ( | ls_db_t * | db, |
| const char * | maker, | ||
| const char * | model, | ||
| float | crop, | ||
| ls_lens_t * | out ) |
Find a lens by maker and model, and fill out with its coefficients.
Same matching as ls_db_find_camera(). When several lenses share a name – upstream distinguishes them by the sensor they were calibrated on – the one whose crop factor is closest to crop wins, matching how a caller would pick by hand; pass 0 to take the first.
out is filled completely, calibration arrays included, and is thereafter independent of db: the handle may be closed and the value stays valid. That is the whole shape of this library – a lens is data, not a handle into a database.
Definition at line 363 of file lensserious_db.c.
References _find_lens_id(), ls_db_lens_by_id(), and ls_db_t::sql.
Load a lens by its database id, for a caller that already resolved one.
Definition at line 295 of file lensserious_db.c.
References _db_err(), _fill_calibrations(), ls_lens_t::aspect_ratio, ls_lens_t::center_x, ls_lens_t::center_y, ls_lens_t::crop_factor, ls_lens_t::max_focal, ls_lens_t::min_focal, ls_db_t::sql, and ls_lens_t::type.
Referenced by ls_db_find_lens().
| int ls_db_lens_fits_mount | ( | ls_db_t * | db, |
| long long | lens_id, | ||
| long long | mount_id ) |
Does a lens fit a camera's mount, upstream's compatibility table included?
Definition at line 411 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_lens_mounts | ( | ls_db_t * | db, |
| long long | lens_id, | ||
| char * | out, | ||
| size_t | out_size ) |
The mounts a lens is made for, joined with ", ".
| db | an open database. |
| lens_id | the lens. |
| out | caller's buffer; always NUL-terminated when out_size is non-zero. |
| out_size | its size. |
Definition at line 527 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_lens_name | ( | ls_db_t * | db, |
| long long | lens_id, | ||
| char * | maker, | ||
| size_t | maker_size, | ||
| char * | model, | ||
| size_t | model_size ) |
The lens's maker/model, as stored.
Definition at line 460 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_lens_range | ( | ls_db_t * | db, |
| long long | lens_id, | ||
| float * | min_focal, | ||
| float * | max_focal, | ||
| float * | min_aperture, | ||
| float * | max_aperture ) |
The lens's focal and aperture range, for a picker that lists it.
| db | an open database. |
| lens_id | the lens. |
| min_focal,max_focal,min_aperture,max_aperture | filled in; any may be NULL. |
Definition at line 496 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_lenses_for_mount | ( | ls_db_t * | db, |
| long long | mount_id, | ||
| long long * | out_ids, | ||
| int | max ) |
The lenses made for one mount.
| db | an open database. |
| mount_id | the mount. |
| out_ids | caller's array, or NULL to count only. |
| max | the length of out_ids. |
out_ids is NULL), or -1.Definition at line 668 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_list_cameras | ( | ls_db_t * | db, |
| long long * | out_ids, | ||
| int | max ) |
Enumerate camera ids, for a GUI's camera picker.
| db | an open database. |
| out_ids | caller's array, or NULL to count only. |
| max | the length of out_ids. |
out_ids is NULL), or -1. Definition at line 564 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_list_lenses | ( | ls_db_t * | db, |
| long long * | out_ids, | ||
| int | max ) |
Enumerate lens ids, oldest-inserted first, for tests and for a GUI's lens picker.
| db | an open database. |
| out_ids | caller's array, or NULL to count only. |
| max | the length of out_ids. |
out_ids is NULL), or -1. Definition at line 436 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_match_lens | ( | ls_db_t * | db, |
| const char * | maker, | ||
| const char * | model, | ||
| long long | mount_id, | ||
| float | crop, | ||
| ls_db_match_t * | out, | ||
| int | max ) |
Find the lenses a free-text name most likely refers to.
ls_db_find_lens() answers "is there a lens called exactly this". This answers the question a raw file actually asks, where the EXIF string is a vendor's abbreviation of the name upstream chose – "16-35mm f/4G ED VR" against "Nikon AF-S Nikkor 16-35mm f/4G ED VR", with tokens missing, reordered, or spelled differently.
Scoring is token-based and deliberately simple; see the implementation for what each weight is and why. It is calibrated against liblensfun's own decisions rather than against a specification: tests/match_lensfun.c asks both this and lf_db_find_lenses_hd() the same questions over the whole database and reports where they disagree.
| maker | may be NULL. When given it is scored, not required – vendors disagree with upstream about their own name often enough that requiring it loses more than it saves. |
| mount_id | when > 0, only lenses that fit this mount are considered (ls_db_find_camera() supplies it). 0 considers every lens. |
| crop | the CAMERA's crop factor (ls_db_find_camera() supplies that too), or 0 to ignore it. It is not a tie-break: upstream REJECTS a lens whose calibration sensor is more than 4% larger than the camera's – such a calibration does not cover the frame – and then grades what is left by how closely the two match. Several lenses share a name and differ only in the sensor they were calibrated on, so without this the matcher can return a name-identical lens with the wrong calibration: measured on a Nikon D5300 (crop 1.534), a full-frame row instead of the 1.528 one, and 0.19 px of geometry. |
| db | an open database. |
| model | the free-text name to resolve. Required. |
| out | caller's array, max entries, filled best-first. |
| max | the length of out. |
Definition at line 1042 of file lensserious_db.c.
References _compare_num(), _crop_score(), _db_err(), _digest_load(), _parse_focal_range(), _score_tokens(), _tokenize(), ls_tokens_t::bloom, ls_db_match_t::lens_id, ls_db_normalize(), LS_MAX_TOKENS, LS_TOKEN_LEN, ls_tokens_t::n, ls_db_match_t::score, ls_db_t::sql, and ls_tokens_t::t.
| int ls_db_meta | ( | ls_db_t * | db, |
| const char * | key, | ||
| char * | out, | ||
| size_t | out_size ) |
A meta value by key (built_utc, source, lensfun_db_version, ...).
Definition at line 691 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| int ls_db_mount_name | ( | ls_db_t * | db, |
| long long | mount_id, | ||
| char * | out, | ||
| size_t | out_size ) |
A mount's name.
| db | an open database. |
| mount_id | the mount, as ls_db_find_camera() reported it. |
| out | caller's buffer; always NUL-terminated when out_size is non-zero. |
| out_size | its size. |
Definition at line 641 of file lensserious_db.c.
References _db_err(), and ls_db_t::sql.
| size_t ls_db_normalize | ( | const char * | in, |
| char * | out, | ||
| size_t | out_size ) |
Case-fold ASCII, drop punctuation, collapse whitespace.
The comparison form of a name: ASCII case-folded, punctuation dropped, runs of whitespace collapsed to one space.
Deliberately ASCII-only and byte-wise: it must produce the same bytes in the importer and here, on every platform, without a locale or an ICU version in the middle of it. Non-ASCII bytes pass through untouched, so a UTF-8 maker name still matches itself; it just does not case-fold, which no upstream name needs.
Definition at line 156 of file lensserious_db.c.
Referenced by _find_lens_id(), insert_name(), ls_db_find_camera(), and ls_db_match_lens().
| ls_db_t * ls_db_open | ( | const char * | path | ) |
Open a database for reading.
| path | a filesystem path, or a file: URI. Either way it is opened read-only and immutable; any query parameters the caller supplies in a URI are preserved, but mode and immutable are forced. |
Definition at line 87 of file lensserious_db.c.
References _db_build_uri(), ls_db_close(), LS_DB_SCHEMA_VERSION, ls_db_t::schema_version, and ls_db_t::sql.
| int ls_db_schema_version | ( | const ls_db_t * | db | ) |
Schema version of the open file, or -1. Bumped when the layout changes.
Definition at line 138 of file lensserious_db.c.
References ls_db_t::schema_version.
| size_t ls_db_token_digest | ( | const char * | norm, |
| unsigned char * | out, | ||
| size_t | out_size ) |
Pack a normalised name's tokens into the digest stored in lens_name.tokens.
Layout: uint16 n, then n x uint32 hash, then n x uint8 length. The matcher scores on those alone – it never sees the token text – which is what lets the candidate query read a blob out of a covering index instead of decoding and re-tokenising text on every lookup.
out is too small. Definition at line 794 of file lensserious_db.c.
References ls_db_token_hash(), ls_db_tokenize(), LS_MAX_TOKENS, and LS_TOKEN_LEN.
Referenced by insert_name().
| unsigned ls_db_token_hash | ( | const char * | token | ) |
FNV-1a of a token, the hash the stored digest and the matcher both use.
Definition at line 783 of file lensserious_db.c.
Referenced by _tokenize(), and ls_db_token_digest().
| int ls_db_tokenize | ( | const char * | norm, |
| char * | out_tokens, | ||
| int | max, | ||
| int | stride ) |
Split a normalised name on spaces, and split letter/digit runs apart.
The tokens ls_db_match_lens() compares, from an already-normalised name.
"16-35mm" normalises to "1635mm" – no. Normalisation drops '-', so the two halves of a focal range would fuse into one meaningless token and stop matching the catalogue's "16 35mm". Splitting where a digit meets a letter, and where a letter meets a digit, keeps "16", "35", "mm", "f", "4g" as separate comparable units.
Definition at line 758 of file lensserious_db.c.
Referenced by _tokenize(), insert_name(), and ls_db_token_digest().