Gemini analyzes raw image and returns crop_bounds for that coordinate space.
Previous code applied EXIF rotation first, changing image dimensions, then
used crop_bounds on the rotated image (coordinate mismatch).
Now: crop on raw image (matches AI) → then apply EXIF + manual rotation.
This ensures cropped region contains the actual item, not background.
- Move manual rotation before cropping and text detection
- Detect text orientation on full rotated image (not just cropped region)
- This allows text angle detection to see full context and properly orient labels
- Crop happens after orientation correction for cleaner results
- Add rotation_degrees parameter to ImageProcessor.process_photo()
- Pass rotation through _auto_save_photo_from_extraction() to processor
- Allow no-crop fallback when crop_bounds is None
- Add buildPhotoUrl() helper to resolve backend URLs correctly
- Update frontend components to use backend URL for image sources
- Replace Use/Skip Photo buttons with checkbox in AI extraction UI
- Add images/ to .gitignore to prevent accidental commits
Addresses: rotation never applied, image 404s (relative to Next.js not backend), preview blank in edit form
- 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
The N+1 optimization in save_image() pre-lowercases the existing_files list
before passing to get_unique_filename(). However, this broke the API contract:
the function should handle any-case input to remain robust.
Changed: get_unique_filename() now defensively lowercases the input list,
ensuring collision detection works regardless of input case.
Benefits:
- Fixes implicit API contract change (function expected any-case input)
- Maintains N+1 optimization (pre-lowercasing still works)
- Supports both optimization and edge cases (direct function calls)
- All 22 tests pass