fix: always use min-channel and add grayscale-only OCR path (refs #113)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 35s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 50s
Deploy to Staging / Verify Staging (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 35s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 50s
Deploy to Staging / Verify Staging (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
Two fixes: 1. Always use min-channel for color images instead of gated comparison that was falling back to standard grayscale (which has only 23% contrast for white-on-green VIN stickers). 2. Add grayscale-only OCR path (CLAHE + denoise, no thresholding) between adaptive and Otsu attempts. Tesseract's LSTM engine is designed to handle grayscale input directly and often outperforms binarized input where thresholding creates artifacts. Pipeline order: adaptive threshold → grayscale-only → Otsu threshold Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -146,6 +146,34 @@ class VinExtractor(BaseExtractor):
|
||||
# No VIN candidates found - try with different PSM modes
|
||||
candidates = self._try_alternate_ocr(preprocessed_bytes)
|
||||
|
||||
if not candidates:
|
||||
# Try grayscale-only (no thresholding) — the Tesseract
|
||||
# LSTM engine often performs better on non-binarized input
|
||||
# because it does its own internal preprocessing.
|
||||
gray_result = vin_preprocessor.preprocess(
|
||||
image_bytes, apply_threshold=False
|
||||
)
|
||||
logger.debug(
|
||||
"Grayscale preprocessing steps: %s",
|
||||
gray_result.preprocessing_applied,
|
||||
)
|
||||
if debug_session:
|
||||
self._save_debug_image(
|
||||
debug_session, "04_preprocessed_gray.png",
|
||||
gray_result.image_bytes,
|
||||
)
|
||||
|
||||
raw_text, word_confidences = self._perform_ocr(
|
||||
gray_result.image_bytes
|
||||
)
|
||||
logger.debug("Gray PSM 6 raw text: '%s'", raw_text)
|
||||
candidates = vin_validator.extract_candidates(raw_text)
|
||||
logger.debug("Gray PSM 6 candidates: %s", candidates)
|
||||
if not candidates:
|
||||
candidates = self._try_alternate_ocr(
|
||||
gray_result.image_bytes, prefix="Gray"
|
||||
)
|
||||
|
||||
if not candidates:
|
||||
# Try alternative preprocessing (Otsu's thresholding)
|
||||
otsu_result = vin_preprocessor.preprocess_otsu(image_bytes)
|
||||
|
||||
Reference in New Issue
Block a user