8 03 2017
Acessando a webcam no .NET com a biblioteca AForge
Uma das possibilidades que temos à nossa disposição para fazermos o acesso à webcam no .NET é através da biblioteca AForge. Essa biblioteca é muito fácil e intuitiva, muito simples de utilizar. Uns tempos atrás eu escrevi um artigo onde eu mostrei como tirar fotos com a webcam no C#, onde eu utilizei tanto a biblioteca AForge quanto a biblioteca DirectShow.NET. Como esse é um dos artigos mais populares do meu site, eu resolvi gravar um vídeo expandindo essa ideia, focando somente na biblioteca AForge, que é a mais utilizada:
Instalação da biblioteca AForge pelo NuGet
No artigo que eu escrevi tempos atrás, eu só mostrei como podemos baixar a biblioteca manualmente no seu site. Hoje em dia a utilização do NuGet se tornou praticamente padrão nos projetos .NET, por isso, faz todo o sentido partirmos para essa estratégia.
Para adicionarmos a referência à biblioteca AForge pelo NuGet, temos que primeiramente procurar por “AForge“. Uma vez listadas as opções, instale o item “AForge.Video.DirectShow“:
Outra opção é utilizarmos o Package Manager Console, executando o comando “Install-Package AForge.Video.DirectShow“.
Namespace
Todas as classes relacionadas à webcam no AForge estão localizadas no namespace “AForge.Video.DirectShow“, dessa forma, para facilitar as coisas, eu sugiro que você adicione uma referência a esse namespace utilizando a cláusula “using AForge.Video.DirectShow” no topo do seu formulário.
Listando as webcams
A biblioteca AForge serve para trabalharmos com diversas coisas no .NET (como áudio e vídeo). O acesso à webcam é somente uma das possibilidades que temos à nossa disposição. Para listarmos as webcams, utilizamos a classe “FilterInfoCollection“, passando “FilterCategory.VideoInputDevice” como parâmetro no construtor.
Instanciando uma webcam
No AForge, a webcam pode ser manipulada através da classe “VideoCaptureDevice“. O construtor dessa classe espera um “moniker string“, que seria algo como um “id” do dispositivo de captura. Essa informação pode ser recuperada através dos objetos retornados pelo “FilterInfoCollection” mencionado logo acima.
O evento NewFrame
A classe “VideoCaptrureDevice” possui um evento chamado “NewFrame“. Estando a câmera ligada, esse evento será disparado cada vez que um novo frame for capturado pela webcam. Isso quer dizer que, caso o frame rate da câmera seja, por exemplo, 40fps, esse evento será disparado 40 vezes por segundo.
Nos argumentos desse evento, temos acesso ao frame capturado (que é uma imagem). Com o frame capturado, podemos cloná-lo para, por exemplo, exibirmos em um controle do tipo PictureBox.
Evitando consumo desnecessário de memória
Uma vez que estamos clonando as imagens retornadas pelo evento “NewFrame“, o consumo de memória pode ficar rapidamente muito alto. O .NET só descartará as imagens que não estão sendo mais utilizadas quando ele perceber que nós não precisamos mais delas. Isso pode demorar 10, 15, até 30 segundos dependendo da situação, o que pode potencialmente levar a um estouro de memória.
Para contornarmos esse problema, o ideal é chamarmos um “Dispose” na imagem atual do PictureBox antes de atualizarmos com a imagem nova. Isso é uma maneira de dizermos para o .NET que não precisamos mais daquela imagem velha, fazendo com que a sua memória seja recuperada quase que imediatamente.
Ligando e desligando a câmera
Para sabermos se a câmera deve ser ligada ou desligada, utilizamos a propriedade “IsRunning“, que retornará verdadeiro caso a câmera já esteja ligada ou falso caso contrário. A câmera pode ser ligada através do método “Start” ou desligada através do método “Stop“. É importante que nós lembremos de limpar a imagem do PictureBox quando desligarmos a câmera, caso contrário ela ficará com o último frame capturado antes do desligamento.
Capturando a imagem da webcam
Estando a câmera ligada, nós podemos salvar o frame atual através do método “Save” da imagem que está sendo exibida no PictureBox. Nós podemos utilizar um caminho fixo para a imagem ou, melhor ainda, nós podemos utilizar um “SaveFileDialog” para perguntar para o usuário onde é que ele quer salvar a imagem.
Um problema muito interessante que vamos encontrar ao utilizarmos a classe “SaveFileDialog” é que o PictureBox continuará sendo atualizado com a imagem da webcam enquanto o usuário escolhe o caminho onde ele quer salvar a imagem. O resultado disso é que a imagem que será salva não necessariamente será a mesma imagem de quando o usuário clicou no botão “Capturar“.
Esse problema pode ser resolvido se pausarmos a captura enquanto o usuário escolhe o caminho, restaurando a captura logo após o confirmação ou cancelamento do diálogo. Isso pode ser feito removendo o “hook” do evento “NewFrame” e depois adicionando novamente, tudo isso dentro de um bloco try-finally para evitarmos inconsistências caso uma exception seja lançada no meio do caminho.
Desligue a câmera antes de fechar o formulário!
Em algumas situações (não muito raras) a biblioteca AForge disparará uma exception caso o formulário (ou aplicativo) seja fechado com a câmera ligada. Para evitar esse problema, temos que fazer um “override” do método “OnFormClosing“, onde verificaremos se a câmera está ligada e, caso positivo, nós desligamos a câmera antes que o formulário seja efetivamente fechado.
O código completo
Aqui vai o código completo do exemplo demonstrado nesse vídeo:
// C# private VideoCaptureDevice videoSource; public FormCaptura() { InitializeComponent(); var videoSources = new FilterInfoCollection(FilterCategory.VideoInputDevice); if (videoSources != null && videoSources.Count > 0) { videoSource = new VideoCaptureDevice(videoSources[0].MonikerString); videoSource.NewFrame += VideoSource_NewFrame; } } private void VideoSource_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs) { if (pbWebcam.Image != null) pbWebcam.Image.Dispose(); pbWebcam.Image = (Bitmap)eventArgs.Frame.Clone(); } private void btLigarDesligar_Click(object sender, EventArgs e) { if (videoSource.IsRunning) { videoSource.Stop(); pbWebcam.Image = null; } else { videoSource.Start(); } } private void btCapturar_Click(object sender, EventArgs e) { if (pbWebcam.Image != null) { try { videoSource.NewFrame -= VideoSource_NewFrame; using (var dialog = new SaveFileDialog()) { dialog.DefaultExt = "png"; dialog.AddExtension = true; if (dialog.ShowDialog() == DialogResult.OK) { pbWebcam.Image.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Png); } } } finally { videoSource.NewFrame += VideoSource_NewFrame; } } } protected override void OnFormClosing(FormClosingEventArgs e) { if (videoSource.IsRunning) { videoSource.Stop(); } base.OnFormClosing(e); }
' VB.NET Private VideoSource As VideoCaptureDevice Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Dim VideoSources = New FilterInfoCollection(FilterCategory.VideoInputDevice) If VideoSources IsNot Nothing AndAlso VideoSources.Count > 0 Then VideoSource = New VideoCaptureDevice(VideoSources(0).MonikerString) AddHandler VideoSource.NewFrame, AddressOf VideoSource_NewFrame End If End Sub Private Sub VideoSource_NewFrame(sender As Object, eventArgs As AForge.Video.NewFrameEventArgs) If PbWebcam.Image IsNot Nothing Then PbWebcam.Image.Dispose() End If PbWebcam.Image = DirectCast(eventArgs.Frame.Clone(), Bitmap) End Sub Private Sub BtLigarDesligar_Click(sender As Object, e As EventArgs) Handles BtLigarDesligar.Click If VideoSource.IsRunning Then VideoSource.Stop() PbWebcam.Image = Nothing Else VideoSource.Start() End If End Sub Private Sub BtCapturar_Click(sender As Object, e As EventArgs) Handles BtCapturar.Click If PbWebcam.Image IsNot Nothing Then Try RemoveHandler VideoSource.NewFrame, AddressOf VideoSource_NewFrame Using dialog = New SaveFileDialog() dialog.DefaultExt = "png" dialog.AddExtension = True If dialog.ShowDialog() = DialogResult.OK Then PbWebcam.Image.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Png) End If End Using Finally AddHandler VideoSource.NewFrame, AddressOf VideoSource_NewFrame End Try End If End Sub Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs) If VideoSource.IsRunning Then VideoSource.[Stop]() End If MyBase.OnFormClosing(e) End Sub
Baixe o projeto de exemplo
Para baixar o projeto de exemplo desse artigo, assine a minha newsletter. Ao fazer isso, além de ter acesso ao projeto, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário no final do artigo.
Até a próxima!
André Lima
Photo by Peter Shanks used under Creative Commons
https://pixabay.com/en/startup-start-up-notebooks-creative-593327/
Song Rocket Power Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/
Falei algumas besteiras sobre o PostgreSQL, veja aqui as correções Como utilizar o Report Viewer no Visual Studio 2017?
Parabéns pelo vídeo, sou seu fã…
Uma dúvida que não está relacionada ao tema do post mas me surgiu aq:
Para o evento de fechamento do form eu vou no designer na janela Events e dou clique duplo em FormClosing que gera o seguinte código:
private void nomeDoForm_FormClosing(object sender, FormClosingEventArgs e) {}
vc usou:
protected override void OnFormClosing(FormClosingEventArgs e) {}
Qual a diferença entre os dois?
Olá Tony!
Basicamente eles fazem a mesma coisa.. O método OnFormClosing do Form faz as chamadas dos hooks que você tiver para o evento FormClosing.. Em artigos eu normalmente faço com o override porque aí o leitor pode simplesmente copiar e colar o código para dentro do formulário e tudo funcionará normalmente, enquanto que com o evento eu teria que instruir o leitor a criar o hook para o evento, sacou?
Já no vídeo eu não sei ao certo o motivo de eu ter feito com o override.. Talvez foi porque eu já estava ali no editor, aí fica mais rápido fazer com o override.. Se eu fosse fazer com o hook do evento eu teria que primeiro ir para o design do formulário, criar o hook pro evento, etc etc..
Mas, resumindo: basicamente não tem diferença.. O base.OnFormClosing vai disparar todos os hooks que tiverem sido feitos para o evento FormClosing..
Abraço!
André Lima
Oi André, top o seu artigo, com certeza será utilizado em um trabalho que estou desenvolvendo. O video excelente também. Parabéns!
Valeu Oduvaldo! Fico feliz que você tenha gostado! :)
Um grande abraço!
André Lima
Show. Estava precisando disso. Valeu.
Muito obrigado, José! Valeu pelo comentário.. Fico feliz por ter conseguido ajudar de alguma forma..
Abraço!
André Lima
André, bom dia!
Muito bom o seu artigo, me ajudou bastante. Estou implementando um software para a faculdade e estou utilizando essa biblioteca. É a primeira vez que implemento algo em C#. Eu consegui fazer a captura perfeitamente, porém estou com um problema quando preciso parar a câmera, a mesma para, porem se eu na mesma hora clicar em iniciar ela não volta..rsrs mas se eu esperar uns 5 segundos e clicar, ela volta. Eu estou utilizando uma câmera Sentech STC-MC83USB. Com isso, gostaria de saber, com a experiência que você tem com a biblioteca AForge.NET, se você já viu isso acontecer antes? Se passou por algum problema parecido, pois estou a dias tentando arrumar este problema e não acho solução.
Desde já agradeço a ajuda.
Abraço!
Olá Felipe, muito obrigado pelo comentário!
Estranho hein.. Acabei de testar aqui o projeto de exemplo (utilizando exatamente o mesmo código que apresentei no artigo) e, com a minha webcam, os comandos são acumulados.. Ou seja, se eu clico para desligar a câmera e logo em seguida clico novamente para ligar, ela demora um pouquinho (uns dois segundos), mas como o comando foi acumulado, a webcam é ligada novamente.. Se eu clicar inúmeras vezes no botão liga/desliga, todas as chamadas são acumuladas e a câmera liga e desliga várias vezes..
Você utilizou exatamente o mesmo código do artigo ou você alterou alguma coisa? Você já tentou testar com outra câmera em outro computador?
Abraço!
André Lima
Boa tarde André!
Parabéns pelo post!
Funcionou perfeitamente aqui. Queria saber agora como gravar o vídeo da webcam e salvar ele. Tipo: ir gravando e a cada 5 minutos salvar em um arquivo e continuar a gravação, até que clique no botão desligar.
Olá Michael, muito obrigado pelo comentário!
Na verdade essa biblioteca captura imagens da webcam, e não vídeo.. Você está querendo capturar uma sequência de imagens para montar um vídeo? É isso que você está querendo? Poderia explicar um pouco mais detalhadamente?
Abraço!
André Lima
Olá André!
Na verdade esse é um projeto pessoal (para usar em casa, rsrs), uma coisa bem simples. É o seguinte, quero usar minha(s) Webcams para monitorar lá me casa, gravando o vídeo em uma pasta especifica e esta pasta sincronizaria com algum serviço da nuvem (gdrive, mega, etc). Esta questão da sincronização, nem me preocupo muito, seria mais fácil instalar um dos aplicativos e apontar para a pasta onde o sistema salva os videos.
Olá novamente, Michael!
OK, estou começando a entender.. A única coisa que não está 100% clara para mim é se você realmente quer que o resultado seja um vídeo ou apenas uma sequência de imagens (gravadas de 5 em 5 minutos, como você mencionou anteriormente)..
Abraço!
André Lima
Seria um vídeo.
Olá Michael!
Para gravar vídeo você terá que fazer alguns ajustes no código.. Veja se esta thread no StackOverflow te ajuda:
C# AForge.NET – how to save video from camera to file
Se não conseguir, me avisa que eu tento fazer um exemplo.. Esse tópico (gravação de vídeo com a webcam) já está na minha lista também..
Abraço!
André Lima
Oi professor boa noite adorei este post mais lhe quero fazer uma pergunta
Eu gostaria de salvar esta imagem em vários tamanhos o seja eu teria um combobox que teria no mínimo quatro tipos de tamanhos e que eu escolhesse, o programa salvaria a imagem no pixel selecionado pelo combobox e possível.
Se for me dei uma luz obrigado
Boa noite
Olá Deuzivaldo, obrigado pelo comentário!
Quanto à sua dúvida, você só precisa pegar o tamanho escolhido pelo usuário no ComboBox e redimensionar a imagem conforme necessário.. Você consegue encontrar pela internet diversos métodos para redimensionar imagens no .NET.. Por exemplo:
Resize an Image C#
C# Image resizing to different size while preserving aspect ratio
Qualquer dificuldade na utilização desses métodos, é só entrar em contato..
Abraço!
André Lima
o brigado professor
De nada, Deuzivaldo! Qualquer coisa estamos aí..
Abraço!
André Lima