Criando Filtros para Webcam com Streamlit e OpenCV

Quando eu estava aprendendo Visão Computacional na universidade, eu frequentemente tinha que construir aplicações para processar imagens de câmeras em tempo real. Para essas aplicações, eu utilizava o OpenCV para processar as imagens e o HighGUI (a biblioteca de interface gráfica do OpenCV) para exibir os resultados.

A imagem abaixo mostra um exemplo simples de como transmitir um feed de câmera usando OpenCV e HighGUI.

Uma aplicação simples de streaming de webcam usando OpenCV e HighGUI. Uma aplicação simples de streaming de webcam usando OpenCV e HighGUI.

Código...

import cv2

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    cv2.imshow('Camera Stream', frame)
    if cv2.waitKey(1) == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
  


No entanto, o HighGUI não é muito amigável ao usuário além de não ser fácil de personalizar a interface. Como eu decidi aprender Streamlit recentemente, eu achei que seria uma boa ideia tentar construir uma aplicação semelhante as que eu construía na universidade, mas agora combinando Streamlit e OpenCV.

Por padrão, o Streamlit tem apenas st.camera_input, usado para capturar imagens da webcam. No entanto, eu queria construir uma aplicação que pudesse processar e exibir o feed da câmera em tempo real.

Depois de algumas pesquisas, descobri dois componentes feitos pela comunidade do Streamlit que poderiam me ajudar a construir a aplicação que eu queria: streamlit-camera-input-live e streamlit_webrtc.

Durante alguns experimentos, notei que streamlit-camera-input-live era visivelmente mais lento do que streamlit_webrtc. Então, decidi usar streamlit_webrtc para construir a aplicação.

Sem mais delongas, vamos ver o resultado final da aplicação.

Como ficou a aplicação

A aplicação em si é bastante simples. Ela tem um player de vídeo que mostra o feed da câmera e alguns botões para controlar quais filtros são aplicados ao vídeo. A imagem abaixo mostra o resultado final.

O resultado final. O resultado final.

Amostra o código!

Para criar esta aplicação, usei o seguinte arquivo requirements.txt.

streamlit>=1.30
streamlit-webrtc>=0.47
opencv-python>=4.9
numpy>=1.26

Para instalar os módulos em seu ambiente, você pode usar:

pip install -r requirements.txt

Agora, vamos entender como a aplicação foi construída em três passos.

Passo 1. Transmitindo o feed da câmera para o streamlit

Este primeiro trecho mostra como transmitir o feed da câmera para o Streamlit usando streamlit_webrtc. O parâmetro sendback_audio é definido como False porque não precisamos de áudio nesta aplicação. Ao defini-lo como True, você também ouviria o áudio da câmera.

import streamlit as st
from streamlit_webrtc import webrtc_streamer

st.title("OpenCV Filters on Video Stream")

webrtc_streamer(key="streamer", sendback_audio=False)

Para executar este código, você pode usar o seguinte comando no seu terminal (supondo que o código está em um arquivo chamado app.py):

streamlit run app.py

Passo 2. Criando botões para controlar os filtros

Agora podemos adicionar botões para controlar os filtros. Para deixá-los mais apresentáveis, usei a função st.columns para criar uma grade de botões e colocar cada filtro em uma coluna (ou célula).

Neste ponto, os filtros ainda não estão implementados, mas toda vez que você clicar em um botão, a variável filter será atualizada com o nome do filtro que você clicou.

import streamlit as st
from streamlit_webrtc import webrtc_streamer

st.title("OpenCV Filters on Video Stream")

filter = "none"

col1, col2, col3, col4, col5, col6 = st.columns([1, 1, 1, 1, 1, 1])

with col1:
    if st.button("None"):
        filter = "none"
with col2:
    if st.button("Blur"):
        filter = "blur"
with col3:
    if st.button("Grayscale"):
        filter = "grayscale"
with col4:
    if st.button("Sepia"):
        filter = "sepia"
with col5:
    if st.button("Canny"):
        filter = "canny"
with col6:
    if st.button("Invert"):
        filter = "invert"

webrtc_streamer(key="streamer", sendback_audio=False)

Passo 3. Aplicando os filtros

No último passo, precisamos aplicar os filtros ao feed de vídeo. Para fazer isso, usaremos o módulo de visão computacional OpenCV e os módulos auxiliares numpy e av.

A função transform é responsável por aplicar os filtros ao feed de vídeo. Usamos o argumento video_frame_callback para dizer ao webrtc_streamer para usar esta função para processar os quadros de vídeo. Fazendo isso, webrtc_streamer chamará a função transform toda vez que um novo quadro estiver disponível.

A função transform recebe um quadro do feed de vídeo e, com base no filtro selecionado, aplica o filtro correspondente ao quadro. Se você quiser aprender mais sobre os filtros, pode verificar algumas referências abaixo:

O código final ficou assim:

import cv2
import streamlit as st
from streamlit_webrtc import webrtc_streamer, VideoHTMLAttributes
import numpy as np
import av

st.title("OpenCV Filters on Video Stream")

filter = "none"


def transform(frame: av.VideoFrame):
    img = frame.to_ndarray(format="bgr24")

    if filter == "blur":
        img = cv2.GaussianBlur(img, (21, 21), 0)
    elif filter == "canny":
        img = cv2.cvtColor(cv2.Canny(img, 100, 200), cv2.COLOR_GRAY2BGR)
    elif filter == "grayscale":
        # We convert the image twice because the first conversion returns a 2D array.
        # the second conversion turns it back to a 3D array.
        img = cv2.cvtColor(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), cv2.COLOR_GRAY2BGR)
    elif filter == "sepia":
        kernel = np.array(
            [[0.272, 0.534, 0.131], [0.349, 0.686, 0.168], [0.393, 0.769, 0.189]]
        )
        img = cv2.transform(img, kernel)
    elif filter == "invert":
        img = cv2.bitwise_not(img)
    elif filter == "none":
        pass

    return av.VideoFrame.from_ndarray(img, format="bgr24")


col1, col2, col3, col4, col5, col6 = st.columns([1, 1, 1, 1, 1, 1])

with col1:
    if st.button("None"):
        filter = "none"
with col2:
    if st.button("Blur"):
        filter = "blur"
with col3:
    if st.button("Grayscale"):
        filter = "grayscale"
with col4:
    if st.button("Sepia"):
        filter = "sepia"
with col5:
    if st.button("Canny"):
        filter = "canny"
with col6:
    if st.button("Invert"):
        filter = "invert"


webrtc_streamer(
    key="streamer",
    video_frame_callback=transform,
    sendback_audio=False
    )

Conclusão

Neste post, mostrei como construir uma aplicação simples de streaming de webcam usando Streamlit e OpenCV. Espero que você tenha gostado e que possa usar esta aplicação como ponto de partida para construir aplicativos mais complexos.

Se você tiver alguma dúvida ou sugestão, sinta-se à vontade para deixar um comentário abaixo. Até o próximo post! 👋