dino-analyzer / utils.py
trashchenkov's picture
Upload 7 files
b0487df verified
raw
history blame
5.73 kB
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"