¡Usa una cámara Raspberry Pi y una cerradura de solenoide para crear una cerradura de puerta que se desbloquee cuando reconozca tu rostro!

Materiales.

  • Raspberry Pi
  • Cerradura solenoide
  • Fuente de alimentacion de CC externa
  • Módulo de relé
  • Cámara Raspberry Pi
  • Cables de puente

El proyecto constará de tres fases:

  1. Detección de rostros y recolección de datos
  2. Reconocidor de entrenamiento
  3. Reconocimiento facial

Antes de sumergirse en el código, conectemos el bloqueo del solenoide con la Raspberry Pi.

Diagrama de circuito y explicación

Los pines GPIO de la Raspberry Pi pueden dar una salida de 3.3V, pero el bloqueo del solenoide requiere 7-12V para funcionar. Debido a esto, necesitaremos usar una fuente de alimentación externa y un relé para operar la cerradura.

Conecte el VCC y GND del módulo de relé a 5V y GND de Raspberry Pi. Luego, conecte el pin de señal del módulo de relé al GPIO 26 de Raspberry Pi.

En el otro lado del módulo de relé, conecte la fuente de alimentación de CC de forma negativa al negativo del bloqueo de la puerta del solenoide. Conecte el positivo de la fuente de alimentación de CC al común del módulo de relé y luego conecte normalmente abierto desde el módulo de relé al positivo de la cerradura de la puerta del solenoide.

Recopilación de datos para la detección de rostros

La primera tarea es recopilar los datos para los que vamos a entrenar a nuestro clasificador. Escribiremos un código de Python que tomará 30 caras de cada persona usando un clasificador pre-entrenado de OpenCV.

OpenCV ya contiene muchos clasificadores previamente entrenados para rostro, ojos, sonrisa, etc. El clasificador que vamos a utilizar detectará rostros y el archivo en cascada está disponible en GitHub .

Guarde este archivo en el directorio de trabajo como «haarcascade_frontalface_default.xml».

Tutorial de código

Ahora escribamos el código. Primero, importamos el paquete requerido.

import cv2
from picamera.array import PiRGBArray
from picamera import PiCamera
import numpy as np 
import os
import sys

Luego inicializamos el objeto de la cámara que nos permitirá jugar con la cámara Raspberry Pi. Establecemos la resolución en (640, 480) y la velocidad de fotogramas en 30 fps.

        
camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 30

    

PiRGBArray () nos proporciona una matriz RGB tridimensional organizada (filas, columnas, colores) a partir de una captura RGB no codificada. La ventaja de PiRGBArray es su capacidad para leer los cuadros de la cámara Raspberry Pi como matrices NumPy, lo que lo hace compatible con OpenCV. Evita la conversión del formato JPEG al formato OpenCV, lo que ralentizaría nuestro proceso.

Se necesitan dos argumentos:

  1. El objeto de la cámara
  2. La resolución
rawCapture = PiRGBArray(camera, size=(640, 480))

Cargue un archivo en cascada para detectar caras.

face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml");

A continuación, le pedimos un nombre al usuario. Si un directorio con ese nombre ya está allí, responderá con «Nombre ya existe» y saldrá del código. Si no hay un directorio con este nombre, creará el directorio y las imágenes se guardarán con este nombre.

name = input("What's his/her Name? ")
dirName = "./images/" + name
print(dirName)
if not os.path.exists(dirName):
	os.makedirs(dirName)
	print("Directory Created")
else:
	print("Name already exists")
	sys.exit()

Después de eso, usamos la función capture_continuous para comenzar a leer los cuadros del módulo de cámara Raspberry Pi.

La función  c apture_continuous toma tres argumentos:

  1. rawCapture
  2. El formato en el que queremos leer cada cuadro ya que OpenCV espera que la imagen esté en formato BGR en lugar de RGB, por lo que especificamos que el formato sea BGR.
  3. El booleano use_video_port, haciéndolo verdadero significa que estamos tratando una transmisión como video.
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):

Una vez que tenemos el marco, podemos acceder a la matriz NumPy sin procesar a través del atributo .array. Después de acceder, convertimos este marco a escala de grises.

frame = frame.array
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

Luego llamamos a nuestra función de clasificador para detectar caras en el marco. El primer argumento que pasamos es la imagen en escala de grises. El segundo argumento es el parámetro que especifica cuánto se reduce el tamaño de la imagen en cada escala de imagen. El tercer argumento es un parámetro que especifica cuántos vecinos debe tener cada rectángulo candidato para retenerlo. Un número más alto da falsos positivos más bajos.

faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)

La función anterior nos da coordenadas rectangulares del área de la cara. Usamos estas coordenadas para extraer la cara de la imagen y la guardamos en el directorio que creamos al principio. Después de eso, mostramos la cara recortada y creamos un rectángulo en el marco original.

El código recogerá 30 imágenes.

for (x, y, w, h) in faces:
		roiGray = gray[y:y+h, x:x+w]
		fileName = dirName + "/" + name + str(count) + ".jpg"
		cv2.imwrite(fileName, roiGray)
		cv2.imshow("face", roiGray)
		cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
		count += 1

Luego mostramos el marco original en la ventana de salida. cv2.waitkey () es una función de enlace de teclado. Espera un milisegundo especificado para cualquier evento de teclado. Se necesita un argumento y este argumento es el tiempo en milisegundos. Si se presiona la tecla en ese momento, el programa continuará. Pasar 0 significa que esperará infinitamente una clave.

        
cv2.imshow('frame', frame)
key = cv2.waitKey(1)

   

Luego, limpiamos la secuencia en preparación para el siguiente marco llamando a truncate (0) entre capturas.

rawCapture.truncate(0)

La detección de rostros y la recopilación de datos ahora están completos. Deberíamos tener 30 imágenes en el directorio recién creado.

Código completo de detección de rostros

        
import cv2
from picamera.array import PiRGBArray
from picamera import PiCamera
import numpy as np 
import os
import sys

camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 30
rawCapture = PiRGBArray(camera, size=(640, 480))

faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

name = input("What's his/her Name? ")
dirName = "./images/" + name
print(dirName)
if not os.path.exists(dirName):
	os.makedirs(dirName)
	print("Directory Created")
else:
	print("Name already exists")
	sys.exit()

count = 1
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
	if count > 30:
		break
	frame = frame.array
	gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
	faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)
	for (x, y, w, h) in faces:
		roiGray = gray[y:y+h, x:x+w]
		fileName = dirName + "/" + name + str(count) + ".jpg"
		cv2.imwrite(fileName, roiGray)
		cv2.imshow("face", roiGray)
		cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
		count += 1

	cv2.imshow('frame', frame)
	key = cv2.waitKey(1)
	rawCapture.truncate(0)

	if key == 27:
		break

cv2.destroyAllWindows()

Entrenando al reconocedor

Ahora podemos entrenar al reconocedor de acuerdo con los datos que recopilamos en el paso anterior.

Utilizaremos el reconocimiento facial LBPH (HISTOGRAMAS DE PATRONES BINARIOS LOCALES), incluido en el paquete OpenCV. Lo cargamos en la siguiente línea:

recognizer = cv2.face.LBPHFaceRecognizer_create()

Obtenemos la ruta del directorio de trabajo actual y nos movemos al directorio donde están presentes los directorios de imágenes.

baseDir = os.path.dirname(os.path.abspath(__file__))
imageDir = os.path.join(baseDir, "images")

    

Luego nos movemos a cada directorio de imágenes y buscamos las imágenes. Si la imagen está presente, la convertimos en la matriz NumPy

for root, dirs, files in os.walk(imageDir):
	print(root, dirs, files)
	for file in files:
		print(file)
		if file.endswith("png") or file.endswith("jpg"):
			path = os.path.join(root, file)
			label = os.path.basename(root)
			print(label)

			if not label in labelIds:
				labelIds[label] = currentId
				print(labelIds)
				currentId += 1

			id_ = labelIds[label]
			pilImage = Image.open(path).convert("L")
			imageArray = np.array(pilImage, "uint8")

Después de eso, volvemos a realizar la detección de rostros para asegurarnos de tener las imágenes correctas y luego preparamos los datos de entrenamiento.

faces = faceCascade.detectMultiScale(imageArray, scaleFactor=1.1, minNeighbors=5)

			for (x, y, w, h) in faces:
				roi = imageArray[y:y+h, x:x+w]
				xTrain.append(roi)
				yLabels.append(id_)

Almacene el diccionario que contiene los nombres de directorio y las ID de etiquetas.

with open("labels", "wb") as f:
	pickle.dump(labelIds, f)
	f.close()

Ahora entrene los datos y guarde el archivo.

        
recognizer.train(xTrain, np.array(yLabels))
recognizer.save("trainer.yml")

Este código crea un archivo trainer.yml y etiqueta los archivos que usamos en el código de reconocimiento.

Código completo para entrenar al reconocedor

import os
import numpy as np 
from PIL import Image 
import cv2
import pickle

faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
recognizer = cv2.face.LBPHFaceRecognizer_create()

baseDir = os.path.dirname(os.path.abspath(__file__))
imageDir = os.path.join(baseDir, "images")

currentId = 1
labelIds = {}
yLabels = []
xTrain = []

for root, dirs, files in os.walk(imageDir):
	print(root, dirs, files)
	for file in files:
		print(file)
		if file.endswith("png") or file.endswith("jpg"):
			path = os.path.join(root, file)
			label = os.path.basename(root)
			print(label)

			if not label in labelIds:
				labelIds[label] = currentId
				print(labelIds)
				currentId += 1

			id_ = labelIds[label]
			pilImage = Image.open(path).convert("L")
			imageArray = np.array(pilImage, "uint8")
			faces = faceCascade.detectMultiScale(imageArray, scaleFactor=1.1, minNeighbors=5)

			for (x, y, w, h) in faces:
				roi = imageArray[y:y+h, x:x+w]
				xTrain.append(roi)
				yLabels.append(id_)

with open("labels", "wb") as f:
	pickle.dump(labelIds, f)
	f.close()

recognizer.train(xTrain, np.array(yLabels))
recognizer.save("trainer.yml")
print(labelIds)

Uso del reconocedor para el reconocimiento facial

El reconocedor que configuramos en la sección anterior ahora se puede usar para reconocer las caras. Nos dará confianza e identificación de etiqueta (cuánta confianza tiene el reconocedor en relación con esta coincidencia). Si la cara coincide, el relé se encenderá.

Primero cargamos el archivo pickle que contiene el diccionario.

with open('labels', 'rb') as f:
	dicti = pickle.load(f)
	f.close()

    

Luego cargamos el clasificador que detectará las caras y el reconocedor que predecirá las caras y los datos entrenados.

faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read("trainer.yml")

Leemos el marco, lo convertimos a escala de grises y buscamos las caras en la imagen. Si hay caras, extraeremos la región de la cara y usaremos el reconocedor para reconocer la imagen.

El reconocedor nos dará nuestra identificación de etiqueta y confianza.

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
	faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)
	for (x, y, w, h) in faces:
		roiGray = gray[y:y+h, x:x+w]
		roiColor = frame[y:y+h, x:x+w]

		id_, conf = recognizer.predict(roiGray)

Buscamos en el diccionario el nombre asignado a esta ID de etiqueta.

for name, value in dict.items():
			if value == id_:
				print(name)

    

Luego verificamos si tenemos suficiente confianza para abrir la cerradura de la puerta. Si la confianza es inferior a 70, la puerta se abrirá. De lo contrario, permanecerá cerrado.

if conf <= 70:
			GPIO.output(relay_pin, 1)
			cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
			cv2.putText(frame, name + str(conf), (x, y), font, 2, (0, 0 ,255), 2,cv2.LINE_AA)

		else:
			GPIO.output(relay_pin, 0)

Cree un rectángulo en la imagen original y escriba este nombre en la parte superior del rectángulo.

cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(frame, name, (x, y), font, 2, (0, 0 ,255), 2,cv2.LINE_AA)

Código completo para entrenar al reconocedor

        
import cv2
from picamera.array import PiRGBArray
from picamera import PiCamera
import numpy as np 
import pickle
import RPi.GPIO as GPIO
from time import sleep

relay_pin = [26]
GPIO.setmode(GPIO.BCM)
GPIO.setup(relay_pin, GPIO.OUT)
GPIO.output(relay_pin, 0)

with open('labels', 'rb') as f:
	dicti = pickle.load(f)
	f.close()

camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 30
rawCapture = PiRGBArray(camera, size=(640, 480))


faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read("trainer.yml")

font = cv2.FONT_HERSHEY_SIMPLEX

for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
	frame = frame.array
	gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
	faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)
	for (x, y, w, h) in faces:
		roiGray = gray[y:y+h, x:x+w]

		id_, conf = recognizer.predict(roiGray)

		for name, value in dict.items():
			if value == id_:
				print(name)

		if conf <= 70:
			GPIO.output(relay_pin, 1)
			cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
			cv2.putText(frame, name + str(conf), (x, y), font, 2, (0, 0 ,255), 2,cv2.LINE_AA)

		else:
			GPIO.output(relay_pin, 0)

	cv2.imshow('frame', frame)
	key = cv2.waitKey(1)

	rawCapture.truncate(0)

	if key == 27:
		break

cv2.destroyAllWindows()
https://maker.pro/

2 thoughts on “Cómo crear una cerradura de puerta de reconocimiento facial con Raspberry Pi”

  1. Hola, muy buen tutorial, soy nueva con la Raspberry Pi y seguí tus pasos para el reconocimiento facial pero tengo una duda al correr el ultimo código me da un error
    detector.py:11: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
    GPIO.setup(relay_pin, GPIO.OUT)
    Traceback (most recent call last):
    Espero que me puedas ayudar, Saludos

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *