Files
imagen4/src/utils/image_utils.py

108 lines
3.6 KiB
Python

"""
Image processing utilities for preview generation
"""
import base64
import io
import logging
from typing import Optional
from PIL import Image
logger = logging.getLogger(__name__)
def create_preview_image_b64(image_data: bytes, target_size: int = 512, quality: int = 85) -> Optional[str]:
"""
Convert PNG image data to JPEG preview with specified size and return as base64
Args:
image_data: Original PNG image data in bytes
target_size: Target size for the preview (default: 512x512)
quality: JPEG quality (1-100, default: 85)
Returns:
Base64 encoded JPEG image string, or None if conversion fails
"""
try:
# Open image from bytes
with Image.open(io.BytesIO(image_data)) as img:
# Convert to RGB if necessary (PNG might have alpha channel)
if img.mode in ('RGBA', 'LA', 'P'):
# Create white background for transparent images
background = Image.new('RGB', img.size, (255, 255, 255))
if img.mode == 'P':
img = img.convert('RGBA')
background.paste(img, mask=img.split()[-1] if img.mode in ('RGBA', 'LA') else None)
img = background
elif img.mode != 'RGB':
img = img.convert('RGB')
# Resize to target size maintaining aspect ratio
img.thumbnail((target_size, target_size), Image.Resampling.LANCZOS)
# If image is smaller than target size, pad it to exact size
if img.size != (target_size, target_size):
# Create new image with target size and white background
new_img = Image.new('RGB', (target_size, target_size), (255, 255, 255))
# Center the resized image
x = (target_size - img.size[0]) // 2
y = (target_size - img.size[1]) // 2
new_img.paste(img, (x, y))
img = new_img
# Convert to JPEG and encode as base64
output_buffer = io.BytesIO()
img.save(output_buffer, format='JPEG', quality=quality, optimize=True)
jpeg_data = output_buffer.getvalue()
# Encode to base64
base64_data = base64.b64encode(jpeg_data).decode('utf-8')
logger.info(f"Preview image created: {target_size}x{target_size} JPEG, {len(jpeg_data)} bytes, base64 length: {len(base64_data)}")
return base64_data
except Exception as e:
logger.error(f"Failed to create preview image: {str(e)}")
return None
def validate_image_data(image_data: bytes) -> bool:
"""
Validate if image data is a valid image
Args:
image_data: Image data in bytes
Returns:
True if valid image, False otherwise
"""
try:
with Image.open(io.BytesIO(image_data)) as img:
img.verify() # Verify image integrity
return True
except Exception:
return False
def get_image_info(image_data: bytes) -> Optional[dict]:
"""
Get image information
Args:
image_data: Image data in bytes
Returns:
Dictionary with image info (format, size, mode) or None if invalid
"""
try:
with Image.open(io.BytesIO(image_data)) as img:
return {
'format': img.format,
'size': img.size,
'mode': img.mode,
'bytes': len(image_data)
}
except Exception as e:
logger.error(f"Failed to get image info: {str(e)}")
return None