feat(phase1): implement OpenCV image processing pipeline

- Create ImageProcessor service with EXIF orientation detection
- Implement smart cropping via OpenCV contour detection (10% padding)
- Add text orientation detection using Hough line transform
- Resize and compress images to 1200px with 85% JPEG quality
- Generate 200px square thumbnails with center crop
- Fallback to Pillow if OpenCV fails
- Comprehensive test suite: 28 tests all passing
- File size validation (reject >10MB)
- Graceful error handling for corrupted/invalid images
- Update requirements.txt with opencv-python, piexif, python-magic
This commit is contained in:
2026-04-20 22:17:11 +03:00
parent 01321bf607
commit 3aafacab12
1068 changed files with 720366 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
__all__: list[str] = []
import cv2
import cv2.typing
import typing as _typing
# Classes
class IntelligentScissorsMB:
# Functions
def __init__(self) -> None: ...
def setWeights(self, weight_non_edge: float, weight_gradient_direction: float, weight_gradient_magnitude: float) -> IntelligentScissorsMB: ...
def setGradientMagnitudeMaxLimit(self, gradient_magnitude_threshold_max: float = ...) -> IntelligentScissorsMB: ...
def setEdgeFeatureZeroCrossingParameters(self, gradient_magnitude_min_value: float = ...) -> IntelligentScissorsMB: ...
def setEdgeFeatureCannyParameters(self, threshold1: float, threshold2: float, apertureSize: int = ..., L2gradient: bool = ...) -> IntelligentScissorsMB: ...
@_typing.overload
def applyImage(self, image: cv2.typing.MatLike) -> IntelligentScissorsMB: ...
@_typing.overload
def applyImage(self, image: cv2.UMat) -> IntelligentScissorsMB: ...
@_typing.overload
def applyImageFeatures(self, non_edge: cv2.typing.MatLike, gradient_direction: cv2.typing.MatLike, gradient_magnitude: cv2.typing.MatLike, image: cv2.typing.MatLike | None = ...) -> IntelligentScissorsMB: ...
@_typing.overload
def applyImageFeatures(self, non_edge: cv2.UMat, gradient_direction: cv2.UMat, gradient_magnitude: cv2.UMat, image: cv2.UMat | None = ...) -> IntelligentScissorsMB: ...
def buildMap(self, sourcePt: cv2.typing.Point) -> None: ...
@_typing.overload
def getContour(self, targetPt: cv2.typing.Point, contour: cv2.typing.MatLike | None = ..., backward: bool = ...) -> cv2.typing.MatLike: ...
@_typing.overload
def getContour(self, targetPt: cv2.typing.Point, contour: cv2.UMat | None = ..., backward: bool = ...) -> cv2.UMat: ...