dino-analyzer / dino_analyzer.py
trashchenkov's picture
Upload 7 files
b0487df verified
import os
import json
from typing import Optional
from PIL import Image
import google.generativeai as genai
# Пытаемся импортировать dotenv, если доступен (для локальной разработки)
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
# На HF Spaces dotenv может быть недоступен, это нормально
pass
from models import DinosaurInfo
from utils import optimize_image_for_api, save_temp_image, cleanup_temp_file, validate_image_file
class DinosaurAnalyzer:
"""Класс для анализа изображений динозавров с помощью Gemini API."""
def __init__(self, api_key: Optional[str] = None):
"""
Инициализация анализатора.
Args:
api_key: API ключ для Gemini. Если не указан, будет взят из переменной окружения.
"""
if api_key is None:
api_key = os.getenv('GEMINI_API_KEY')
if not api_key:
raise ValueError(
"API ключ не найден. Укажите его в параметре api_key или "
"установите переменную окружения GEMINI_API_KEY"
)
genai.configure(api_key=api_key)
# Инициализация модели с системной инструкцией
self.model = genai.GenerativeModel(
model_name='gemini-1.5-flash-latest',
generation_config={
"response_mime_type": "application/json",
"response_schema": DinosaurInfo
},
system_instruction="""
ВАЖНО: Отвечай ТОЛЬКО на РУССКОМ языке! Весь твой ответ должен быть на русском языке.
Ты — эксперт-палеонтолог и ИИ для анализа изображений пластиковых фигурок динозавров.
Твоя задача — идентифицировать вид динозавра по фотографии игрушечной фигурки.
ИНСТРУКЦИИ ПО АНАЛИЗУ:
1. 🔍 ОПРЕДЕЛИ ВИД: Внимательно изучи форму тела, голову, конечности, хвост, характерные особенности для определения точного вида динозавра. Назови вид на РУССКОМ языке.
2. 🎨 ОПИШИ ЦВЕТА: Опиши основные цвета именно этой пластиковой фигурки (как они выглядят на фото). НЕ описывай реальные цвета динозавра, а только то, что видишь на игрушке.
3. ⏰ УКАЖИ ПЕРИОД: Определи геологический период, в котором жил этот вид динозавра. Ответ дай на РУССКОМ языке (например, "Юрский период", "Поздний меловой период").
4. 📚 РАССКАЖИ ФАКТ: Поделись интересным фактом об этом виде динозавра. Факт должен быть познавательным и написан на РУССКОМ языке.
ВАЖНЫЕ ТРЕБОВАНИЯ:
- ВСЕ поля заполняй только на РУССКОМ языке
- Если не можешь точно определить вид, напиши "Неопределенный вид" или опиши как "Динозавр семейства..."
- Для цветов используй простые русские названия (зеленый, коричневый, желтый и т.д.)
- Геологические периоды называй по-русски
- Факты должны быть интересными и понятными
Верни всю информацию в указанной JSON-схеме НА РУССКОМ ЯЗЫКЕ.
"""
)
def analyze_image(self, image_path: str) -> Optional[DinosaurInfo]:
"""
Анализирует изображение динозавра и возвращает структурированную информацию.
Args:
image_path: Путь к файлу изображения
Returns:
DinosaurInfo объект с информацией о динозавре или None при ошибке
"""
try:
# Проверяем существование и валидность файла
if not os.path.exists(image_path):
print(f"Ошибка: файл {image_path} не найден")
return None
if not validate_image_file(image_path):
print(f"Ошибка: файл {image_path} не является корректным изображением")
return None
# Загружаем и оптимизируем изображение
img = Image.open(image_path)
optimized_img = optimize_image_for_api(img)
# Отправляем запрос к Gemini API
response = self.model.generate_content([optimized_img])
# Парсим JSON ответ в объект DinosaurInfo
dino_data = DinosaurInfo.model_validate_json(response.text)
return dino_data
except json.JSONDecodeError as e:
print(f"Ошибка парсинга JSON: {e}")
print(f"Ответ модели: {response.text}")
return None
except Exception as e:
print(f"Произошла ошибка при анализе изображения: {e}")
if 'response' in locals() and hasattr(response, 'prompt_feedback'):
print(f"Обратная связь: {response.prompt_feedback}")
return None
def analyze_image_from_pil(self, image: Image.Image) -> Optional[DinosaurInfo]:
"""
Анализирует изображение динозавра из PIL.Image объекта.
Args:
image: PIL.Image объект
Returns:
DinosaurInfo объект с информацией о динозавре или None при ошибке
"""
try:
print(f"📸 Анализируем изображение размера {image.size}...")
# Оптимизируем изображение для API
optimized_image = optimize_image_for_api(image)
print(f"✅ Изображение оптимизировано до размера {optimized_image.size}")
# Отправляем запрос к Gemini API
response = self.model.generate_content([
"Проанализируй эту фигурку динозавра согласно инструкциям:",
optimized_image
])
# Парсим JSON ответ
result_text = response.text.strip()
print(f"📝 Получен ответ от API: {result_text[:100]}...")
# Парсим ответ как JSON и создаем объект DinosaurInfo
result_data = json.loads(result_text)
dinosaur_info = DinosaurInfo(**result_data)
print(f"🦕 Успешно идентифицирован: {dinosaur_info.species_name}")
return dinosaur_info
except json.JSONDecodeError as e:
print(f"❌ Ошибка парсинга JSON: {e}")
print(f"📄 Полученный ответ: {result_text}")
return None
except Exception as e:
print(f"❌ Ошибка при анализе изображения: {e}")
return None
def print_dinosaur_info(self, info: DinosaurInfo) -> None:
"""
Красиво выводит информацию о динозавре.
Args:
info: Объект с информацией о динозавре
"""
separator = "=" * 50
print(f"\n{separator}")
print("🦕 ИНФОРМАЦИЯ О ДИНОЗАВРЕ 🦕")
print(f"{separator}")
print(f"📛 Вид: {info.species_name}")
print(f"🎨 Цвет фигурки: {info.color_description}")
print(f"⏰ Период: {info.geological_period}")
print(f"📚 Интересный факт: {info.brief_info}")
print(f"{separator}\n")
def main():
"""Основная функция для демонстрации работы анализатора."""
# Пример использования
try:
analyzer = DinosaurAnalyzer()
# Замените на путь к вашему изображению динозавра
image_path = input("Введите путь к изображению динозавра: ").strip()
if not image_path:
print("Путь к изображению не указан")
return
print("🔍 Анализируем изображение...")
info = analyzer.analyze_image(image_path)
if info:
analyzer.print_dinosaur_info(info)
else:
print("❌ Не удалось проанализировать изображение")
except ValueError as e:
print(f"❌ Ошибка конфигурации: {e}")
print("💡 Убедитесь, что у вас есть API ключ для Gemini")
except Exception as e:
print(f"❌ Неожиданная ошибка: {e}")
if __name__ == "__main__":
main()