File size: 10,484 Bytes
b0487df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
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()