AI Agents · Aula

Compreensão de vídeo em agentes

Extração de quadros, resumo de vídeos e raciocínio temporal sobre clipes.

Aula 3 de 413 etapas

Compreensão de vídeo em agentes é uma aula grátis de AI Agents no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Agents, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Agents inclui 4 aulas no total.

Compreensão de vídeo para agentes

A maioria dos LLMs não consegue processar arquivos de vídeo diretamente, mas os agentes podem analisar vídeos extraindo quadros representativos e enviando-os como imagens. Em seguida, o agente raciocina sobre a sequência visual para detectar eventos, mudanças e padrões ao longo do tempo.

Instalação do OpenCV para extração de quadros

O OpenCV (cv2) é a biblioteca padrão para processamento de vídeo em Python. Ele permite abrir arquivos de vídeo, ler seus metadados (taxa de quadros, resolução e quantidade de quadros) e extrair quadros individuais como matrizes NumPy.

# pip install opencv-python-headless
import cv2

def get_video_metadata(video_path: str) -> dict:
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        raise IOError(f'Cannot open video: {video_path}')

    fps = cap.get(cv2.CAP_PROP_FPS)
    frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    duration_s = frame_count / fps if fps > 0 else 0
    cap.release()

    return {
        'fps': round(fps, 2),
        'frame_count': frame_count,
        'width': width,
        'height': height,
        'duration_seconds': round(duration_s, 2)
    }

meta = get_video_metadata('recording.mp4')
print(meta)

Extração de quadros a cada N segundos

Na maioria das tarefas de compreensão de vídeo, não é necessário usar todos os quadros. A amostragem de um quadro a cada N segundos fornece um resumo representativo do conteúdo do vídeo. A 1 quadro por segundo, um vídeo de 60 segundos produz 60 quadros.

import cv2
import os

def extract_frames(
    video_path: str,
    output_dir: str,
    every_n_seconds: float = 5.0
) -> list:
    cap = cv2.VideoCapture(video_path)
    fps = cap.get(cv2.CAP_PROP_FPS)
    frame_interval = int(fps * every_n_seconds)
    os.makedirs(output_dir, exist_ok=True)

    saved_frames = []
    frame_idx = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        if frame_idx % frame_interval == 0:
            timestamp = round(frame_idx / fps, 2)
            fname = f'{output_dir}/frame_{frame_idx:06d}_{timestamp}s.jpg'
            cv2.imwrite(fname, frame)
            saved_frames.append({'path': fname, 'timestamp': timestamp})
        frame_idx += 1

    cap.release()
    print(f'Extracted {len(saved_frames)} frames')
    return saved_frames

Detecção de mudança de cena

Em vez de fazer uma amostragem uniforme, extraia quadros nos limites das cenas — momentos em que o conteúdo visual muda significativamente. Isso fornece quadros mais informativos por chamada à API. Use a diferenciação de quadros: se a diferença absoluta média entre quadros consecutivos exceder um limiar, ocorreu uma mudança de cena.

import cv2
import numpy as np

def extract_scene_change_frames(
    video_path: str,
    output_dir: str,
    diff_threshold: float = 30.0
) -> list:
    cap = cv2.VideoCapture(video_path)
    fps = cap.get(cv2.CAP_PROP_FPS)
    os.makedirs(output_dir, exist_ok=True)
    prev_gray = None
    saved = []
    frame_idx = 0

    while True:
        ret, frame = cap.read()
        if not ret:
            break
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        if prev_gray is not None:
            diff = np.mean(np.abs(gray.astype(float) - prev_gray.astype(float)))
            if diff > diff_threshold:
                ts = round(frame_idx / fps, 2)
                fname = f'{output_dir}/scene_{frame_idx:06d}_{ts}s.jpg'
                cv2.imwrite(fname, frame)
                saved.append({'path': fname, 'timestamp': ts, 'diff': round(diff, 2)})
        prev_gray = gray
        frame_idx += 1

    cap.release()
    return saved

Envio de uma sequência de quadros à API de visão

Envie vários quadros extraídos à API de visão em uma única mensagem. Inclua a marcação de tempo de cada quadro para que o modelo possa raciocinar sobre a ordem temporal e a duração. Peça explicitamente raciocínio temporal na sua instrução.

import base64
import anthropic

def describe_video_frames(frame_info_list: list, query: str) -> str:
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    content = []
    for fi in frame_info_list:
        with open(fi['path'], 'rb') as f:
            b64 = base64.standard_b64encode(f.read()).decode('utf-8')
        content.append({
            'type': 'text',
            'text': f'Frame at {fi["timestamp"]}s:'
        })
        content.append({
            'type': 'image',
            'source': {'type': 'base64', 'media_type': 'image/jpeg', 'data': b64}
        })
    content.append({'type': 'text', 'text': query})

    response = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=1024,
        messages=[{'role': 'user', 'content': content}]
    )
    return response.content[0].text

Instruções para raciocínio temporal

O raciocínio temporal exige uma formulação específica da instrução. Peça ao modelo que compare os quadros explicitamente, observe o que mudou, estime a duração das atividades e identifique relações causais entre os quadros.

TEMPORAL_QUERY = (
    'You are analysing a video sequence. The frames above are in chronological order '
    'with their timestamps shown. Please:\n'
    '1. Describe what changed between the first and last frame\n'
    '2. Identify any notable events and when they occurred (timestamp)\n'
    '3. Summarise the overall activity shown in the video\n'
    '4. Estimate the total duration of the main activity\n'
    'Be specific about timestamps.'
)

# More targeted temporal query:
CHANGE_DETECTION_QUERY = (
    'Compare frame at {start_ts}s and frame at {end_ts}s. '
    'List all visible differences in bullet points. '
    'Categorise each change as: object_moved, object_added, '
    'object_removed, lighting_change, or camera_move.'
)

# Usage:
query = CHANGE_DETECTION_QUERY.format(start_ts=0.0, end_ts=20.0)

if __name__ == '__main__':
    print('Temporal reasoning query:')
    print(TEMPORAL_QUERY)
    print()
    print('Change detection query:')
    print(query)

Fluxo de resumo de vídeo

Um fluxo completo de resumo de vídeo é: extrair quadros → enviá-los ao modelo de visão em lotes → coletar os resumos de cada lote → sintetizar o resumo final. O processamento em lotes evita exceder o limite da janela de contexto.

def summarise_video(
    video_path: str,
    every_n_seconds: float = 10.0,
    batch_size: int = 5
) -> str:
    import anthropic
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')

    # Extract frames
    frames = extract_frames(video_path, '/tmp/video_frames', every_n_seconds)

    # Process in batches
    batch_summaries = []
    for i in range(0, len(frames), batch_size):
        batch = frames[i:i + batch_size]
        ts_range = f'{batch[0]["timestamp"]}s - {batch[-1]["timestamp"]}s'
        query = f'Summarise what happens in this video segment ({ts_range}).'
        summary = describe_video_frames(batch, query)
        batch_summaries.append(f'[{ts_range}] {summary}')

    # Synthesise
    all_summaries = '\n\n'.join(batch_summaries)
    result = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=512,
        messages=[{'role': 'user', 'content':
            f'Here are segment-level video summaries:\n\n{all_summaries}\n\n'
            'Write a single coherent summary of the entire video.'
        }]
    )
    return result.content[0].text

Rastreamento de objetos entre quadros

Peça ao modelo de visão que rastreie objetos específicos ao longo de uma sequência de quadros. Por exemplo: 'Em cada quadro, descreva a posição do carro vermelho: canto superior esquerdo, canto superior direito, centro, canto inferior esquerdo, canto inferior direito.' Isso cria uma trajetória aproximada do movimento sem algoritmos de rastreamento de visão computacional.

def track_object_across_frames(
    frames: list,
    object_description: str
) -> list:
    import anthropic, base64
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    trajectory = []

    for fi in frames:
        with open(fi['path'], 'rb') as f:
            b64 = base64.standard_b64encode(f.read()).decode('utf-8')
        response = client.messages.create(
            model='claude-opus-4-5',
            max_tokens=128,
            messages=[{
                'role': 'user',
                'content': [
                    {'type': 'image', 'source': {'type': 'base64',
                      'media_type': 'image/jpeg', 'data': b64}},
                    {'type': 'text', 'text':
                      f'Where is the {object_description} in this frame? '
                      'Answer with one of: top-left, top-right, centre, '
                      'bottom-left, bottom-right, not-visible.'}
                ]
            }]
        )
        trajectory.append({
            'timestamp': fi['timestamp'],
            'position': response.content[0].text.strip()
        })
    return trajectory

Como lidar com limites de quantidade de quadros

As janelas de contexto da API de visão têm limites para a quantidade de imagens que pode ser enviada por solicitação (normalmente, de 5 a 20 imagens). Para vídeos longos, use uma abordagem hierárquica: analise os segmentos de forma independente e depois combine os resumos dos segmentos em um resumo final usando uma segunda chamada ao LLM.

MAX_FRAMES_PER_REQUEST = 8

def hierarchical_video_analysis(frames: list, final_query: str) -> str:
    import anthropic
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')

    # Level 1: analyse each chunk
    chunk_summaries = []
    for i in range(0, len(frames), MAX_FRAMES_PER_REQUEST):
        chunk = frames[i:i + MAX_FRAMES_PER_REQUEST]
        ts = f'{chunk[0]["timestamp"]}s-{chunk[-1]["timestamp"]}s'
        summary = describe_video_frames(
            chunk, f'What happens in this segment ({ts})?'
        )
        chunk_summaries.append(f'Segment {ts}: {summary}')

    # Level 2: synthesise all chunk summaries
    combined = '\n'.join(chunk_summaries)
    result = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=512,
        messages=[{
            'role': 'user',
            'content': f'Video segments:\n{combined}\n\n{final_query}'
        }]
    )
    return result.content[0].text

Combinação da análise de áudio e vídeo

Para obter a compreensão mais completa, combine a análise visual em nível de quadro com a transcrição de áudio do Whisper. O agente pode correlacionar o que é visto com o que é dito em cada marcação de tempo, possibilitando tarefas avançadas como análise de reuniões ou indexação de tutoriais.

def full_video_analysis(video_path: str) -> dict:
    import subprocess

    # Step 1: Extract audio track
    audio_path = '/tmp/video_audio.mp3'
    subprocess.run([
        'ffmpeg', '-i', video_path, '-q:a', '0', '-map', 'a',
        audio_path, '-y'
    ], capture_output=True)

    # Step 2: Transcribe audio
    transcript_data = transcribe_with_timestamps(audio_path)

    # Step 3: Extract key frames
    frames = extract_frames(video_path, '/tmp/key_frames', every_n_seconds=15.0)

    # Step 4: Visual summary
    visual_summary = hierarchical_video_analysis(
        frames, 'Summarise the visual content.'
    )

    return {
        'transcript': transcript_data['full_text'],
        'transcript_segments': transcript_data['segments'],
        'visual_summary': visual_summary,
        'frame_count': len(frames)
    }

Limpeza e gerenciamento de quadros

Os quadros extraídos podem se acumular rapidamente. Sempre limpe os arquivos temporários de quadros após o processamento e implemente uma memória cache de quadros indexada por um hash de (caminho do vídeo, taxa de quadros) para evitar a reextração durante análises repetidas do mesmo vídeo.

import os
import shutil
import hashlib

FRAME_CACHE_DIR = '/tmp/frame_cache'

def get_cache_key(video_path: str, every_n_seconds: float) -> str:
    content = f'{video_path}:{every_n_seconds}'
    return hashlib.md5(content.encode()).hexdigest()[:12]

def get_or_extract_frames(video_path: str, every_n_seconds: float) -> list:
    key = get_cache_key(video_path, every_n_seconds)
    cache_dir = os.path.join(FRAME_CACHE_DIR, key)

    if os.path.exists(cache_dir):
        print(f'Cache hit: {key}')
        files = sorted(os.listdir(cache_dir))
        return [
            {'path': os.path.join(cache_dir, f),
             'timestamp': float(f.split('_')[-1].replace('s.jpg', ''))}
            for f in files if f.endswith('.jpg')
        ]
    return extract_frames(video_path, cache_dir, every_n_seconds)

def clear_frame_cache():
    if os.path.exists(FRAME_CACHE_DIR):
        shutil.rmtree(FRAME_CACHE_DIR)
        print('Frame cache cleared')

Verificação de conhecimento

Por que a detecção de mudança de cena é preferível à amostragem uniforme de quadros para a análise de vídeo?

Recapitulação: compreensão de vídeo em agentes

Muito bem! Principais aprendizados desta lição:

  • OpenCV: extraia quadros com cap.read(); faça amostragens a cada N segundos ou nas mudanças de cena
  • Sequências de quadros: envie-as em ordem cronológica, com marcações de tempo para fornecer contexto temporal
  • Análise hierárquica: divida em lotes → resumos dos segmentos → síntese final
  • Análise combinada: o ffmpeg extrai o áudio; o Whisper faz a transcrição; faça a comparação com os quadros visuais
  • Memória cache de quadros: evite extrações redundantes com um diretório de cache indexado por hash

Próximo: raciocínio entre modalidades — quando o texto diz uma coisa e a imagem mostra outra.

Grátis para começar

Aprenda AI Agents com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
60
Aulas
239

Perguntas Frequentes

A aula “Compreensão de vídeo em agentes” é grátis?

Sim — o texto completo de “Compreensão de vídeo em agentes” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Agents, atualize para CoddyKit PRO. O curso de AI Agents inclui 4 aulas no total.

O que vou aprender em “Compreensão de vídeo em agentes”?

Extração de quadros, resumo de vídeos e raciocínio temporal sobre clipes. Você pratica AI Agents com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar AI Agents?

Nenhuma experiência prévia é necessária. AI Agents no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Compreensão de vídeo em agentes”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de AI Agents?

Sim. Cada aula de AI Agents inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Agentes de imagem + texto com Claude Vision e GPT-4V
  2. Fluxos de trabalho de agentes de áudio + texto
  3. Compreensão de vídeo em agentes
  4. Padrões de raciocínio entre modalidades
← Voltar para AI Agents