gdms commited on
Commit
4c13cc7
·
1 Parent(s): 2fd5c20

Utilizando Chessvision para gerar o FEN

Browse files
image_analysis_output/analysis_results.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "image": "arquivos-perguntas/cca530fc-4052-43b2-b130-b30968d8aa44.png",
4
+ "analysis": "3r2k1/pp3pp1/4b2p/7Q/3n4/PqBBR2P/5PP1/6K1_b_-_-_0_1"
5
+ }
6
+ ]
tool_image_to_fen.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Script para baixar um vídeo do YouTube, extrair frames, analisar com GPT-4o e contar aves.
4
+ """
5
+
6
+ import os
7
+ import subprocess
8
+ import cv2
9
+ import base64
10
+ import time
11
+ from openai import OpenAI # Importa a classe OpenAI
12
+ import json
13
+ import re
14
+ import shutil
15
+
16
+ import google.generativeai as genai
17
+ import requests
18
+
19
+
20
+ # --- Configurações (Substitua os placeholders) ---
21
+ OUTPUT_DIR = "./image_analysis_output" # Diretório para salvar o vídeo e os frames
22
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
23
+ GEMINI_MODEL = "gemini-2.0-flash"
24
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
25
+ GPT_MODEL = "gpt-4o" # Modelo GPT a ser usado (certifique-se que é o correto para análise de imagem)
26
+ PROMPT_TEXT = "Analyze the provided image of a chessboard, return the corresponding FEN (Forsyth–Edwards Notation), assuming black at the bottom and black turn. Include turn, castling rights, en passant (if possible), and full notation. Return only the FEN."
27
+ #PROMPT_TEXT = "You are a chessboard position analyzer. Given an image of a chessboard: - Assume standard orientation: White at the bottom, Black at the top. - Identify all visible pieces and their positions. - Return the FEN string corresponding to the exact position. - Be precise. Do not omit or infer captured pieces. - Return only the FEN, no explanations."
28
+ IMAGE_FILE = "arquivos-perguntas/cca530fc-4052-43b2-b130-b30968d8aa44.png"
29
+ RESULTS_FILE = os.path.join(OUTPUT_DIR, "analysis_results.json")
30
+ FEN_CORRETA = "3r2k1/pp3pp1/4b2p/7Q/3n4/PqBBR2P/5PP1/6K1_b_-_-_0_1"
31
+ CHESSVISION_TO_FEN_URL = "http://app.chessvision.ai/predict"
32
+
33
+
34
+
35
+ if GEMINI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI" or not GEMINI_API_KEY or len(GEMINI_API_KEY) ==0 :
36
+ print("AVISO: A chave da API GEMINI não foi definida. Por favor, edite o script e insira sua chave.")
37
+ # Considerar sair do script ou lançar um erro se a chave for essencial para a execução completa
38
+ # exit(1)
39
+
40
+ # --- Funções ---
41
+
42
+ def create_or_clear_output_directory():
43
+ """Cria o diretório de saída se não existir."""
44
+ if not os.path.exists(OUTPUT_DIR):
45
+ os.makedirs(OUTPUT_DIR)
46
+ print(f"Diretório criado: {OUTPUT_DIR}")
47
+ else:
48
+ # Limpa todos os arquivos e subdiretórios
49
+ for filename in os.listdir(OUTPUT_DIR):
50
+ file_path = os.path.join(OUTPUT_DIR, filename)
51
+ try:
52
+ if os.path.isfile(file_path) or os.path.islink(file_path):
53
+ os.unlink(file_path)
54
+ elif os.path.isdir(file_path):
55
+ shutil.rmtree(file_path)
56
+ except Exception as e:
57
+ print(f"Erro ao excluir {file_path}: {e}")
58
+ print(f"Diretório limpo: {OUTPUT_DIR}")
59
+
60
+
61
+
62
+
63
+ def encode_image_to_base64(image_path):
64
+ """Codifica um arquivo de imagem (frame) para base64."""
65
+ try:
66
+ with open(image_path, "rb") as image_file:
67
+ return base64.b64encode(image_file.read()).decode('utf-8')
68
+ except FileNotFoundError:
69
+ print(f"Erro: Arquivo de frame não encontrado em {image_path}")
70
+ return None
71
+ except Exception as e:
72
+ print(f"Erro ao codificar o frame {image_path} para base64: {e}")
73
+ return None
74
+
75
+
76
+ def analyze_image_with_gpt(base64_image, prompt):
77
+ if OPENAI_API_KEY:
78
+ try:
79
+ openai_client = OpenAI(api_key=OPENAI_API_KEY)
80
+ print("Cliente OpenAI inicializado.")
81
+ except Exception as e:
82
+ print(f"Erro ao inicializar o cliente OpenAI: {e}. As chamadas de API serão puladas.")
83
+ else:
84
+ print("Chave da API OpenAI não configurada. As chamadas de API serão puladas.")
85
+
86
+ """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
87
+ print(f"Enviando imagem para análise no {GPT_MODEL}...")
88
+
89
+
90
+ payload = {
91
+ "model": GPT_MODEL,
92
+ "messages": [
93
+ {
94
+ "role": "user",
95
+ "content": [
96
+ {
97
+ "type": "text",
98
+ "text": prompt
99
+ },
100
+ {
101
+ "type": "image_url",
102
+ "image_url": {
103
+ "url": f"data:image/png;base64,{base64_image}"
104
+ }
105
+ }
106
+ ]
107
+ }
108
+ ],
109
+ "max_tokens": 100 # Ajuste conforme necessário para a resposta esperada
110
+ }
111
+
112
+ try:
113
+ # Cria o cliente OpenAI dentro da função para garantir que use a chave mais recente
114
+ # (embora seja definida globalmente, isso pode ser útil se a chave for atualizada dinamicamente no futuro)
115
+ # client = OpenAI(api_key=OPENAI_API_KEY)
116
+
117
+ response = openai_client.chat.completions.create(
118
+ model=payload["model"],
119
+ messages=payload["messages"],
120
+ max_tokens=payload["max_tokens"],
121
+ temperature=0
122
+ )
123
+
124
+ # Extrai o conteúdo da resposta
125
+ analysis_result = response.choices[0].message.content.strip()
126
+ fen = analysis_result.strip("`")
127
+ print(f"Análise recebida (raw): {analysis_result}")
128
+ print(f"Análise tratada : {fen}")
129
+ if fen != FEN_CORRETA:
130
+ print(f"FEN INCORRETA ")
131
+ else:
132
+ print(f"FEN CORRETA ")
133
+
134
+ return {"image_response": fen}
135
+ except Exception as e:
136
+ print(f"Erro ao chamar a API OpenAI: {e}")
137
+ return {"error": str(e)}
138
+
139
+
140
+ def analyze_image_with_gemini(base64_image, prompt):
141
+ genai.configure(api_key=GEMINI_API_KEY)
142
+ model = genai.GenerativeModel(GEMINI_MODEL)
143
+
144
+ """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
145
+ print(f"Enviando frame para análise no {GEMINI_MODEL}...")
146
+
147
+
148
+ try:
149
+
150
+ response = model.generate_content(
151
+ contents=[
152
+ {
153
+ "role": "user",
154
+ "parts": [
155
+ {f"text": f"{prompt}"},
156
+ {"inline_data": {
157
+ "mime_type": "image/jpeg",
158
+ "data": base64_image
159
+ }}
160
+ ]
161
+ }
162
+ ],
163
+ generation_config={
164
+ "temperature": 0.7,
165
+ "max_output_tokens": 500
166
+ })
167
+
168
+ # Extrai o conteúdo da resposta
169
+ analysis_result = response.text.strip()
170
+ print(f"Análise recebida: {analysis_result}")
171
+
172
+ return {"image_response": analysis_result}
173
+
174
+ except Exception as e:
175
+ print(f"Erro ao chamar a API Gemini: {e}")
176
+ return {"error": str(e)}
177
+
178
+
179
+ def analyze_image_with_chessvision(base64_image):
180
+ base64_image_encoded = f"data:image/jpeg;base64,{base64_image}"
181
+ url = CHESSVISION_TO_FEN_URL
182
+ payload = {
183
+ "board_orientation": "predict",
184
+ "cropped": False,
185
+ "current_player": "black",
186
+ "image": base64_image_encoded,
187
+ "predict_turn": False
188
+ }
189
+
190
+ response = requests.post(url, json=payload)
191
+ if response.status_code == 200:
192
+ dados = response.json()
193
+ if dados.get("success"):
194
+ print(f"Retorno Chessvision {dados}")
195
+ fen = dados.get("result")
196
+ return fen
197
+ else:
198
+ raise Exception("Requisição feita, mas falhou na predição.")
199
+ else:
200
+ raise Exception(f"Erro na requisição: {response.status_code}")
201
+
202
+ def save_results_to_json(results_list, output_file):
203
+ """Salva a lista de resultados da análise em um arquivo JSON."""
204
+ print(f"Salvando resultados da análise em {output_file}...")
205
+ try:
206
+ with open(output_file, 'w', encoding='utf-8') as f:
207
+ json.dump(results_list, f, ensure_ascii=False, indent=4)
208
+ print(f"Resultados salvos com sucesso em: {output_file}")
209
+ return True
210
+ except Exception as e:
211
+ print(f"Erro ao salvar os resultados em JSON: {e}")
212
+ return False
213
+
214
+
215
+ # --- Atualização do Bloco Principal ---
216
+ # (Adicionar inicialização do cliente OpenAI e o loop de análise)
217
+ if __name__ == "__main__":
218
+ create_or_clear_output_directory()
219
+ analysis_results_list = []
220
+
221
+ print(f"\nIniciando análise da imagem {IMAGE_FILE} frames com {GEMINI_MODEL}...")
222
+ # Extrai timestamp do nome do arquivo, se possível
223
+ base64_image = encode_image_to_base64(IMAGE_FILE)
224
+ if base64_image:
225
+ # Analisa a imagem com Gemini
226
+ analysis_result = analyze_image_with_chessvision(base64_image) #analyze_image_with_gpt(base64_image, PROMPT_TEXT)
227
+ result_entry = {
228
+ "image": IMAGE_FILE,
229
+ "analysis": analysis_result
230
+ }
231
+ analysis_results_list.append(result_entry)
232
+
233
+ else:
234
+ print(f"Falha ao codificar o frame {IMAGE_FILE}. Pulando análise.")
235
+ analysis_results_list.append({
236
+ "frame_path": IMAGE_FILE,
237
+ "analysis": {"error": "Failed to encode frame to base64."}
238
+ })
239
+
240
+ # break # teste somente uma chamada
241
+ print("\nAnálise de imagem concluída.")
242
+
243
+ # Próxima etapa: Compilar resultados
244
+ print(f"\nPróxima etapa a ser implementada: Compilação dos resultados ({len(analysis_results_list)} análises) em um relatório.")
245
+
246
+
247
+ # ... (código anterior para inicialização, download, extração, análise) ...
248
+
249
+ # Etapa 5: Compilar e Salvar Resultados
250
+ if analysis_results_list:
251
+ print(f"\nCompilando {len(analysis_results_list)} resultados da análise...")
252
+ if save_results_to_json(analysis_results_list, RESULTS_FILE):
253
+ print("Compilação e salvamento dos resultados concluídos.")
254
+ else:
255
+ print("Falha ao salvar os resultados da análise.")
256
+ else:
257
+ print("Nenhum resultado de análise para compilar.")
258
+
259
+ print("\n--- Processo de Análise de Vídeo Concluído ---")
260
+ print(f"Verifique o diretório '{OUTPUT_DIR}' para os frames extraídos (se aplicável).")
261
+ print(f"Verifique o arquivo '{RESULTS_FILE}' para os resultados da análise (se aplicável).")
262
+ print("Lembre-se de substituir os placeholders para URL_DO_SEU_VIDEO_AQUI e SUA_CHAVE_API_OPENAI_AQUI no script.")
263
+
264
+
tool_video_analyzer.py CHANGED
@@ -280,11 +280,7 @@ def analyze_frame_with_gemini(base64_image, prompt):
280
 
281
  """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
282
  print(f"Enviando frame para análise no {GEMINI_MODEL}...")
283
- headers = {
284
- "Content-Type": "application/json",
285
- "Authorization": f"Bearer {OPENAI_API_KEY}"
286
- }
287
-
288
  try:
289
 
290
  response = model.generate_content(
@@ -317,7 +313,7 @@ def analyze_frame_with_gemini(base64_image, prompt):
317
  return {"error": "Failed to parse bird count from response.", "raw_response": analysis_result}
318
 
319
  except Exception as e:
320
- print(f"Erro ao chamar a API OpenAI: {e}")
321
  return {"error": str(e)}
322
 
323
 
 
280
 
281
  """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
282
  print(f"Enviando frame para análise no {GEMINI_MODEL}...")
283
+
 
 
 
 
284
  try:
285
 
286
  response = model.generate_content(
 
313
  return {"error": "Failed to parse bird count from response.", "raw_response": analysis_result}
314
 
315
  except Exception as e:
316
+ print(f"Erro ao chamar a API Gemini: {e}")
317
  return {"error": str(e)}
318
 
319
 
video_analyzer_temp.py DELETED
@@ -1,391 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Script para baixar um vídeo do YouTube, extrair frames, analisar com GPT-4o e contar aves.
4
- """
5
-
6
- import os
7
- import subprocess
8
- import cv2
9
- import base64
10
- import time
11
- from openai import OpenAI # Importa a classe OpenAI
12
- import json
13
-
14
- # --- Configurações (Substitua os placeholders) ---
15
- VIDEO_URL = "URL_DO_SEU_VIDEO_AQUI" # Substitua pela URL do vídeo do YouTube
16
- OUTPUT_DIR = "./video_analysis_output" # Diretório para salvar o vídeo e os frames
17
- FRAME_INTERVAL_SECONDS = 5 # Intervalo entre frames a serem extraídos
18
- OPENAI_API_KEY = "SUA_CHAVE_API_OPENAI_AQUI" # Substitua pela sua chave da API OpenAI
19
- GPT_MODEL = "gpt-4o" # Modelo GPT a ser usado (certifique-se que é o correto para análise de imagem)
20
- PROMPT_TEXT = "Quantas aves existem nesta imagem? Responda apenas com o número." # Prompt para o GPT-4o
21
- RESULTS_FILE = os.path.join(OUTPUT_DIR, "analysis_results.json")
22
- VIDEO_FILENAME = "downloaded_video.mp4"
23
- VIDEO_PATH = os.path.join(OUTPUT_DIR, VIDEO_FILENAME)
24
-
25
- # Verifica se a chave da API foi definida
26
- if OPENAI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI":
27
- print("AVISO: A chave da API OpenAI não foi definida. Por favor, edite o script e insira sua chave.")
28
- # Considerar sair do script ou lançar um erro se a chave for essencial para a execução completa
29
- # exit(1)
30
-
31
- # Verifica se a URL foi definida
32
- if VIDEO_URL == "URL_DO_SEU_VIDEO_AQUI":
33
- print("AVISO: A URL do vídeo não foi definida. Por favor, edite o script e insira a URL desejada.")
34
- # exit(1)
35
-
36
- # --- Funções ---
37
-
38
- def create_output_directory():
39
- """Cria o diretório de saída se não existir."""
40
- if not os.path.exists(OUTPUT_DIR):
41
- os.makedirs(OUTPUT_DIR)
42
- print(f"Diretório criado: {OUTPUT_DIR}")
43
-
44
- def download_video(url, output_path):
45
- """Baixa o vídeo do YouTube usando yt-dlp."""
46
- print(f"Baixando vídeo de {url} para {output_path}...")
47
- try:
48
- # Comando yt-dlp para baixar o melhor formato mp4
49
- command = [
50
- 'yt-dlp',
51
- '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
52
- '-o', output_path,
53
- url
54
- ]
55
- result = subprocess.run(command, check=True, capture_output=True, text=True)
56
- print("Download concluído com sucesso.")
57
- # print(result.stdout) # Descomente para ver a saída do yt-dlp
58
- return True
59
- except subprocess.CalledProcessError as e:
60
- print(f"Erro ao baixar o vídeo: {e}")
61
- print(f"Saída do erro: {e.stderr}")
62
- return False
63
- except FileNotFoundError:
64
- print("Erro: O comando 'yt-dlp' não foi encontrado. Certifique-se de que ele está instalado e no PATH do sistema.")
65
- print("Você pode instalá-lo com: pip install yt-dlp")
66
- return False
67
-
68
- # --- Bloco Principal (Inicial) ---
69
- if __name__ == "__main__":
70
- create_output_directory()
71
-
72
- # Etapa 1: Baixar o vídeo
73
- if VIDEO_URL != "URL_DO_SEU_VIDEO_AQUI":
74
- if not download_video(VIDEO_URL, VIDEO_PATH):
75
- print("Falha no download do vídeo. Abortando.")
76
- # exit(1) # Descomente se quiser que o script pare aqui em caso de falha no download
77
- else:
78
- print(f"Vídeo salvo em: {VIDEO_PATH}")
79
- else:
80
- print("URL do vídeo não fornecida. Pulando etapa de download.")
81
- print("Certifique-se de que o arquivo 'downloaded_video.mp4' existe em '{OUTPUT_DIR}' se quiser continuar.")
82
-
83
- # Próximas etapas (extração de frames, análise, etc.) serão adicionadas aqui
84
- print("\nPróximas etapas a serem implementadas: extração de frames, análise com GPT-4o, compilação de resultados.")
85
-
86
-
87
-
88
-
89
- def extract_frames(video_path, output_dir, interval_sec):
90
- """Extrai frames de um vídeo em intervalos específicos."""
91
- print(f"Extraindo frames de {video_path} a cada {interval_sec} segundos...")
92
- if not os.path.exists(video_path):
93
- print(f"Erro: Arquivo de vídeo não encontrado em {video_path}")
94
- return []
95
-
96
- cap = cv2.VideoCapture(video_path)
97
- if not cap.isOpened():
98
- print(f"Erro ao abrir o arquivo de vídeo: {video_path}")
99
- return []
100
-
101
- fps = cap.get(cv2.CAP_PROP_FPS)
102
- if fps == 0:
103
- print("Erro: Não foi possível obter o FPS do vídeo. Usando FPS padrão de 30.")
104
- fps = 30 # Valor padrão caso a leitura falhe
105
-
106
- frame_interval = int(fps * interval_sec)
107
- total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
108
- print(f"Vídeo FPS: {fps:.2f}, Intervalo de frames: {frame_interval}, Total de frames: {total_frames}")
109
-
110
- extracted_frames_paths = []
111
- frame_count = 0
112
- saved_frame_index = 0
113
-
114
- while True:
115
- # Define a posição do próximo frame a ser lido
116
- # Adiciona frame_interval para pegar o frame *após* o intervalo de tempo
117
- target_frame_pos = saved_frame_index * frame_interval
118
- if target_frame_pos >= total_frames:
119
- break # Sai se o próximo frame alvo estiver além do final do vídeo
120
-
121
- cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame_pos)
122
- ret, frame = cap.read()
123
-
124
- if not ret:
125
- print(f"Não foi possível ler o frame na posição {target_frame_pos}. Pode ser o fim do vídeo ou um erro.")
126
- break # Sai se não conseguir ler o frame
127
-
128
- # Calcula o timestamp em segundos
129
- timestamp_sec = target_frame_pos / fps
130
-
131
- # Salva o frame
132
- frame_filename = f"frame_{saved_frame_index:04d}_time_{timestamp_sec:.2f}s.png"
133
- frame_path = os.path.join(output_dir, frame_filename)
134
- try:
135
- cv2.imwrite(frame_path, frame)
136
- extracted_frames_paths.append(frame_path)
137
- print(f"Frame salvo: {frame_path} (Timestamp: {timestamp_sec:.2f}s)")
138
- saved_frame_index += 1
139
- except Exception as e:
140
- print(f"Erro ao salvar o frame {frame_path}: {e}")
141
- # Continua para o próximo intervalo mesmo se um frame falhar
142
-
143
- # Segurança para evitar loop infinito caso algo dê errado com a lógica de posição
144
- if saved_frame_index > (total_frames / frame_interval) + 2:
145
- print("Aviso: Número de frames salvos parece exceder o esperado. Interrompendo extração.")
146
- break
147
-
148
- cap.release()
149
- print(f"Extração de frames concluída. Total de frames salvos: {len(extracted_frames_paths)}")
150
- return extracted_frames_paths
151
-
152
- # --- Atualização do Bloco Principal ---
153
- # (Remover a linha print anterior sobre próximas etapas e adicionar a chamada da nova função)
154
-
155
-
156
-
157
-
158
- def encode_frame_to_base64(frame_path):
159
- """Codifica um arquivo de imagem (frame) para base64."""
160
- try:
161
- with open(frame_path, "rb") as image_file:
162
- return base64.b64encode(image_file.read()).decode('utf-8')
163
- except FileNotFoundError:
164
- print(f"Erro: Arquivo de frame não encontrado em {frame_path}")
165
- return None
166
- except Exception as e:
167
- print(f"Erro ao codificar o frame {frame_path} para base64: {e}")
168
- return None
169
-
170
- # --- Atualização do Bloco Principal ---
171
- # (Remover a linha print anterior sobre próximas etapas e adicionar a chamada da nova função)
172
- if __name__ == "__main__":
173
- create_output_directory()
174
- extracted_frames = [] # Inicializa a lista de frames extraídos
175
-
176
- # Etapa 1: Baixar o vídeo
177
- if VIDEO_URL != "URL_DO_SEU_VIDEO_AQUI":
178
- if not download_video(VIDEO_URL, VIDEO_PATH):
179
- print("Falha no download do vídeo. Pulando etapas dependentes.")
180
- # Considerar sair se o vídeo for essencial
181
- # exit(1)
182
- else:
183
- print(f"Vídeo salvo em: {VIDEO_PATH}")
184
- # Etapa 2: Extrair frames (só executa se o download foi bem-sucedido ou se a URL não foi fornecida mas o vídeo existe)
185
- if os.path.exists(VIDEO_PATH):
186
- extracted_frames = extract_frames(VIDEO_PATH, OUTPUT_DIR, FRAME_INTERVAL_SECONDS)
187
- else:
188
- print(f"Arquivo de vídeo {VIDEO_PATH} não encontrado após tentativa de download. Pulando extração de frames.")
189
-
190
- elif os.path.exists(VIDEO_PATH):
191
- print(f"URL não fornecida, mas vídeo encontrado em {VIDEO_PATH}. Tentando extrair frames.")
192
- extracted_frames = extract_frames(VIDEO_PATH, OUTPUT_DIR, FRAME_INTERVAL_SECONDS)
193
- else:
194
- print("URL do vídeo não fornecida e vídeo local não encontrado. Pulando download e extração de frames.")
195
- print(f"Certifique-se de que o arquivo '{VIDEO_FILENAME}' existe em '{OUTPUT_DIR}' para processamento manual.")
196
-
197
- # Etapa 3: Codificar frames para base64 (será usado na próxima etapa de análise)
198
- if extracted_frames:
199
- print(f"\nIniciando codificação de {len(extracted_frames)} frames para base64...")
200
- # A codificação será feita dentro do loop de análise na próxima etapa
201
- # para otimizar o uso de memória, processando um frame por vez.
202
- print("Codificação será realizada frame a frame antes do envio para a API.")
203
- else:
204
- print("Nenhum frame foi extraído. Pulando etapas de codificação e análise.")
205
-
206
- # Próxima etapa: Enviar frames para GPT-4o
207
- print("\nPróxima etapa a ser implementada: Envio dos frames (codificados em base64) para análise com GPT-4o.")
208
-
209
-
210
-
211
-
212
- def analyze_frame_with_gpt4o(client, base64_image, prompt):
213
- """Envia um frame codificado em base64 para a API GPT-4o e retorna a análise."""
214
- print(f"Enviando frame para análise no {GPT_MODEL}...")
215
- headers = {
216
- "Content-Type": "application/json",
217
- "Authorization": f"Bearer {OPENAI_API_KEY}"
218
- }
219
-
220
- payload = {
221
- "model": GPT_MODEL,
222
- "messages": [
223
- {
224
- "role": "user",
225
- "content": [
226
- {
227
- "type": "text",
228
- "text": prompt
229
- },
230
- {
231
- "type": "image_url",
232
- "image_url": {
233
- "url": f"data:image/png;base64,{base64_image}"
234
- }
235
- }
236
- ]
237
- }
238
- ],
239
- "max_tokens": 100 # Ajuste conforme necessário para a resposta esperada
240
- }
241
-
242
- try:
243
- # Verifica se a chave da API é o placeholder
244
- if OPENAI_API_KEY == "SUA_CHAVE_API_OPENAI_AQUI":
245
- print("AVISO: Chave da API OpenAI não configurada. Pulando análise.")
246
- return {"error": "API key not configured."}
247
-
248
- # Cria o cliente OpenAI dentro da função para garantir que use a chave mais recente
249
- # (embora seja definida globalmente, isso pode ser útil se a chave for atualizada dinamicamente no futuro)
250
- # client = OpenAI(api_key=OPENAI_API_KEY)
251
-
252
- response = client.chat.completions.create(
253
- model=payload["model"],
254
- messages=payload["messages"],
255
- max_tokens=payload["max_tokens"]
256
- )
257
-
258
- # Extrai o conteúdo da resposta
259
- analysis_result = response.choices[0].message.content.strip()
260
- print(f"Análise recebida: {analysis_result}")
261
- # Tenta converter a resposta para um inteiro (contagem de aves)
262
- try:
263
- bird_count = int(analysis_result)
264
- return {"bird_count": bird_count, "raw_response": analysis_result}
265
- except ValueError:
266
- print(f"Aviso: Não foi possível converter a resposta '{analysis_result}' para um número inteiro.")
267
- return {"error": "Failed to parse bird count from response.", "raw_response": analysis_result}
268
-
269
- except Exception as e:
270
- print(f"Erro ao chamar a API OpenAI: {e}")
271
- return {"error": str(e)}
272
-
273
- # --- Atualização do Bloco Principal ---
274
- # (Adicionar inicialização do cliente OpenAI e o loop de análise)
275
- if __name__ == "__main__":
276
- create_output_directory()
277
- extracted_frames = []
278
- analysis_results_list = []
279
-
280
- # Inicializa o cliente OpenAI (se a chave estiver definida)
281
- openai_client = None
282
- if OPENAI_API_KEY != "SUA_CHAVE_API_OPENAI_AQUI":
283
- try:
284
- openai_client = OpenAI(api_key=OPENAI_API_KEY)
285
- print("Cliente OpenAI inicializado.")
286
- except Exception as e:
287
- print(f"Erro ao inicializar o cliente OpenAI: {e}. As chamadas de API serão puladas.")
288
- else:
289
- print("Chave da API OpenAI não configurada. As chamadas de API serão puladas.")
290
-
291
- # Etapa 1: Baixar o vídeo
292
- video_downloaded_or_exists = False
293
- if VIDEO_URL != "URL_DO_SEU_VIDEO_AQUI":
294
- if download_video(VIDEO_URL, VIDEO_PATH):
295
- print(f"Vídeo salvo em: {VIDEO_PATH}")
296
- video_downloaded_or_exists = True
297
- else:
298
- print("Falha no download do vídeo. Pulando etapas dependentes.")
299
- elif os.path.exists(VIDEO_PATH):
300
- print(f"URL não fornecida, mas vídeo encontrado em {VIDEO_PATH}. Tentando processar.")
301
- video_downloaded_or_exists = True
302
- else:
303
- print("URL do vídeo não fornecida e vídeo local não encontrado. Pulando download e extração.")
304
-
305
- # Etapa 2: Extrair frames
306
- if video_downloaded_or_exists:
307
- extracted_frames = extract_frames(VIDEO_PATH, OUTPUT_DIR, FRAME_INTERVAL_SECONDS)
308
- else:
309
- print("Pulando extração de frames pois o vídeo não está disponível.")
310
-
311
- # Etapa 3 e 4: Codificar e Analisar Frames
312
- if extracted_frames and openai_client:
313
- print(f"\nIniciando análise de {len(extracted_frames)} frames com {GPT_MODEL}...")
314
- for frame_path in extracted_frames:
315
- print(f"\nProcessando frame: {frame_path}")
316
- # Extrai timestamp do nome do arquivo, se possível
317
- timestamp_str = "unknown"
318
- try:
319
- # Exemplo: frame_0000_time_0.00s.png
320
- parts = os.path.basename(frame_path).split('_')
321
- if len(parts) >= 4 and parts[2] == 'time':
322
- timestamp_str = parts[3].replace('s.png','')
323
- except Exception:
324
- pass # Mantém 'unknown' se o parsing falhar
325
-
326
- # Codifica o frame
327
- base64_image = encode_frame_to_base64(frame_path)
328
-
329
- if base64_image:
330
- # Analisa o frame com GPT-4o
331
- analysis_result = analyze_frame_with_gpt4o(openai_client, base64_image, PROMPT_TEXT)
332
- result_entry = {
333
- "frame_path": frame_path,
334
- "timestamp_approx_sec": timestamp_str,
335
- "analysis": analysis_result
336
- }
337
- analysis_results_list.append(result_entry)
338
-
339
- # Pausa opcional para evitar rate limiting
340
- time.sleep(1) # Pausa de 1 segundo entre as chamadas
341
- else:
342
- print(f"Falha ao codificar o frame {frame_path}. Pulando análise.")
343
- analysis_results_list.append({
344
- "frame_path": frame_path,
345
- "timestamp_approx_sec": timestamp_str,
346
- "analysis": {"error": "Failed to encode frame to base64."}
347
- })
348
- print("\nAnálise de todos os frames concluída.")
349
- elif not extracted_frames:
350
- print("Nenhum frame foi extraído. Pulando etapa de análise.")
351
- elif not openai_client:
352
- print("Cliente OpenAI não inicializado (verifique a API Key). Pulando etapa de análise.")
353
-
354
- # Próxima etapa: Compilar resultados
355
- print(f"\nPróxima etapa a ser implementada: Compilação dos resultados ({len(analysis_results_list)} análises) em um relatório.")
356
-
357
-
358
-
359
-
360
- def save_results_to_json(results_list, output_file):
361
- """Salva a lista de resultados da análise em um arquivo JSON."""
362
- print(f"Salvando resultados da análise em {output_file}...")
363
- try:
364
- with open(output_file, 'w', encoding='utf-8') as f:
365
- json.dump(results_list, f, ensure_ascii=False, indent=4)
366
- print(f"Resultados salvos com sucesso em: {output_file}")
367
- return True
368
- except Exception as e:
369
- print(f"Erro ao salvar os resultados em JSON: {e}")
370
- return False
371
-
372
- # --- Finalização do Bloco Principal ---
373
- if __name__ == "__main__":
374
- # ... (código anterior para inicialização, download, extração, análise) ...
375
-
376
- # Etapa 5: Compilar e Salvar Resultados
377
- if analysis_results_list:
378
- print(f"\nCompilando {len(analysis_results_list)} resultados da análise...")
379
- if save_results_to_json(analysis_results_list, RESULTS_FILE):
380
- print("Compilação e salvamento dos resultados concluídos.")
381
- else:
382
- print("Falha ao salvar os resultados da análise.")
383
- else:
384
- print("Nenhum resultado de análise para compilar.")
385
-
386
- print("\n--- Processo de Análise de Vídeo Concluído ---")
387
- print(f"Verifique o diretório '{OUTPUT_DIR}' para os frames extraídos (se aplicável).")
388
- print(f"Verifique o arquivo '{RESULTS_FILE}' para os resultados da análise (se aplicável).")
389
- print("Lembre-se de substituir os placeholders para URL_DO_SEU_VIDEO_AQUI e SUA_CHAVE_API_OPENAI_AQUI no script.")
390
-
391
-