feat: rework lens test to account for ambiguous lenses
For each lens, its test target is now defined as the list of all lenses which are possible given that lenses exif values.
This commit is contained in:
parent
a70896c1ac
commit
cc89dca5a2
@ -4,7 +4,7 @@ import re
|
||||
import os
|
||||
import system_tests
|
||||
import math
|
||||
from lens_tests.utils import extract_lenses_from_cpp
|
||||
from lens_tests.utils import extract_lenses_from_cpp, make_test_cases
|
||||
|
||||
# get directory of the current file
|
||||
file_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
@ -14,38 +14,37 @@ canon_lens_file = os.path.abspath(os.path.join(file_dir, "./../../src/canonmn_in
|
||||
startpattern = "constexpr TagDetails canonCsLensType[] = {"
|
||||
# use utils function to extract all lenses
|
||||
lenses = extract_lenses_from_cpp(canon_lens_file, startpattern)
|
||||
# use utils function to define test case data
|
||||
test_cases = make_test_cases(lenses)
|
||||
|
||||
# see https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/Canon.pm#L9678
|
||||
def aperture_to_raw_exif(aperture):
|
||||
#see https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/Canon.pm#L9678
|
||||
aperture = float(aperture)
|
||||
# for apertures < 1 the below is negative
|
||||
num = math.log(aperture)*2/math.log(2)
|
||||
num = math.log(aperture) * 2 / math.log(2)
|
||||
|
||||
# temporarily make the number positive
|
||||
if (num < 0):
|
||||
if num < 0:
|
||||
num = -num
|
||||
sign = -1
|
||||
else:
|
||||
sign = 1;
|
||||
sign = 1
|
||||
|
||||
val = int(num)
|
||||
frac = num - val
|
||||
|
||||
if (abs(frac - 0.33) < 0.05):
|
||||
frac = 0x0c
|
||||
elif (abs(frac - 0.67) < 0.05):
|
||||
frac = 0x14;
|
||||
if abs(frac - 0.33) < 0.05:
|
||||
frac = 0x0C
|
||||
elif abs(frac - 0.67) < 0.05:
|
||||
frac = 0x14
|
||||
else:
|
||||
frac = int(frac * 0x20 + 0.5)
|
||||
|
||||
return sign * (val * 0x20 + frac);
|
||||
return sign * (val * 0x20 + frac)
|
||||
|
||||
|
||||
for (lens_id, lens_desc, meta) in lenses:
|
||||
for lens_tc in test_cases:
|
||||
|
||||
tc = float(meta["tc"] or 1)
|
||||
|
||||
testname = lens_id + "_" + lens_desc
|
||||
testname = lens_tc["id"] + "_" + lens_tc["desc"]
|
||||
|
||||
globals()[testname] = system_tests.CaseMeta(
|
||||
"canon_lenses." + testname,
|
||||
@ -58,10 +57,10 @@ for (lens_id, lens_desc, meta) in lenses:
|
||||
"stderr": [""],
|
||||
"stdout": ["Exif.CanonCs.LensType Short 1 $lens_description\n"],
|
||||
"retval": [0],
|
||||
"lens_id": lens_id,
|
||||
"lens_description": lens_desc,
|
||||
"aperture_max": aperture_to_raw_exif(meta["aperture_max_short"]),
|
||||
"focal_length_min": int(int(meta["focal_length_min"]) * tc),
|
||||
"focal_length_max": int(int(meta["focal_length_max"]) * tc),
|
||||
"lens_id": lens_tc["id"],
|
||||
"lens_description": lens_tc["target"],
|
||||
"aperture_max": aperture_to_raw_exif(lens_tc["aperture_max_short"]),
|
||||
"focal_length_min": int(lens_tc["focal_length_min"] * lens_tc["tc"]),
|
||||
"focal_length_max": int(lens_tc["focal_length_max"] * lens_tc["tc"]),
|
||||
},
|
||||
)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import re
|
||||
import os
|
||||
import logging
|
||||
from itertools import groupby
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -49,17 +50,54 @@ def extract_meta(text, pattern=LENS_META_DEFAULT_RE):
|
||||
"""
|
||||
result = pattern.match(text)
|
||||
|
||||
if(not result):
|
||||
if not result:
|
||||
# didn't match
|
||||
return None
|
||||
|
||||
ret = result.groupdict()
|
||||
# set min to max value if we didn't get a range but a single value
|
||||
ret['focal_length_min'] = ret['focal_length_min'] or ret['focal_length_max']
|
||||
ret['aperture_max_short'] = ret['aperture_max_short'] or ret['aperture_max_tele']
|
||||
ret["focal_length_min"] = int(ret["focal_length_min"] or ret["focal_length_max"])
|
||||
ret["focal_length_max"] = int(ret["focal_length_max"])
|
||||
ret["aperture_max_short"] = float(ret["aperture_max_short"] or ret["aperture_max_tele"])
|
||||
ret["aperture_max_tele"] = float(ret["aperture_max_tele"])
|
||||
ret["tc"] = float(ret["tc"] or 1)
|
||||
return ret
|
||||
|
||||
|
||||
# FIXME explain somwhere that lens_is_match(l1,l2) does not imply lens_is_match(l2,l1)
|
||||
# becuse we don't have short and tele aperture values in exif
|
||||
def lens_is_match(l1, l2):
|
||||
"""
|
||||
Test if lens l2 is compatible with lens l1,
|
||||
assuming we write l1's metadata and apeture_max_short into exif
|
||||
"""
|
||||
return (
|
||||
all([l1[k] == l2[k] for k in ["tc", "focal_length_min", "focal_length_max"]])
|
||||
and l2["aperture_max_short"] <= l1["aperture_max_short"] <= l2["aperture_max_tele"]
|
||||
)
|
||||
|
||||
|
||||
def make_test_cases(lenses):
|
||||
"""
|
||||
Creates a test case for each lens
|
||||
Main job of this function is to collect all ambiguous lenses and define a test target
|
||||
as the " *OR* " joined string of all ambiguous lens descriptions
|
||||
"""
|
||||
test_cases = []
|
||||
for lens_id, group in groupby(lenses, lambda x: x["id"]):
|
||||
lens_group = list(group)
|
||||
test_cases += [
|
||||
{
|
||||
**lens["meta"],
|
||||
"id": lens["id"],
|
||||
"desc": lens["desc"],
|
||||
"target": " *OR* ".join([l["desc"] for l in lens_group if lens_is_match(lens["meta"], l["meta"])]),
|
||||
}
|
||||
for lens in lens_group
|
||||
]
|
||||
return test_cases
|
||||
|
||||
|
||||
def extract_lenses_from_cpp(filename, start_pattern):
|
||||
"""
|
||||
Extract lens information from the lens descritpions array in a maker note cpp file
|
||||
@ -99,6 +137,5 @@ def extract_lenses_from_cpp(filename, start_pattern):
|
||||
log.error(f"Failure extracing metadata from lens description: {lens_entry[0]}: {lens_entry[1]}.")
|
||||
continue
|
||||
|
||||
lenses.append((*lens_entry, meta))
|
||||
lenses.append({"id": lens_entry[0], "desc": lens_entry[1], "meta": meta})
|
||||
return lenses
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user