Spaces:
Sleeping
Sleeping
import os | |
from typing import Tuple, Optional | |
from PIL import Image, ImageOps | |
import io | |
def validate_image_file(file_path: str) -> bool: | |
""" | |
Проверяет, является ли файл корректным изображением. | |
Args: | |
file_path: Путь к файлу изображения | |
Returns: | |
True если файл является корректным изображением | |
""" | |
try: | |
with Image.open(file_path) as img: | |
img.verify() | |
return True | |
except Exception: | |
return False | |
def get_image_info(file_path: str) -> Optional[dict]: | |
""" | |
Получает информацию об изображении. | |
Args: | |
file_path: Путь к файлу изображения | |
Returns: | |
Словарь с информацией об изображении или None при ошибке | |
""" | |
try: | |
with Image.open(file_path) as img: | |
return { | |
"width": img.width, | |
"height": img.height, | |
"format": img.format, | |
"mode": img.mode, | |
"size_bytes": os.path.getsize(file_path) | |
} | |
except Exception: | |
return None | |
def resize_image_if_needed(image: Image.Image, max_size: Tuple[int, int] = (1024, 1024)) -> Image.Image: | |
""" | |
Изменяет размер изображения, если оно слишком большое. | |
Args: | |
image: PIL изображение | |
max_size: Максимальный размер (ширина, высота) | |
Returns: | |
Изображение с измененным размером (если необходимо) | |
""" | |
if image.width > max_size[0] or image.height > max_size[1]: | |
# Сохраняем пропорции | |
image.thumbnail(max_size, Image.Resampling.LANCZOS) | |
return image | |
def optimize_image_for_api(image: Image.Image, quality: int = 85) -> Image.Image: | |
""" | |
Оптимизирует изображение для отправки в API. | |
Args: | |
image: PIL изображение | |
quality: Качество сжатия JPEG (1-100) | |
Returns: | |
Оптимизированное изображение | |
""" | |
# Изменяем размер если нужно | |
optimized = resize_image_if_needed(image) | |
# Автоматически поворачиваем на основе EXIF данных | |
optimized = ImageOps.exif_transpose(optimized) | |
# Конвертируем в RGB если изображение в RGBA или другом формате | |
if optimized.mode in ('RGBA', 'LA', 'P'): | |
# Создаем белый фон для прозрачных изображений | |
background = Image.new('RGB', optimized.size, (255, 255, 255)) | |
if optimized.mode == 'P': | |
optimized = optimized.convert('RGBA') | |
background.paste(optimized, mask=optimized.split()[-1] if optimized.mode == 'RGBA' else None) | |
optimized = background | |
elif optimized.mode != 'RGB': | |
optimized = optimized.convert('RGB') | |
return optimized | |
def save_temp_image(image: Image.Image, prefix: str = "temp_dino") -> str: | |
""" | |
Сохраняет временное изображение и возвращает путь к нему. | |
Args: | |
image: PIL изображение | |
prefix: Префикс для имени файла | |
Returns: | |
Путь к временному файлу | |
""" | |
import tempfile | |
import uuid | |
# Создаем уникальное имя файла | |
temp_name = f"{prefix}_{uuid.uuid4().hex[:8]}.jpg" | |
temp_path = os.path.join(tempfile.gettempdir(), temp_name) | |
# Оптимизируем и сохраняем | |
optimized_image = optimize_image_for_api(image) | |
optimized_image.save(temp_path, "JPEG", quality=85, optimize=True) | |
return temp_path | |
def cleanup_temp_file(file_path: str) -> bool: | |
""" | |
Удаляет временный файл. | |
Args: | |
file_path: Путь к файлу для удаления | |
Returns: | |
True если файл успешно удален | |
""" | |
try: | |
if os.path.exists(file_path): | |
os.remove(file_path) | |
return True | |
return False | |
except Exception: | |
return False | |
def convert_bytes_to_image(image_bytes: bytes) -> Optional[Image.Image]: | |
""" | |
Конвертирует байты в PIL изображение. | |
Args: | |
image_bytes: Байты изображения | |
Returns: | |
PIL изображение или None при ошибке | |
""" | |
try: | |
return Image.open(io.BytesIO(image_bytes)) | |
except Exception: | |
return None | |
def get_supported_formats() -> list: | |
""" | |
Возвращает список поддерживаемых форматов изображений. | |
Returns: | |
Список расширений файлов | |
""" | |
return ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp'] | |
def format_file_size(size_bytes: int) -> str: | |
""" | |
Форматирует размер файла в читаемый вид. | |
Args: | |
size_bytes: Размер в байтах | |
Returns: | |
Отформатированная строка размера | |
""" | |
for unit in ['B', 'KB', 'MB', 'GB']: | |
if size_bytes < 1024.0: | |
return f"{size_bytes:.1f} {unit}" | |
size_bytes /= 1024.0 | |
return f"{size_bytes:.1f} TB" |