| |
|
|
| import numpy as np |
| import cv2 |
| from PIL import Image |
| from .coordinate_system import CoordinateTransformer |
| from .notifications import notify_success, notify_error, NotificationMessages |
|
|
| def process_uploaded_image(image, target_size=(640, 640)): |
| """ |
| アップロードされた画像を処理 |
| |
| Args: |
| image: PIL Image or numpy array |
| target_size: 表示用のターゲットサイズ |
| |
| Returns: |
| tuple: (processed_image, original_size, scale_info) |
| """ |
| try: |
| if image is None: |
| return None, None, None |
| |
| |
| if isinstance(image, Image.Image): |
| original_image = np.array(image) |
| else: |
| original_image = image |
| |
| original_size = (original_image.shape[1], original_image.shape[0]) |
| |
| |
| processed_image, scale_info = resize_with_aspect_ratio( |
| original_image, target_size |
| ) |
| |
| notify_success(NotificationMessages.IMAGE_UPLOADED) |
| |
| return processed_image, original_size, scale_info |
| |
| except Exception as e: |
| notify_error(f"画像処理中にエラーが発生しました: {str(e)}") |
| return None, None, None |
|
|
| def resize_with_aspect_ratio(image, target_size): |
| """ |
| アスペクト比を保持してリサイズ |
| |
| Args: |
| image: numpy array |
| target_size: (width, height) |
| |
| Returns: |
| tuple: (resized_image, scale_info) |
| """ |
| h, w = image.shape[:2] |
| target_w, target_h = target_size |
| |
| |
| scale = min(target_w / w, target_h / h) |
| new_w = int(w * scale) |
| new_h = int(h * scale) |
| |
| |
| resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA) |
| |
| |
| if new_w != target_w or new_h != target_h: |
| |
| pad_x = (target_w - new_w) // 2 |
| pad_y = (target_h - new_h) // 2 |
| |
| if len(image.shape) == 3: |
| padded = np.full((target_h, target_w, image.shape[2]), 128, dtype=image.dtype) |
| else: |
| padded = np.full((target_h, target_w), 128, dtype=image.dtype) |
| |
| padded[pad_y:pad_y+new_h, pad_x:pad_x+new_w] = resized |
| resized = padded |
| |
| scale_info = { |
| 'scale': scale, |
| 'original_size': (w, h), |
| 'resized_size': (new_w, new_h), |
| 'final_size': target_size, |
| 'padding': { |
| 'x': pad_x if 'pad_x' in locals() else 0, |
| 'y': pad_y if 'pad_y' in locals() else 0 |
| } |
| } |
| |
| return resized, scale_info |
|
|
| def create_background_canvas(image, canvas_size=(640, 640)): |
| """ |
| 背景画像用のCanvasを作成 |
| |
| Args: |
| image: 背景画像 |
| canvas_size: Canvasサイズ |
| |
| Returns: |
| numpy array: Canvas用背景画像 |
| """ |
| try: |
| if image is None: |
| |
| background = np.full((*canvas_size[::-1], 3), 240, dtype=np.uint8) |
| return background |
| |
| |
| processed_image, _ = resize_with_aspect_ratio(image, canvas_size) |
| |
| return processed_image |
| |
| except Exception as e: |
| print(f"Background canvas creation error: {e}") |
| |
| background = np.full((*canvas_size[::-1], 3), 240, dtype=np.uint8) |
| return background |
|
|
| def image_to_base64(image): |
| """ |
| 画像をbase64文字列に変換(Canvas表示用) |
| |
| Args: |
| image: numpy array or PIL Image |
| |
| Returns: |
| str: base64エンコードされた画像データ |
| """ |
| try: |
| import base64 |
| import io |
| |
| if isinstance(image, np.ndarray): |
| |
| if image.dtype != np.uint8: |
| image = (image * 255).astype(np.uint8) |
| pil_image = Image.fromarray(image) |
| else: |
| pil_image = image |
| |
| |
| buffer = io.BytesIO() |
| pil_image.save(buffer, format='PNG') |
| img_str = base64.b64encode(buffer.getvalue()).decode() |
| |
| return f"data:image/png;base64,{img_str}" |
| |
| except Exception as e: |
| print(f"Image to base64 conversion error: {e}") |
| return None |