refactor: solidify startup UX and engine-aware preprocessing
- Slim core deps: move ML stack to optional extras (paddle/tables/figures/scientific/full) - Lazy settings proxy with config search paths (env var, cwd, user dir) - New commands: init, demo, setup [basic|full], first-run guard on run/watch - Engine-aware preprocessing: Paddle gets original image (fixes dark mode 0.83->0.95) - Results table shows Skipped count; lazy run-dir creation - kg_ocr marked experimental with extra, Docker defaults with OCR_PIPELINE_CONFIG - 25/25 tests, ruff clean
This commit is contained in:
@@ -27,7 +27,9 @@ class ImagePreprocessor:
|
||||
if max(height, width) <= self.max_dim:
|
||||
return image
|
||||
scale = self.max_dim / max(height, width)
|
||||
return cv2.resize(image, (int(width * scale), int(height * scale)), interpolation=cv2.INTER_AREA)
|
||||
return cv2.resize(
|
||||
image, (int(width * scale), int(height * scale)), interpolation=cv2.INTER_AREA
|
||||
)
|
||||
|
||||
def deskew(self, image: np.ndarray) -> np.ndarray:
|
||||
if not self.config.deskew:
|
||||
@@ -43,10 +45,16 @@ class ImagePreprocessor:
|
||||
return image
|
||||
height, width = image.shape[:2]
|
||||
matrix = cv2.getRotationMatrix2D((width // 2, height // 2), angle, 1.0)
|
||||
return cv2.warpAffine(image, matrix, (width, height), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
|
||||
return cv2.warpAffine(
|
||||
image, matrix, (width, height), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE
|
||||
)
|
||||
|
||||
def denoise(self, image: np.ndarray) -> np.ndarray:
|
||||
return cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 21) if self.config.denoise else image
|
||||
return (
|
||||
cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 21)
|
||||
if self.config.denoise
|
||||
else image
|
||||
)
|
||||
|
||||
def apply_clahe(self, image: np.ndarray) -> np.ndarray:
|
||||
if not self.config.clahe:
|
||||
@@ -61,8 +69,12 @@ class ImagePreprocessor:
|
||||
return image
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
|
||||
horizontal = cv2.morphologyEx(binary, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (40, 1)))
|
||||
vertical = cv2.morphologyEx(binary, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (1, 40)))
|
||||
horizontal = cv2.morphologyEx(
|
||||
binary, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (40, 1))
|
||||
)
|
||||
vertical = cv2.morphologyEx(
|
||||
binary, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (1, 40))
|
||||
)
|
||||
mask = cv2.bitwise_or(horizontal, vertical)
|
||||
result = image.copy()
|
||||
result[mask > 0] = (255, 255, 255)
|
||||
@@ -72,7 +84,9 @@ class ImagePreprocessor:
|
||||
if not self.config.adaptive_threshold:
|
||||
return image
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||
binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
|
||||
binary = cv2.adaptiveThreshold(
|
||||
gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
|
||||
)
|
||||
return cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
|
||||
|
||||
def preprocess_image(self, image: np.ndarray) -> np.ndarray:
|
||||
|
||||
Reference in New Issue
Block a user