FAILURE MAP
← Case archive

FA-79206 / Image orientation metadata / Open access

Centimetre resolution is converted with the inch ratio · case 01

Images whose resolution is in dots per centimetre print about four times too small.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The centimetre unit uses 2.54 mm instead of 10 mm per unit.

VERIFIED REPAIR

Use 10 mm per centimetre.

Unsuccessful approach: Using 100 confuses millimetres with tenths of a millimetre in the unit table.

Case contract

Input {w, h, tag?, unit?, xres: [num, den], yres?: [num, den]}. ResolutionUnit 2 is inches (25.4 mm), 3 is centimetres (10 mm), a missing unit defaults to 2 and any other unit has no physical size (None). A missing yres equals xres; non-positive rational parts give None. For tags 5..8 the displayed width comes from the stored height at YResolution. Return displayed [width, height] in tenths of a millimetre, each rounded half up from the exact value.

Why this case matters

Camera, phone and scanner images carry an orientation hint separately from the stored pixels; galleries, thumbnailers, editors and upload pipelines must interpret it consistently or photos appear sideways, mirrored or doubly rotated.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
    w, h = d['w'], d['h']
    tag = d.get('tag', 1)
    unit = d.get('unit', 2)
    per = {2: Fraction(254, 10), 3: Fraction(254, 100)}.get(unit)
    if per is None:
        return None
    xn, xd = d['xres']
    yn, yd = d.get('yres', d['xres'])
    if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
        return None
    xr = Fraction(xn, xd)
    yr = Fraction(yn, yd)
    if tag in (5, 6, 7, 8):
        w, h, xr, yr = h, w, yr, xr
    def tenths(px, r):
        return math.floor(px * per * 10 / r + Fraction(1, 2))
    return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 3942, 'h': 988, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1372, 5475]], [{'w': 456, 'h': 408, 'tag': 8, 'unit': 3, 'xres': [96, 1], 'yres': [96, 1]}, [425, 475]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 3491, 'h': 649, 'xres': [40, 1], 'yres': [1000, 3]}, [22168, 495]], [{'w': 1315, 'h': 446, 'tag': 6, 'unit': 2, 'xres': [1000, 3], 'yres': [1000, 3]}, [340, 1002]], [{'w': 3975, 'h': 1299, 'unit': 2, 'xres': [300, 1], 'yres': [-300, 1]}, None], [{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]]], [[{'w': 1140, 'h': 2204, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [2296, 2850]], [{'w': 2836, 'h': 866, 'tag': 2, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [851, 2165]], [{'w': 1360, 'h': 1067, 'tag': 7, 'xres': [300, 1]}, [903, 1151]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 3380, 'h': 2534, 'tag': 6, 'xres': [300, 1]}, [2145, 2862]], [{'w': 2562, 'h': 960, 'tag': 3, 'unit': 3, 'xres': [-72, 1], 'yres': [40, 1]}, None], [{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]]], [[{'w': 2235, 'h': 1712, 'tag': 8, 'unit': 3, 'xres': [40, 1]}, [4280, 5588]], [{'w': 2114, 'h': 2984, 'tag': 7, 'unit': 3, 'xres': [7200, 100]}, [4144, 2936]], [{'w': 2503, 'h': 3897, 'tag': 6, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 2, 'h': 3716, 'unit': 2, 'xres': [300, 1]}, [2, 3146]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 3374, 'h': 1982, 'tag': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [2857, 1510]], [{'w': 3954, 'h': 1399, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1943, 5492]]], [[{'w': 531, 'h': 2956, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [600, 1]}, [493, 1328]], [{'w': 3512, 'h': 2297, 'tag': 7, 'unit': 3, 'xres': [40, 1]}, [5743, 8780]], [{'w': 2120, 'h': 3278, 'tag': 5, 'xres': [1000, 3], 'yres': [40, 1]}, [20815, 1615]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 1975, 'h': 3847, 'tag': 8, 'unit': 2, 'xres': [72, 1], 'yres': [40, 1]}, [24428, 6967]], [{'w': 3043, 'h': 1338, 'tag': 8, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 1489, 'h': 2076, 'tag': 1, 'unit': 2, 'xres': [7200, 100], 'yres': [600, 1]}, [5253, 879]], [{'w': 3732, 'h': 1632, 'tag': 6, 'unit': 3, 'xres': [150, 2], 'yres': [72, 1]}, [2267, 4976]]], [[{'w': 3248, 'h': 3090, 'unit': 3, 'xres': [72, 1], 'yres': [300, 1]}, [4511, 1030]], [{'w': 690, 'h': 3589, 'tag': 6, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [3739, 1725]], [{'w': 3914, 'h': 1446, 'tag': 3, 'unit': 2, 'xres': [7200, 100], 'yres': [1000, 3]}, [13808, 1102]], [{'w': 2880, 'h': 3333, 'tag': 1, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 3908, 'h': 2121, 'tag': 6, 'unit': 1, 'xres': [96, 1], 'yres': [40, 1]}, None], [{'w': 690, 'h': 269, 'xres': [40, 1], 'yres': [72, 1]}, [4382, 949]], [{'w': 2499, 'h': 800, 'tag': 6, 'xres': [150, 2]}, [2709, 8463]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]]]]
labels = ["regression: centimetre unit factor", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("%s %d" % (labels[i % len(labels)], i), solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: centimetre unit factor 0[349, 1391][1372, 5475]Failed
repair trap 1[108, 121][425, 475]Failed
combined fault 2[3150, 1905][3150, 1905]Passed
control 3NoneNonePassed
control 4[22168, 495][22168, 495]Passed
boundary 5[340, 1002][340, 1002]Passed
boundary 6NoneNonePassed
control 7[77, 227][302, 892]Failed

SHA-256 / a142a1e5d4a339ff3a05d1b9c555daafb98c9add606a64818e417aeff8592826

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
    w, h = d['w'], d['h']
    tag = d.get('tag', 1)
    unit = d.get('unit', 2)
    per = {2: Fraction(254, 10), 3: Fraction(100)}.get(unit)
    if per is None:
        return None
    xn, xd = d['xres']
    yn, yd = d.get('yres', d['xres'])
    if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
        return None
    xr = Fraction(xn, xd)
    yr = Fraction(yn, yd)
    if tag in (5, 6, 7, 8):
        w, h, xr, yr = h, w, yr, xr
    def tenths(px, r):
        return math.floor(px * per * 10 / r + Fraction(1, 2))
    return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 3942, 'h': 988, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1372, 5475]], [{'w': 456, 'h': 408, 'tag': 8, 'unit': 3, 'xres': [96, 1], 'yres': [96, 1]}, [425, 475]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 3491, 'h': 649, 'xres': [40, 1], 'yres': [1000, 3]}, [22168, 495]], [{'w': 1315, 'h': 446, 'tag': 6, 'unit': 2, 'xres': [1000, 3], 'yres': [1000, 3]}, [340, 1002]], [{'w': 3975, 'h': 1299, 'unit': 2, 'xres': [300, 1], 'yres': [-300, 1]}, None], [{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]]], [[{'w': 1140, 'h': 2204, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [2296, 2850]], [{'w': 2836, 'h': 866, 'tag': 2, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [851, 2165]], [{'w': 1360, 'h': 1067, 'tag': 7, 'xres': [300, 1]}, [903, 1151]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 3380, 'h': 2534, 'tag': 6, 'xres': [300, 1]}, [2145, 2862]], [{'w': 2562, 'h': 960, 'tag': 3, 'unit': 3, 'xres': [-72, 1], 'yres': [40, 1]}, None], [{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]]], [[{'w': 2235, 'h': 1712, 'tag': 8, 'unit': 3, 'xres': [40, 1]}, [4280, 5588]], [{'w': 2114, 'h': 2984, 'tag': 7, 'unit': 3, 'xres': [7200, 100]}, [4144, 2936]], [{'w': 2503, 'h': 3897, 'tag': 6, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 2, 'h': 3716, 'unit': 2, 'xres': [300, 1]}, [2, 3146]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 3374, 'h': 1982, 'tag': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [2857, 1510]], [{'w': 3954, 'h': 1399, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1943, 5492]]], [[{'w': 531, 'h': 2956, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [600, 1]}, [493, 1328]], [{'w': 3512, 'h': 2297, 'tag': 7, 'unit': 3, 'xres': [40, 1]}, [5743, 8780]], [{'w': 2120, 'h': 3278, 'tag': 5, 'xres': [1000, 3], 'yres': [40, 1]}, [20815, 1615]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 1975, 'h': 3847, 'tag': 8, 'unit': 2, 'xres': [72, 1], 'yres': [40, 1]}, [24428, 6967]], [{'w': 3043, 'h': 1338, 'tag': 8, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 1489, 'h': 2076, 'tag': 1, 'unit': 2, 'xres': [7200, 100], 'yres': [600, 1]}, [5253, 879]], [{'w': 3732, 'h': 1632, 'tag': 6, 'unit': 3, 'xres': [150, 2], 'yres': [72, 1]}, [2267, 4976]]], [[{'w': 3248, 'h': 3090, 'unit': 3, 'xres': [72, 1], 'yres': [300, 1]}, [4511, 1030]], [{'w': 690, 'h': 3589, 'tag': 6, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [3739, 1725]], [{'w': 3914, 'h': 1446, 'tag': 3, 'unit': 2, 'xres': [7200, 100], 'yres': [1000, 3]}, [13808, 1102]], [{'w': 2880, 'h': 3333, 'tag': 1, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 3908, 'h': 2121, 'tag': 6, 'unit': 1, 'xres': [96, 1], 'yres': [40, 1]}, None], [{'w': 690, 'h': 269, 'xres': [40, 1], 'yres': [72, 1]}, [4382, 949]], [{'w': 2499, 'h': 800, 'tag': 6, 'xres': [150, 2]}, [2709, 8463]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]]]]
labels = ["regression: centimetre unit factor", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("%s %d" % (labels[i % len(labels)], i), solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: centimetre unit factor 0[13722, 54750][1372, 5475]Failed
repair trap 1[4250, 4750][425, 475]Failed
combined fault 2[3150, 1905][3150, 1905]Passed
control 3NoneNonePassed
control 4[22168, 495][22168, 495]Passed
boundary 5[340, 1002][340, 1002]Passed
boundary 6NoneNonePassed
control 7[3023, 8920][302, 892]Failed

SHA-256 / 2b8c953d9b86155fab61b14c47ff46e0466469bbdb45b4a45a4a1e4b34c8d84b

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
    w, h = d['w'], d['h']
    tag = d.get('tag', 1)
    unit = d.get('unit', 2)
    per = {2: Fraction(254, 10), 3: Fraction(10)}.get(unit)
    if per is None:
        return None
    xn, xd = d['xres']
    yn, yd = d.get('yres', d['xres'])
    if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
        return None
    xr = Fraction(xn, xd)
    yr = Fraction(yn, yd)
    if tag in (5, 6, 7, 8):
        w, h, xr, yr = h, w, yr, xr
    def tenths(px, r):
        return math.floor(px * per * 10 / r + Fraction(1, 2))
    return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 3942, 'h': 988, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1372, 5475]], [{'w': 456, 'h': 408, 'tag': 8, 'unit': 3, 'xres': [96, 1], 'yres': [96, 1]}, [425, 475]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 3491, 'h': 649, 'xres': [40, 1], 'yres': [1000, 3]}, [22168, 495]], [{'w': 1315, 'h': 446, 'tag': 6, 'unit': 2, 'xres': [1000, 3], 'yres': [1000, 3]}, [340, 1002]], [{'w': 3975, 'h': 1299, 'unit': 2, 'xres': [300, 1], 'yres': [-300, 1]}, None], [{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]]], [[{'w': 1140, 'h': 2204, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [2296, 2850]], [{'w': 2836, 'h': 866, 'tag': 2, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [851, 2165]], [{'w': 1360, 'h': 1067, 'tag': 7, 'xres': [300, 1]}, [903, 1151]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 3380, 'h': 2534, 'tag': 6, 'xres': [300, 1]}, [2145, 2862]], [{'w': 2562, 'h': 960, 'tag': 3, 'unit': 3, 'xres': [-72, 1], 'yres': [40, 1]}, None], [{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]]], [[{'w': 2235, 'h': 1712, 'tag': 8, 'unit': 3, 'xres': [40, 1]}, [4280, 5588]], [{'w': 2114, 'h': 2984, 'tag': 7, 'unit': 3, 'xres': [7200, 100]}, [4144, 2936]], [{'w': 2503, 'h': 3897, 'tag': 6, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 2, 'h': 3716, 'unit': 2, 'xres': [300, 1]}, [2, 3146]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 3374, 'h': 1982, 'tag': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [2857, 1510]], [{'w': 3954, 'h': 1399, 'tag': 8, 'unit': 3, 'xres': [72, 1]}, [1943, 5492]]], [[{'w': 531, 'h': 2956, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [600, 1]}, [493, 1328]], [{'w': 3512, 'h': 2297, 'tag': 7, 'unit': 3, 'xres': [40, 1]}, [5743, 8780]], [{'w': 2120, 'h': 3278, 'tag': 5, 'xres': [1000, 3], 'yres': [40, 1]}, [20815, 1615]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 1975, 'h': 3847, 'tag': 8, 'unit': 2, 'xres': [72, 1], 'yres': [40, 1]}, [24428, 6967]], [{'w': 3043, 'h': 1338, 'tag': 8, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 1489, 'h': 2076, 'tag': 1, 'unit': 2, 'xres': [7200, 100], 'yres': [600, 1]}, [5253, 879]], [{'w': 3732, 'h': 1632, 'tag': 6, 'unit': 3, 'xres': [150, 2], 'yres': [72, 1]}, [2267, 4976]]], [[{'w': 3248, 'h': 3090, 'unit': 3, 'xres': [72, 1], 'yres': [300, 1]}, [4511, 1030]], [{'w': 690, 'h': 3589, 'tag': 6, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [3739, 1725]], [{'w': 3914, 'h': 1446, 'tag': 3, 'unit': 2, 'xres': [7200, 100], 'yres': [1000, 3]}, [13808, 1102]], [{'w': 2880, 'h': 3333, 'tag': 1, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 3908, 'h': 2121, 'tag': 6, 'unit': 1, 'xres': [96, 1], 'yres': [40, 1]}, None], [{'w': 690, 'h': 269, 'xres': [40, 1], 'yres': [72, 1]}, [4382, 949]], [{'w': 2499, 'h': 800, 'tag': 6, 'xres': [150, 2]}, [2709, 8463]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]]]]
labels = ["regression: centimetre unit factor", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("%s %d" % (labels[i % len(labels)], i), solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: centimetre unit factor 0[1372, 5475][1372, 5475]Passed
repair trap 1[425, 475][425, 475]Passed
combined fault 2[3150, 1905][3150, 1905]Passed
control 3NoneNonePassed
control 4[22168, 495][22168, 495]Passed
boundary 5[340, 1002][340, 1002]Passed
boundary 6NoneNonePassed
control 7[302, 892][302, 892]Passed

SHA-256 / b275cd3210e358a3a45499d50bb7c93f2403b269e665846e87337bd522b55158

Verification & scope

A deterministic bounded teaching model with a stipulated contract; it makes no claim of conformance to any published specification. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:49:42.418339+00:00.

Case digest / fd10a7ff20dba774c38612df1ec2bd0365d1707aac5d1f4dcf6004fad2c12be3