101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
"""Сервис обработки изображений."""
|
|
import io
|
|
from pathlib import Path
|
|
from typing import Optional, Tuple
|
|
|
|
from PIL import Image
|
|
|
|
|
|
class ImageProcessor:
|
|
"""Сервис для обработки изображений заданий."""
|
|
|
|
MAX_SIZE = (800, 800)
|
|
SUPPORTED_FORMATS = {"JPEG", "PNG", "WEBP"}
|
|
QUALITY = 85
|
|
|
|
@staticmethod
|
|
def resize_image(
|
|
image_data: bytes, max_size: Tuple[int, int] = MAX_SIZE, quality: int = QUALITY
|
|
) -> bytes:
|
|
"""
|
|
Изменить размер изображения.
|
|
|
|
Args:
|
|
image_data: Байты изображения
|
|
max_size: Максимальный размер (width, height)
|
|
quality: Качество JPEG (1-100)
|
|
|
|
Returns:
|
|
Байты обработанного изображения
|
|
"""
|
|
image = Image.open(io.BytesIO(image_data))
|
|
image_format = image.format or "JPEG"
|
|
|
|
# Конвертируем в RGB если нужно
|
|
if image_format == "PNG" and image.mode in ("RGBA", "LA"):
|
|
background = Image.new("RGB", image.size, (255, 255, 255))
|
|
if image.mode == "RGBA":
|
|
background.paste(image, mask=image.split()[3])
|
|
else:
|
|
background.paste(image)
|
|
image = background
|
|
elif image.mode != "RGB":
|
|
image = image.convert("RGB")
|
|
|
|
# Изменяем размер с сохранением пропорций
|
|
image.thumbnail(max_size, Image.Resampling.LANCZOS)
|
|
|
|
# Сохраняем в байты
|
|
output = io.BytesIO()
|
|
image.save(output, format="JPEG", quality=quality, optimize=True)
|
|
return output.getvalue()
|
|
|
|
@staticmethod
|
|
def validate_image(image_data: bytes) -> Tuple[bool, Optional[str]]:
|
|
"""
|
|
Валидировать изображение.
|
|
|
|
Args:
|
|
image_data: Байты изображения
|
|
|
|
Returns:
|
|
(is_valid, error_message)
|
|
"""
|
|
try:
|
|
image = Image.open(io.BytesIO(image_data))
|
|
image_format = image.format
|
|
|
|
if image_format not in ImageProcessor.SUPPORTED_FORMATS:
|
|
return False, f"Неподдерживаемый формат: {image_format}"
|
|
|
|
# Проверяем размер
|
|
width, height = image.size
|
|
if width > 2000 or height > 2000:
|
|
return False, "Изображение слишком большое (максимум 2000x2000)"
|
|
|
|
# Проверяем файл на валидность
|
|
image.verify()
|
|
return True, None
|
|
|
|
except Exception as e:
|
|
return False, f"Ошибка валидации: {str(e)}"
|
|
|
|
@staticmethod
|
|
def get_image_info(image_data: bytes) -> dict:
|
|
"""
|
|
Получить информацию об изображении.
|
|
|
|
Args:
|
|
image_data: Байты изображения
|
|
|
|
Returns:
|
|
Словарь с информацией (format, size, mode)
|
|
"""
|
|
image = Image.open(io.BytesIO(image_data))
|
|
return {
|
|
"format": image.format,
|
|
"size": image.size,
|
|
"mode": image.mode,
|
|
}
|
|
|