¿Te imaginas entrar en tu aplicación sin escribir ni una contraseña? El reconocimiento facial dejó de ser cosa de películas: con Windows Forms y la librería Emgu CV (el wrapper de .NET para OpenCV) podemos montar un inicio de sesión por rostro en un par de horas. En esta guía construyo una app que captura tu cara con la webcam, la detecta dentro del cuadro y la guarda como plantilla de usuario para que el login sea por cara y no por teclado.

🤔 ¿Qué vamos a conseguir?
- Una aplicación Windows Forms en C# que abre la webcam y muestra el vídeo en un
PictureBox. - La detección de rostros en tiempo real usando el clasificador Haar Cascade de OpenCV vía Emgu CV.
- Un inicio de sesión por cara: el botón se habilita cuando se detecta un rostro en pantalla.
- Guardar de forma local la plantilla del usuario logueado (su “huella” facial).
🧰 Prerrequisitos
- Windows con Visual Studio 2022+ y la carga de trabajo “Desarrollo de escritorio con .NET”.
- Un portátil o PC con cámara web integrada o USB.
- Librería Emgu CV (mapeo de OpenCV a .NET), que instalaremos con NuGet.
- Conocimientos básicos de C# y de eventos de Windows Forms.
Desde Visual Studio abre la consola de NuGet (o el Package Manager Console) e instala los paquetes:
Install-Package Emgu.CV
Install-Package Emgu.CV.Bitmap
Emgu CV necesita el runtime de OpenCV; los paquetes anteriores lo traen incluido para que no tengas que instalarlo a mano.
🛠️ Crear el proyecto y el formulario
Crea un proyecto nuevo Windows Forms App (.NET) llamado FaceLogin. En el formulario Form1 añade:
- Un
PictureBoxllamadopictureBoxCamarapara mostrar el vídeo. - Un botón
btnGuardarcon el texto “Guardar usuario”. - Un botón
btnLogincon el texto “Iniciar sesión”. - Una etiqueta
labelEstadopara mostrar lo que está pasando.
Ahora conectamos los namespace de Emgu CV al principio del archivo de código:
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
🚀 Capturar la cámara con Emgu CV
La pieza central es el objeto VideoCapture, que lee los fotogramas de la cámara. Arrancamos la cámara al cargar el formulario:
private VideoCapture captura;
private void Form1_Load(object sender, EventArgs e)
{
// 0 = cámara por defecto
captura = new VideoCapture(0);
captura.Set(CapProp.FrameWidth, 640);
captura.Set(CapProp.FrameHeight, 480);
Application.Idle += CapturaNuevoFotograma;
}
private void CapturaNuevoFotograma(object sender, EventArgs e)
{
using (Mat marco = captura.QueryFrame())
{
if (marco != null)
{
pictureBoxCamara.Image = marco.ToBitmap();
}
}
}
Por qué funciona así:
VideoCapture.QueryFrame()devuelve unMat(la estructura de imagen de OpenCV) que luego convertimos aBitmappara poder mostrarlo en elPictureBox. El eventoApplication.Idlerefresca el fotograma cada vez que la interfaz queda libre, sin necesidad de timers.
🚀 Detectar el rostro en tiempo real
Detectar una cara es mucho más fácil de lo que parece gracias a los clasificadores Haar Cascade: un modelo ya entrenado que OpenCV incluye para caras, ojos y sonrisas. Cargamos el clasificador y buscamos el rostro dentro de cada fotograma:
private CascadeClassifier detectorCara;
private void Form1_Load(object sender, EventArgs e)
{
// Archivo de modelo que viene con el paquete de Emgu CV
string rutaCascade = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"haarcascade_frontalface_default.xml");
detectorCara = new CascadeClassifier(rutaCascade);
captura = new VideoCapture(0);
Application.Idle += CapturaNuevoFotograma;
}
private bool rostroDetectado;
private void CapturaNuevoFotograma(object sender, EventArgs e)
{
using (Mat marco = captura.QueryFrame())
{
if (marco == null) return;
// Convertir a escala de grises: el clasificador trabaja mejor así
using (Image<Gray, byte> gris = marco.ToImage<Gray, byte>())
{
Rectangle[] caras = detectorCara.DetectMultiScale(gris, 1.1, 6);
// Dibujar un rectángulo alrededor del rostro
using (Image<Bgr, byte> imagen = marco.ToImage<Bgr, byte>())
{
foreach (Rectangle cara in caras)
{
imagen.Draw(cara, new Bgr(Color.Red), 2);
}
pictureBoxCamara.Image = imagen.ToBitmap();
}
// Habilitamos el login solo si hay al menos una cara visible
rostroDetectado = caras.Length > 0;
btnLogin.Enabled = rostroDetectado;
labelEstado.Text = rostroDetectado
? "🟢 Rostro detectado, ya puedes iniciar sesión"
: "🔴 Colócate frente a la cámara";
}
}
}

🚀 Guardar el rostro del usuario
Cuando pulsas “Guardar”, recortamos la zona del rostro, la convertimos a escala de grises y unificamos el tamaño a 100x100 píxeles. Así cualquier cara, sin importar la distancia a la cámara, se guarda con las mismas dimensiones:
private void btnGuardar_Click(object sender, EventArgs e)
{
if (!rostroDetectado)
{
labelEstado.Text = "No hay ningún rostro que guardar";
return;
}
using (Mat marco = captura.QueryFrame())
using (Mat gris = new Mat())
{
CvInvoke.CvtColor(marco, gris, ColorConversion.Bgr2Gray);
Rectangle[] caras = detectorCara.DetectMultiScale(
new Image<Gray, byte>(gris.Bitmap), 1.1, 6);
if (caras.Length == 0) return;
Rectangle cara = caras[0];
using (Mat recorte = new Mat(gris, cara))
{
CvInvoke.Resize(recorte, recorte, new Size(100, 100));
recorte.Save("usuario.png");
}
}
labelEstado.Text = "✅ Plantilla facial guardada en usuario.png";
}
🚀 Iniciar sesión por rostro
Para el login sacamos la plantilla guardada y la comparamos con el rostro actual. Medimos la distancia con CvInvoke.MatchTemplate; si es suficientemente pequeña, damos por bueno el acceso:
private void btnLogin_Click(object sender, EventArgs e)
{
if (!File.Exists("usuario.png"))
{
labelEstado.Text = "Guarda primero un usuario";
return;
}
using (Mat marco = captura.QueryFrame())
using (Mat plantilla = CvInvoke.Imread("usuario.png", ImreadModes.Grayscale))
using (Mat gris = new Mat())
{
CvInvoke.CvtColor(marco, gris, ColorConversion.Bgr2Gray);
Rectangle[] caras = detectorCara.DetectMultiScale(
new Image<Gray, byte>(gris.Bitmap), 1.1, 6);
if (caras.Length == 0)
{
labelEstado.Text = "No se detecta ningún rostro";
return;
}
using (Mat recorte = new Mat(gris, caras[0]))
{
CvInvoke.Resize(recorte, recorte, new Size(100, 100));
CvInvoke.Resize(plantilla, plantilla, new Size(100, 100));
using (Mat resultado = new Mat())
{
CvInvoke.MatchTemplate(recorte, plantilla, resultado,
TemplateMatchingType.CcoeffNormed);
double[] minValores, maxValores;
Point[] minUbicaciones, maxUbicaciones;
resultado.MinMax(out minValores, out maxValores,
out minUbicaciones, out maxUbicaciones);
bool acceso = maxValores[0] > 0.6;
labelEstado.Text = acceso
? "✅ Bienvenido, sesión iniciada"
: "❌ No coincide con el usuario guardado";
}
}
}
}
🧪 Comprobar que funciona
- Al arrancar el programa ves el vídeo de la cámara en el
PictureBoxcon un rectángulo rojo rodeando tu cara. - El botón Iniciar sesión se habilita solo cuando hay un rostro delante.
- Pulsas Guardar y verás
usuario.pngen la carpeta de compilación (bin/Debug), de 100x100 píxeles. - Pulsas Iniciar sesión delante de la cámara y la etiqueta cambia a ”✅ Bienvenido, sesión iniciada”.
⚠️ Errores comunes
| Error | Causa | Solución |
|---|---|---|
DllNotFoundException al arrancar | Faltan las DLL de OpenCV de Emgu CV | Instala Emgu.CV.runtime.windows por NuGet y comprueba que bin contiene opencv_*.dll |
haarcascade_frontalface_default.xml no encontrado | El archivo del modelo no se copia a la salida | Marca el XML como Content y Copy to Output Directory = Copy always |
| La cámara lanza una excepción | El dispositivo 0 ya está en uso por otra app | Cierra zoom/Teams y usa new VideoCapture(0) o new VideoCapture(1) para otra cámara |
QueryFrame() devuelve null | Cámara sin permiso o mal conectada | Comprueba permisos de cámara de Windows y que la webcam funciona en otra app |
| El login no reconoce nunca mi cara | La plantilla se guardó con otra iluminación | Regraba el usuario con buena luz frontal y sin gafas |
MatchTemplate no existe | Versión antigua de Emgu CV | Usa Emgu.CV 4.x y TemplateMatchingType.CcoeffNormed |
🪜 Siguiente nivel
Esta demo jecha las bases del reconocimiento facial en C#. La escalera natural para llevarlo más allá:
- Verificación real de identidad — en vez de
MatchTemplate(que solo sirve para un usuario), usa el reconocedor EigenFace o LBPH de OpenCV que guarda muchas muestras por usuario. Ver OpenCV — Face Recognition. - Persistencia con base de datos — guarda las plantillas en SQLite para tener varios usuarios y no un único
usuario.png. - Detectar ojos y sonrisa con los cascades
haarcascade_eye.xmlyhaarcascade_smile.xmlpara refinar la captura. - Si te mola el mundo .NET, repasa la base del lenguaje con la Guía de C# y .NET: de 0 a 100 o empieza por el Arranque rápido de C#.
