Use native per-axis scanner resolution, resampling y to reach beyond it - #1010
Open
dtrtuser wants to merge 1 commit into
Open
Use native per-axis scanner resolution, resampling y to reach beyond it#1010dtrtuser wants to merge 1 commit into
dtrtuser wants to merge 1 commit into
Conversation
`resolution` alone is sometimes an artificially narrow convenience subset of what a scanner can actually do — confirmed on this project's reference Epson V500 (epkowa/interpreter backend): `resolution` tops out at 1600dpi, while the device's real native per-axis capability (`x_resolution`/`y_resolution`) reaches 6400 on x and, depending on source, up to 9600 on y. The two axes aren't just different, they're asymmetric in a way that changes with `source`: under this device's Transparency Unit, y's native ladder drops 3200 and 6400 entirely (replaced by a 9600 that appears nowhere under Flatbed), while x keeps its full native ladder either way. A plain "use whichever values both axes have in common" approach caps out at 1600 for exactly this reason. _resolve_resampled_resolutions fixes this by treating x (the sensor-pitch axis, native at every target worth offering on the reference device) as authoritative, and for each of its native values finds the largest native y value at or below it. Any gap between the two is resampled up in software after the read (_resample_rows_to_dpi, bilinear via cv2.resize) — always an upsample, never a downsample, so this only ever recovers real captured detail, never invents it. This is a strict superset of the previous exact-intersection behavior (_resolve_square_resolutions, kept as its own function since "no resampling needed" is occasionally the more precise thing to ask for) and supersedes it in _detect_dpi and at scan time. IR capture is explicitly rejected together with a DPI that needs resampling: _align_ir_to_rgb documents that even interpolating the IR channel risks softening a thin dust defect's minimum below the detection pipeline's noise floor (a downsample case measured there, 0.22 -> 0.31, "shattered"). Untested for the upsample case this resample is, and not worth guessing at on a precision-sensitive measurement — fails loudly with the native-only alternatives instead of silently degrading dust detection. Verified end-to-end on an Epson Perfection V500 (epkowa/interpreter, Transparency Unit) on Fedora 44: a 3200dpi scan of a 6x4.5cm medium format negative — unreachable at all under the previous exact-match logic, which capped at 1600 on this device — now scans y natively at 2400 and resamples up, producing visibly more real detail than 1600dpi did, with correct proportions (no stretching on either axis). Adds regression coverage: pure-function tests for _resolve_resampled_resolutions and _resample_rows_to_dpi (test_capabilities.py), and scan-level tests in test_transparency_source_switch.py covering the axis-pair write, the actual row-count upsample on the returned array, the fail-loud path for an unreachable DPI, and the IR-capture rejection. Each new behavior was confirmed to actually fail against the prior (exact-match or no-resample) code before being confirmed against the fix, not just checked for shape.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
resolutionalone is sometimes an artificially narrow convenience subset of what a scanner can actually do — confirmed on this project's reference Epson V500 (epkowa/interpreter backend):resolutiontops out at 1600dpi, while the device's real native per-axis capability (x_resolution/y_resolution) reaches 6400 on x and, depending on source, up to 9600 on y.The two axes aren't just different, they're asymmetric in a way that changes with
source: under this device's Transparency Unit, y's native ladder drops 3200 and 6400 entirely (replaced by a 9600 that appears nowhere under Flatbed), while x keeps its full native ladder either way. A plain "use whichever values both axes have in common" approach caps out at 1600 for exactly this reason._resolve_resampled_resolutions fixes this by treating x (the sensor-pitch axis, native at every target worth offering on the reference device) as authoritative, and for each of its native values finds the largest native y value at or below it. Any gap between the two is resampled up in software after the read (_resample_rows_to_dpi, bilinear via cv2.resize) — always an upsample, never a downsample, so this only ever recovers real captured detail, never invents it. This is a strict superset of the previous exact-intersection behavior (_resolve_square_resolutions, kept as its own function since "no resampling needed" is occasionally the more precise thing to ask for) and supersedes it in _detect_dpi and at scan time.
IR capture is explicitly rejected together with a DPI that needs resampling: _align_ir_to_rgb documents that even interpolating the IR channel risks softening a thin dust defect's minimum below the detection pipeline's noise floor (a downsample case measured there, 0.22 -> 0.31, "shattered"). Untested for the upsample case this resample is, and not worth guessing at on a precision-sensitive measurement — fails loudly with the native-only alternatives instead of silently degrading dust detection.
Verified end-to-end on an Epson Perfection V500 (epkowa/interpreter, Transparency Unit) on Fedora 44: a 3200dpi scan of a 6x4.5cm medium format negative — unreachable at all under the previous exact-match logic, which capped at 1600 on this device — now scans y natively at 2400 and resamples up, producing visibly more real detail than 1600dpi did, with correct proportions (no stretching on either axis).
Adds regression coverage: pure-function tests for
_resolve_resampled_resolutions and _resample_rows_to_dpi (test_capabilities.py), and scan-level tests in
test_transparency_source_switch.py covering the axis-pair write, the actual row-count upsample on the returned array, the fail-loud path for an unreachable DPI, and the IR-capture rejection. Each new behavior was confirmed to actually fail against the prior (exact-match or no-resample) code before being confirmed against the fix, not just checked for shape.