|
|
|
|
|
|
|
|
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 |