Para implementar la funcionalidad de captura de imágenes en una aplicación WPF, se puede emplear la biblioteca AForge.Video.DirectShow. Esta opción resulta adecuada para la adquisición directa desde dispositivos de video, a diferencia de otras librerías que se limitan a capturar contenido de pantalla.
El procedimiento requieer inocrporar las siguientes referencias en el proyecto:
- DLLs de AForge.Controls y AForge.Video.DirectShow
- System.Drawing
- System.Windows.Forms
- WindowsFormsIntegration
En el archivo de marcado XAML, es necesario definir los espacios de nombres para la integración con Windows Forms y los controles de AForge:
<Window x:Class="CameraApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
xmlns:aforge="clr-namespace:AForge.Controls;assembly=AForge.Controls"
Title="Captura de Video" Height="600" Width="900">
<Grid>
<wfi:WindowsFormsHost Width="500" Height="350" HorizontalAlignment="Left" VerticalAlignment="Top">
<aforge:VideoSourcePlayer x:Name="videoPreview" Width="640" Height="480"/>
</wfi:WindowsFormsHost>
<Button Content="Iniciar Cámara" HorizontalAlignment="Left" Margin="50,380,0,0" VerticalAlignment="Top" Width="120" Click="OnStartCamera"/>
<Button Content="Capturar Imagen" HorizontalAlignment="Left" Margin="190,380,0,0" VerticalAlignment="Top" Width="120" Click="OnCaptureImage"/>
<Button Content="Detener" HorizontalAlignment="Left" Margin="330,380,0,0" VerticalAlignment="Top" Width="120" Click="OnStopCamera"/>
<Image x:Name="previewImage" HorizontalAlignment="Left" Margin="570,50,0,0" VerticalAlignment="Top" Width="250" Height="200"/>
</Grid>
</Window>
La lógica de manejo de cámara se encapsula en una clase separada que gestiona el dipsositivo de video:
using AForge.Video.DirectShow;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Media.Imaging;
namespace CameraApp
{
public class CameraManager
{
private FilterInfoCollection _dispositivosVideo;
private VideoCaptureDevice _fuenteVideo;
public void DetectarDispositivos()
{
_dispositivosVideo = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (_dispositivosVideo.Count > 0)
{
Console.WriteLine($"Dispositivos encontrados: {_dispositivosVideo.Count}");
}
}
public void ConectarCamara(VideoSourcePlayer reproductor, int indiceDispositivo = 0)
{
if (_dispositivosVideo == null || _dispositivosVideo.Count == 0)
{
throw new InvalidOperationException("No se detectaron cámaras disponibles");
}
_fuenteVideo = new VideoCaptureDevice(
_dispositivosVideo[indiceDispositivo].MonikerString);
reproductor.VideoSource = _fuenteVideo;
_fuenteVideo.Start();
reproductor.Start();
}
public string CapturarImagen(VideoSourcePlayer reproductor)
{
if (reproductor.VideoSource == null) return null;
try
{
using (Image fotograma = reproductor.GetCurrentVideoFrame())
{
string nombreArchivo = $"captura_{DateTime.Now:yyyyMMdd_HHmmss}.jpg";
fotograma.Save(nombreArchivo, ImageFormat.Jpeg);
return nombreArchivo;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error en captura: {ex.Message}");
return null;
}
}
public void Desconectar(VideoSourcePlayer reproductor)
{
if (_fuenteVideo != null && _fuenteVideo.IsRunning)
{
reproductor.Stop();
_fuenteVideo.SignalToStop();
_fuenteVideo.WaitForStop();
_fuenteVideo = null;
}
}
public BitmapImage ConvertirBitmap(string rutaArchivo)
{
using (var flujo = new MemoryStream(File.ReadAllBytes(rutaArchivo)))
{
var imagen = new BitmapImage();
imagen.BeginInit();
imagen.CacheOption = BitmapCacheOption.OnLoad;
imagen.StreamSource = flujo;
imagen.EndInit();
imagen.Freeze();
return imagen;
}
}
}
}
El código de la ventana principal coordina la interacción del usuario con los controles y el administrador de cámara:
using System.Windows;
using System.Windows.Media.Imaging;
namespace CameraApp
{
public partial class MainWindow : Window
{
private readonly CameraManager _gestorCamara = new CameraManager();
public MainWindow()
{
InitializeComponent();
_gestorCamara.DetectarDispositivos();
}
private void OnStartCamera(object sender, RoutedEventArgs e)
{
try
{
_gestorCamara.ConectarCamara(videoPreview);
}
catch (System.Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void OnCaptureImage(object sender, RoutedEventArgs e)
{
string archivo = _gestorCamara.CapturarImagen(videoPreview);
if (archivo != null)
{
BitmapImage imagenPreview = _gestorCamara.ConvertirBitmap(archivo);
previewImage.Source = imagenPreview;
}
}
private void OnStopCamera(object sender, RoutedEventArgs e)
{
_gestorCamara.Desconectar(videoPreview);
}
protected override void OnClosed(EventArgs e)
{
_gestorCamara.Desconectar(videoPreview);
base.OnClosed(e);
}
}
}
Este enfoque utiliza flujos de memoria para evitar bloqueos de archivos y genera nombres únicos para cada captura. La integración con WPF se realiza mediante el control WindowsFormsHost que aloja el reproductor de video de AForge.