cngsm commited on
Commit
c0c39ac
·
verified ·
1 Parent(s): 0140812

Update video_analyzer_keyframes.py

Browse files
Files changed (1) hide show
  1. video_analyzer_keyframes.py +44 -43
video_analyzer_keyframes.py CHANGED
@@ -31,88 +31,89 @@ class VideoSceneAnalyzer:
31
  'aspect_ratio': self.clip.size[0] / self.clip.size[1],
32
  'total_frames': int(self.clip.duration * self.clip.fps)
33
  }
34
- print(f"✅ Vídeo carregado: {self.video_info}")
35
  return True
36
  except Exception as e:
37
- print(f"Erro ao carregar vídeo: {e}")
38
  return False
39
 
40
- def describe_image_and_generate_prompt(self, frame, scene_number):
41
- temp_img_path = f"temp_scene_{scene_number:02d}.jpg"
42
- Image.fromarray(np.uint8(frame)).save(temp_img_path)
43
- image = Image.open(temp_img_path).convert("RGB")
44
- inputs = blip_processor(images=image, return_tensors="pt")
 
45
  out = blip_model.generate(**inputs)
46
- caption = blip_processor.decode(out[0], skip_special_tokens=True).strip().capitalize()
 
 
 
 
 
 
 
 
47
 
48
- descricao = f"Imagem da cena {scene_number}: {caption}."
49
  prompt = (
50
- f"A cinematic, naturalistic shot showing: {caption}. "
51
- "Captured with shallow depth of field, soft natural light, and handheld motion. "
52
- "Realistic skin texture, clean background separation, true-to-life tone. "
 
 
53
  "--ar 16:9 --v 6 --style photorealistic --quality 2"
54
  )
55
- negative_prompt = "--no (CGI skin, artificial lighting, cartoon textures, overexposed highlights, static pose, low detail)"
56
- return descricao, prompt, negative_prompt
 
 
 
 
57
 
58
  def extract_keyframes(self, threshold=30.0):
59
- print("🔍 Iniciando extração de quadros-chave inteligentes...")
60
  output_dir = f"keyframes_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
61
  os.makedirs(output_dir, exist_ok=True)
62
 
63
  cap = cv2.VideoCapture(self.video_path)
64
- prev_frame = None
65
- saved_count = 0
66
  success, frame = cap.read()
 
67
 
68
  while success:
69
  gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
70
- if prev_frame is None:
71
- diff = float('inf')
72
- else:
73
- diff = np.mean(cv2.absdiff(gray, prev_frame))
74
-
75
- if prev_frame is None or diff > threshold:
76
  timestamp = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000.0
77
- img_path = os.path.join(output_dir, f"keyframe_{saved_count+1:02d}.jpg")
78
  Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)).save(img_path)
79
- descricao, prompt, negative_prompt = self.describe_image_and_generate_prompt(
80
- cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), saved_count + 1
81
  )
82
- scene_info = {
83
- 'scene_number': saved_count + 1,
84
  'time': timestamp,
85
  'image_path': img_path,
86
- 'descricao_detalhada': descricao,
87
- 'prompt_ia': prompt,
88
- 'negative_prompt': negative_prompt
89
- }
90
- self.scenes.append(scene_info)
91
- saved_count += 1
92
- print(f"✅ Keyframe {saved_count} salvo (diferença: {diff:.2f})")
93
-
94
- prev_frame = gray
95
  success, frame = cap.read()
96
 
97
  cap.release()
98
- print(f"🎞️ {saved_count} quadros-chave extraídos com sucesso.")
99
  return True
100
 
101
  def save_results(self, output_file=None):
102
  if not output_file:
103
  output_file = f"video_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
104
-
105
  results = {
106
  'video_info': self.video_info,
107
- 'analysis_type': "keyframe_extraction",
108
  'scenes': self.scenes,
109
  'generated_at': datetime.now().isoformat()
110
  }
111
-
112
  with open(output_file, 'w', encoding='utf-8') as f:
113
  json.dump(results, f, indent=2, ensure_ascii=False)
114
-
115
- print(f"💾 Resultados salvos em: {output_file}")
116
  return output_file
117
 
118
  def cleanup(self):
 
31
  'aspect_ratio': self.clip.size[0] / self.clip.size[1],
32
  'total_frames': int(self.clip.duration * self.clip.fps)
33
  }
 
34
  return True
35
  except Exception as e:
36
+ print(f"Erro ao carregar vídeo: {e}")
37
  return False
38
 
39
+ def describe_image_and_generate_prompt(self, frame, idx):
40
+ tmp_path = f"temp_{idx:02d}.jpg"
41
+ Image.fromarray(np.uint8(frame)).save(tmp_path)
42
+ img = Image.open(tmp_path).convert("RGB")
43
+
44
+ inputs = blip_processor(images=img, return_tensors="pt")
45
  out = blip_model.generate(**inputs)
46
+ cap = blip_processor.decode(out[0], skip_special_tokens=True).strip().capitalize()
47
+
48
+ descricao = (
49
+ f"Cena {idx}: {cap}. "
50
+ "Registrada ao entardecer com luz dourada suave, profundidade de campo rasa desfocando o fundo. "
51
+ "O ambiente apresenta uma paisagem urbana vibrante com vegetação e construções ao longe, "
52
+ "enquanto o sujeito exibe expressão serena e postura confiante. "
53
+ "Atmosfera de leve nostalgia e contemplação."
54
+ )
55
 
 
56
  prompt = (
57
+ f"A cinematic, warm golden-hour shot of {cap}. "
58
+ "Captured with a 35mm lens at f/1.8 for shallow depth of field, "
59
+ "soft backlighting, and gentle handheld motion. "
60
+ "Background features a vibrant urban setting with trees and distant buildings, "
61
+ "evoking a sense of nostalgia and calm. "
62
  "--ar 16:9 --v 6 --style photorealistic --quality 2"
63
  )
64
+
65
+ negative = (
66
+ "--no (CGI artifacts, plastic textures, overexposed skies, cartoonish colors, static poses, low detail)"
67
+ )
68
+
69
+ return descricao, prompt, negative
70
 
71
  def extract_keyframes(self, threshold=30.0):
 
72
  output_dir = f"keyframes_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
73
  os.makedirs(output_dir, exist_ok=True)
74
 
75
  cap = cv2.VideoCapture(self.video_path)
76
+ prev = None
77
+ count = 0
78
  success, frame = cap.read()
79
+ self.scenes = []
80
 
81
  while success:
82
  gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
83
+ diff = np.inf if prev is None else np.mean(cv2.absdiff(gray, prev))
84
+ if prev is None or diff > threshold:
 
 
 
 
85
  timestamp = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000.0
86
+ img_path = os.path.join(output_dir, f"keyframe_{count+1:02d}.jpg")
87
  Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)).save(img_path)
88
+ desc, prmpt, neg = self.describe_image_and_generate_prompt(
89
+ cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), count+1
90
  )
91
+ self.scenes.append({
92
+ 'scene_number': count+1,
93
  'time': timestamp,
94
  'image_path': img_path,
95
+ 'descricao_detalhada': desc,
96
+ 'prompt_ia': prmpt,
97
+ 'negative_prompt': neg
98
+ })
99
+ count += 1
100
+ prev = gray
 
 
 
101
  success, frame = cap.read()
102
 
103
  cap.release()
 
104
  return True
105
 
106
  def save_results(self, output_file=None):
107
  if not output_file:
108
  output_file = f"video_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
 
109
  results = {
110
  'video_info': self.video_info,
111
+ 'analysis_type': 'keyframe_extraction',
112
  'scenes': self.scenes,
113
  'generated_at': datetime.now().isoformat()
114
  }
 
115
  with open(output_file, 'w', encoding='utf-8') as f:
116
  json.dump(results, f, indent=2, ensure_ascii=False)
 
 
117
  return output_file
118
 
119
  def cleanup(self):