Blog

Minha câmera virtual com blur e auto-framing: um NVIDIA Broadcast só para a câmera

Eu usava o NVIDIA Broadcast so para uma coisa: desfocar o fundo da minha webcam. So que ele insistia em ser muito mais do que eu pedia. Ativava efeito de ruido no microfone e nos alto-falantes quando eu so queria mexer na camera, e ao ligar o PC abria a janela cheia na minha cara em vez de iniciar quieto na bandeja. Decidi resolver isso construindo minha propria ferramenta: um app de desktop que aplica blur de fundo e auto-framing apenas na camera, expoe o resultado como uma webcam virtual para qualquer programa de chamada e inicia minimizado. Este artigo conta a decisao de arquitetura, o porque de cada escolha tecnica e como o app foi montado e validado.

2026-06-22 / Visão Computacional / 12 min

01

O problema com o NVIDIA Broadcast

O NVIDIA Broadcast e uma otima suite, mas para o meu uso ele resolvia demais. Tres incomodos concretos me empurraram para construir algo proprio.

  • Escopo amplo demais: eu so queria blur na camera, e o app girava em torno de tres dispositivos (camera, microfone e alto-falantes). Cada vez que eu mexia numa coisa, tinha o risco de ativar efeito onde eu nao queria.
  • Abria maximizado no boot: ao ligar o PC, a janela do Broadcast aparecia inteira na tela, em vez de iniciar minimizada na bandeja. Todo dia eu fechava na mao.
  • Peso e acoplamento: uma suite grande para uma funcao pequena. Eu queria uma ferramenta enxuta que fizesse so o blur e o enquadramento, do meu jeito.

A inspiracao veio de um projeto web open source do Takashi Yoshinaga que faz blur de fundo e auto-framing com MediaPipe rodando no navegador. A ideia era exatamente o que eu queria, mas em pagina web nao da para virar webcam dos outros apps. Eu precisava de um aplicativo instalavel no PC que expusesse uma camera virtual.

02

Escolhendo a linguagem e a arquitetura

O requisito que define tudo nao e o blur em si: e fazer o video processado aparecer como uma webcam selecionavel no Zoom, Meet, Discord e OBS. Isso exige registrar uma camera virtual no Windows. Avaliei tres caminhos.

AbordagemProsContras
Electron + JS (igual ao repo original)Reaproveita o MediaPipe.js do projeto web; UI facilCriar camera virtual real no Windows a partir do Electron e instavel; bom so para janela de preview
C# / .NET nativoApp nativo robusto, instalador limpo, camera virtual via DirectShowMediaPipe em C# e menos direto; desenvolvimento bem mais longo
Python + pyvirtualcamMediaPipe oficial, OpenCV maduro, camera virtual pronta via backend do OBS, vira .exe com PyInstallerDepende do OBS instalado uma vez para registrar o driver da camera virtual

Escolhi Python. O MediaPipe tem suporte oficial em Python, o OpenCV resolve captura e processamento de imagem com folga, e a biblioteca pyvirtualcam entrega a camera virtual reaproveitando o driver que o OBS Studio ja instala. E o melhor custo-beneficio: chego rapido num app funcional e ainda empacoto tudo num executavel.

03

Como o blur de fundo funciona

O blur depende de separar a pessoa do fundo. Para isso uso o Image Segmenter do MediaPipe com o modelo de selfie segmentation, que devolve, para cada pixel, a confianca de pertencer a pessoa. Com essa mascara, componho a pessoa nitida sobre uma copia desfocada do mesmo frame.

import cv2
import numpy as np
import mediapipe as mp
from mediapipe.tasks import python as mp_python
from mediapipe.tasks.python import vision

options = vision.ImageSegmenterOptions(
    base_options=mp_python.BaseOptions(model_asset_path="selfie_segmenter.tflite"),
    running_mode=vision.RunningMode.VIDEO,
    output_confidence_masks=True,
)
segmenter = vision.ImageSegmenter.create_from_options(options)

def blur_background(frame_bgr, ts_ms, strength=25, threshold=0.5):
    rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
    image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb)
    result = segmenter.segment_for_video(image, ts_ms)

    confidence = result.confidence_masks[0].numpy_view()  # [0..1] por pixel
    mask = (confidence >= threshold).astype(np.float32)
    mask = cv2.GaussianBlur(mask, (7, 7), 0)              # suaviza a borda

    k = strength | 1                                      # kernel impar
    blurred = cv2.GaussianBlur(frame_bgr, (k, k), 0)
    alpha = mask[:, :, None]
    out = frame_bgr * alpha + blurred * (1.0 - alpha)     # composicao alpha
    return out.astype(np.uint8)

Dois cuidados fazem diferenca na qualidade: binarizar a mascara num limiar e depois suavizar a borda com um leve blur gaussiano, para a transicao entre pessoa e fundo nao ficar serrilhada; e usar o modo VIDEO do segmenter passando o timestamp de cada frame, o que da rastreamento temporal e estabiliza o resultado quadro a quadro.

04

Como o auto-framing funciona

O auto-framing mantem seu rosto sempre bem enquadrado, como se a camera te seguisse. Detecto o rosto com o Face Detector do MediaPipe (BlazeFace), calculo um retangulo de corte centrado nele com um zoom configuravel e redimensiono esse corte de volta ao tamanho de saida. O segredo para nao tremer e suavizar o movimento com media exponencial, a mesma ideia do projeto original.

def smooth(prev, target, factor=0.9):
    # factor alto = movimento mais suave e mais lento a reagir.
    if prev is None:
        return target
    return tuple(factor * p + (1 - factor) * t for p, t in zip(prev, target))

def target_crop(detection, w, h, zoom=1.4):
    box = detection.bounding_box
    cx = box.origin_x + box.width / 2
    cy = box.origin_y + box.height / 2
    return (cx, cy, w / zoom, h / zoom)  # centro no rosto, area reduzida pelo zoom

A cada frame eu calculo o corte alvo a partir do rosto, misturo com o corte anterior pela media exponencial e fixo o resultado dentro dos limites da imagem. Quando nenhum rosto e detectado, o enquadramento relaxa devagar de volta para o frame cheio, em vez de saltar.

05

A camera virtual: a peca central

De nada adianta processar o video se ele nao aparece como webcam nos outros programas. Esse e o papel do pyvirtualcam, que no Windows usa o driver da camera virtual do OBS Studio. Por isso o OBS precisa estar instalado uma vez (nem precisa abrir): ele registra o driver que o app reutiliza. O loop e simples: capturo da camera fisica, aplico os efeitos e envio o frame para a camera virtual.

import pyvirtualcam
from pyvirtualcam import PixelFormat

cap = cv2.VideoCapture(camera_index, cv2.CAP_DSHOW)
with pyvirtualcam.Camera(width=1280, height=720, fps=30, fmt=PixelFormat.BGR) as cam:
    while running:
        ok, frame = cap.read()
        if not ok:
            break
        frame = auto_framing(frame, ts)     # 1) enquadra no rosto
        frame = blur_background(frame, ts)  # 2) desfoca o fundo
        cam.send(frame)
        cam.sleep_until_next_frame()
camera fisica
   -> OpenCV (captura)
      -> auto-framing  (FaceDetector + corte suavizado)
         -> blur de fundo (ImageSegmenter + composicao alpha)
            -> pyvirtualcam (OBS Virtual Camera)
               -> Zoom / Meet / Discord / OBS

06

Uma armadilha real: capturar a propria camera virtual

No teste de ponta a ponta apareceu um problema que vale registrar. Ao listar as cameras pelo OpenCV, a OBS Virtual Camera tambem aparece como um dispositivo de entrada. Se o app captura ela por engano, voce acaba enquadrando a tela de standby do OBS em vez do seu rosto, ou cria um loop de video. A correcao foi enumerar as cameras pelo nome (via DirectShow) e filtrar qualquer dispositivo cujo nome contenha "OBS Virtual", deixando so as cameras fisicas na lista de entrada.

from pygrabber.dshow_graph import FilterGraph

VIRTUAL_HINTS = ("obs virtual", "obs-camera", "obs cam")

def list_cameras():
    devices = FilterGraph().get_input_devices()  # nomes na ordem de indice do OpenCV
    return [
        (i, name)
        for i, name in enumerate(devices)
        if not any(h in name.lower() for h in VIRTUAL_HINTS)
    ]

Para confirmar que o efeito realmente acontece, alem da inspecao visual eu medi a variancia do Laplaciano (um indicador de nitidez) no centro do frame, onde fica a pessoa, e num canto, onde fica o fundo. O centro deu cerca de 680 e o canto perto de zero, ou seja, fundo desfocado e pessoa preservada nitida, exatamente o esperado.

07

Resolvendo as queixas: bandeja e inicio minimizado

Os dois incomodos que me motivaram viraram requisitos explicitos. Primeiro, escopo: o app mexe so na camera, ponto. Nao toca em microfone nem em alto-falantes, porque simplesmente nao lida com audio. Segundo, comportamento de inicializacao: um icone na bandeja do sistema (com pystray) permite rodar minimizado, pausar e retomar; e o inicio com o Windows registra o app no boot ja com a flag de iniciar minimizado, o oposto do Broadcast abrindo maximizado.

import winreg, sys

RUN_KEY = r"Software\Microsoft\Windows\CurrentVersion\Run"

def set_autostart(enabled):
    with winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_KEY, 0, winreg.KEY_SET_VALUE) as key:
        if enabled:
            cmd = f'"{sys.executable}" --minimized'  # abre direto na bandeja
            winreg.SetValueEx(key, "CamFX", 0, winreg.REG_SZ, cmd)
        else:
            winreg.DeleteValue(key, "CamFX")

08

Empacotando como executavel

Para virar algo instalavel, gerei um executavel unico com o PyInstaller. O detalhe importante e empacotar junto os modelos .tflite do MediaPipe e os assets internos da biblioteca, para o app rodar sem depender de download na primeira vez. Os modelos sao baixados automaticamente na primeira execucao a partir do fonte e ficam em cache na pasta do usuario; no executavel, vao embutidos.

pyinstaller --onefile --windowed --name CamFX \
  --collect-all mediapipe \
  --add-data "selfie_segmenter.tflite;models" \
  --add-data "blaze_face_short_range.tflite;models" \
  main.py

Resultado: um CamFX.exe que abre, deixa escolher a camera, ligar blur e auto-framing com controles de intensidade, e manda o video para a camera virtual. No Zoom ou no Meet basta selecionar a OBS Virtual Camera como webcam. Fazendo so o que eu precisava, do jeito que eu queria.

FAQ

Perguntas frequentes

Por que precisa do OBS instalado?

O pyvirtualcam, no Windows, usa o driver da camera virtual que o OBS Studio registra ao ser instalado. Voce nao precisa abrir o OBS, apenas instala-lo uma vez para que o driver exista. A partir dai o CamFX cria a camera virtual sozinho e o video processado aparece como a webcam OBS Virtual Camera nos outros aplicativos.

Da para rodar sem placa NVIDIA?

Sim. Diferente do NVIDIA Broadcast, que exige GPU NVIDIA RTX, o CamFX roda o MediaPipe na CPU por padrao. Nos meus testes, blur e auto-framing juntos em 720p rodaram em torno de 13 FPS na CPU, suficiente para chamadas; em maquinas mais fortes ou reduzindo a resolucao, o numero sobe.

Por que o efeito so na camera e nao no audio?

Por escolha de escopo. O incomodo que me levou a construir a ferramenta era justamente o efeito vazar para microfone e alto-falantes quando eu so queria mexer na camera. O CamFX nao lida com audio de proposito: ele captura video, processa e expoe uma camera virtual, e nada mais.

Uma ferramenta que faz so o que voce precisa

Construir o proprio substituto do NVIDIA Broadcast me deu exatamente o que eu queria: blur e auto-framing so na camera, inicio minimizado na bandeja e nenhum efeito indesejado no audio. Se voce tem um incomodo parecido com uma ferramenta grande demais para a sua necessidade, posso ajudar a desenhar e construir a solucao enxuta certa.