From cc89dca5a216eced7ebed84eba432896b074443c Mon Sep 17 00:00:00 2001 From: Christoph Hasse Date: Thu, 3 Jun 2021 00:03:06 +0200 Subject: [PATCH] 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. --- tests/lens_tests/test_canon_lenses.py | 39 +++++++++++----------- tests/lens_tests/utils.py | 47 ++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/tests/lens_tests/test_canon_lenses.py b/tests/lens_tests/test_canon_lenses.py index 3217574d..37f5200a 100644 --- a/tests/lens_tests/test_canon_lenses.py +++ b/tests/lens_tests/test_canon_lenses.py @@ -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"]), }, ) diff --git a/tests/lens_tests/utils.py b/tests/lens_tests/utils.py index 31e7afa6..fa03cf86 100644 --- a/tests/lens_tests/utils.py +++ b/tests/lens_tests/utils.py @@ -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 -