feat: two-pass deskew with wider angle range and residual correction
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 24s
CI / test-go-edu-search (push) Successful in 25s
CI / test-python-klausur (push) Failing after 1m52s
CI / test-python-agent-core (push) Successful in 15s
CI / test-nodejs-website (push) Successful in 16s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 24s
CI / test-go-edu-search (push) Successful in 25s
CI / test-python-klausur (push) Failing after 1m52s
CI / test-python-agent-core (push) Successful in 15s
CI / test-nodejs-website (push) Successful in 16s
- Increase iterative deskew coarse_range from ±2° to ±5° to handle heavily skewed scans - New deskew_two_pass(): runs iterative projection first, then word-alignment on the corrected image to detect/fix residual skew (applied when residual ≥ 0.3°) - OCR pipeline API auto_deskew now uses deskew_two_pass by default - Vocab worksheet _run_ocr_pipeline_for_page uses deskew_two_pass - Deskew result now includes angle_residual and two_pass_debug Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -413,7 +413,7 @@ def _projection_gradient_score(profile: np.ndarray) -> float:
|
||||
|
||||
def deskew_image_iterative(
|
||||
img: np.ndarray,
|
||||
coarse_range: float = 2.0,
|
||||
coarse_range: float = 5.0,
|
||||
coarse_step: float = 0.1,
|
||||
fine_range: float = 0.15,
|
||||
fine_step: float = 0.02,
|
||||
@@ -528,6 +528,66 @@ def deskew_image_iterative(
|
||||
return rotated, final_angle, debug
|
||||
|
||||
|
||||
def deskew_two_pass(
|
||||
img: np.ndarray,
|
||||
coarse_range: float = 5.0,
|
||||
) -> Tuple[np.ndarray, float, Dict[str, Any]]:
|
||||
"""Two-pass deskew: iterative projection + word-alignment residual check.
|
||||
|
||||
Pass 1: ``deskew_image_iterative()`` (vertical-edge projection, wide range).
|
||||
Pass 2: ``deskew_image_by_word_alignment()`` on the already-corrected image
|
||||
to detect and fix residual skew that the projection method missed.
|
||||
|
||||
The two corrections are summed. If the residual from Pass 2 is below
|
||||
0.3° it is ignored (already good enough).
|
||||
|
||||
Returns:
|
||||
(corrected_bgr, total_angle_degrees, debug_dict)
|
||||
"""
|
||||
debug: Dict[str, Any] = {}
|
||||
|
||||
# --- Pass 1: iterative projection ---
|
||||
corrected, angle1, dbg1 = deskew_image_iterative(
|
||||
img.copy(), coarse_range=coarse_range,
|
||||
)
|
||||
debug["pass1_angle"] = round(angle1, 3)
|
||||
debug["pass1_method"] = "iterative"
|
||||
debug["pass1_debug"] = dbg1
|
||||
|
||||
# --- Pass 2: word-alignment residual check on corrected image ---
|
||||
angle2 = 0.0
|
||||
try:
|
||||
# Encode the corrected image to PNG bytes for word-alignment
|
||||
ok, buf = cv2.imencode(".png", corrected)
|
||||
if ok:
|
||||
corrected_bytes, angle2 = deskew_image_by_word_alignment(buf.tobytes())
|
||||
if abs(angle2) >= 0.3:
|
||||
# Significant residual — decode and use the second correction
|
||||
arr2 = np.frombuffer(corrected_bytes, dtype=np.uint8)
|
||||
corrected2 = cv2.imdecode(arr2, cv2.IMREAD_COLOR)
|
||||
if corrected2 is not None:
|
||||
corrected = corrected2
|
||||
logger.info(f"deskew_two_pass: pass2 residual={angle2:.2f}° applied "
|
||||
f"(total={angle1 + angle2:.2f}°)")
|
||||
else:
|
||||
angle2 = 0.0
|
||||
else:
|
||||
logger.info(f"deskew_two_pass: pass2 residual={angle2:.2f}° < 0.3° — skipped")
|
||||
angle2 = 0.0
|
||||
except Exception as e:
|
||||
logger.warning(f"deskew_two_pass: pass2 word-alignment failed: {e}")
|
||||
angle2 = 0.0
|
||||
|
||||
total_angle = angle1 + angle2
|
||||
debug["pass2_angle"] = round(angle2, 3)
|
||||
debug["pass2_method"] = "word_alignment"
|
||||
debug["total_angle"] = round(total_angle, 3)
|
||||
|
||||
logger.info(f"deskew_two_pass: pass1={angle1:.2f}° + pass2={angle2:.2f}° = {total_angle:.2f}°")
|
||||
|
||||
return corrected, total_angle, debug
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Stage 3: Dewarp (Book Curvature Correction)
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user