Spaces:
Sleeping
Sleeping
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| import io | |
| import base64 | |
| from typing import Tuple, Optional | |
| def load_image_from_bytes(image_bytes: bytes) -> np.ndarray: | |
| """ | |
| Load image from bytes into numpy array | |
| Args: | |
| image_bytes: Raw image bytes | |
| Returns: | |
| Image as RGB numpy array | |
| """ | |
| image = Image.open(io.BytesIO(image_bytes)) | |
| # Convert to RGB if needed | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| return np.array(image) | |
| def load_image_from_base64(base64_string: str) -> np.ndarray: | |
| """ | |
| Load image from base64 string | |
| Args: | |
| base64_string: Base64 encoded image | |
| Returns: | |
| Image as RGB numpy array | |
| """ | |
| # Remove data URL prefix if present | |
| if ',' in base64_string: | |
| base64_string = base64_string.split(',')[1] | |
| image_bytes = base64.b64decode(base64_string) | |
| return load_image_from_bytes(image_bytes) | |
| def resize_image( | |
| image: np.ndarray, | |
| target_size: int, | |
| maintain_aspect: bool = True | |
| ) -> Tuple[np.ndarray, Tuple[int, int]]: | |
| """ | |
| Resize image to target size | |
| Args: | |
| image: Input image array | |
| target_size: Target size (will be longest edge if maintain_aspect=True) | |
| maintain_aspect: Whether to maintain aspect ratio | |
| Returns: | |
| Tuple of (resized_image, original_size) | |
| """ | |
| h, w = image.shape[:2] | |
| original_size = (w, h) | |
| if maintain_aspect: | |
| # Calculate new dimensions maintaining aspect ratio | |
| if h > w: | |
| new_h = target_size | |
| new_w = int(w * (target_size / h)) | |
| else: | |
| new_w = target_size | |
| new_h = int(h * (target_size / w)) | |
| else: | |
| new_w = target_size | |
| new_h = target_size | |
| resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LINEAR) | |
| return resized, original_size | |
| def normalize_image(image: np.ndarray) -> np.ndarray: | |
| """ | |
| Normalize image for model input | |
| Args: | |
| image: Input image array (RGB) | |
| Returns: | |
| Normalized image array | |
| """ | |
| # Convert to float32 and normalize to [0, 1] | |
| image = image.astype(np.float32) / 255.0 | |
| # ImageNet normalization | |
| mean = np.array([0.485, 0.456, 0.406]) | |
| std = np.array([0.229, 0.224, 0.225]) | |
| image = (image - mean) / std | |
| return image | |
| def depth_to_colormap( | |
| depth: np.ndarray, | |
| colormap: int = cv2.COLORMAP_INFERNO | |
| ) -> np.ndarray: | |
| """ | |
| Convert depth map to colorized visualization | |
| Args: | |
| depth: Depth map array | |
| colormap: OpenCV colormap constant | |
| Returns: | |
| Colorized depth map (RGB) | |
| """ | |
| # Normalize depth to 0-255 | |
| depth_normalized = cv2.normalize(depth, None, 0, 255, cv2.NORM_MINMAX) | |
| depth_uint8 = depth_normalized.astype(np.uint8) | |
| # Apply colormap | |
| colored = cv2.applyColorMap(depth_uint8, colormap) | |
| # Convert BGR to RGB | |
| colored = cv2.cvtColor(colored, cv2.COLOR_BGR2RGB) | |
| return colored | |
| def array_to_base64(image: np.ndarray, format: str = 'PNG') -> str: | |
| """ | |
| Convert numpy array to base64 string | |
| Args: | |
| image: Image array | |
| format: Output format (PNG, JPEG, etc.) | |
| Returns: | |
| Base64 encoded image string | |
| """ | |
| pil_image = Image.fromarray(image.astype(np.uint8)) | |
| buffer = io.BytesIO() | |
| pil_image.save(buffer, format=format) | |
| buffer.seek(0) | |
| base64_string = base64.b64encode(buffer.read()).decode('utf-8') | |
| return f"data:image/{format.lower()};base64,{base64_string}" | |
| def array_to_bytes(image: np.ndarray, format: str = 'PNG') -> bytes: | |
| """ | |
| Convert numpy array to bytes | |
| Args: | |
| image: Image array | |
| format: Output format (PNG, JPEG, etc.) | |
| Returns: | |
| Image bytes | |
| """ | |
| pil_image = Image.fromarray(image.astype(np.uint8)) | |
| buffer = io.BytesIO() | |
| pil_image.save(buffer, format=format) | |
| buffer.seek(0) | |
| return buffer.read() | |
| def create_side_by_side( | |
| original: np.ndarray, | |
| depth: np.ndarray, | |
| colormap: bool = True | |
| ) -> np.ndarray: | |
| """ | |
| Create side-by-side comparison of original and depth | |
| Args: | |
| original: Original image | |
| depth: Depth map | |
| colormap: Whether to apply colormap to depth | |
| Returns: | |
| Side-by-side image | |
| """ | |
| # Ensure same height | |
| h = original.shape[0] | |
| depth_resized = cv2.resize(depth, (depth.shape[1], h)) | |
| if colormap and len(depth_resized.shape) == 2: | |
| depth_resized = depth_to_colormap(depth_resized) | |
| # Concatenate horizontally | |
| combined = np.hstack([original, depth_resized]) | |
| return combined | |