I apologise in advance if this in the wrong place, or this confuses you. I'm a relative newbie to any kind of electronics.
I want to build a security system entrance to my work space using face recognition (OpenCV),
I found a tutorial by Tony DiCola which is perfect for that desire.
https://learn.adafruit.com/raspberry-pi ... x?view=all
I own picamera, push button, relay and solenoid.
I prefer to use solenoid, because I think solenoid much more promising than servo, and because I already have it.
and in the tutorial it is not included how to modify the software ..Small servo or lock solenoid for the locking mechanism, depending on how your box can be latched shut.
A lock solenoid can work with boxes that have a door or drawer. See this locking drawer project for information on using a lock solenoid. Note: the software for this project is written to use a servo as the locking mechanism, so if you use a lock solenoid you will need to modify the software to actuate the lock with the solenoid instead of the servo.
I do not have the ability in the python
Any help would be appreciated..
Thanks in advance,
the software for this project:
https://github.com/tdicola/pi-facerec-b ... master.zip
Box.py
Code: Select all
"""Raspberry Pi Face Recognition Treasure Box
Treasure Box Script
Copyright 2013 Tony DiCola
"""
import cv2
import config
import face
import hardware
if __name__ == '__main__':
# Load training data into model
print 'Loading training data...'
model = cv2.createEigenFaceRecognizer()
model.load(config.TRAINING_FILE)
print 'Training data loaded!'
# Initialize camer and box.
camera = config.get_camera()
box = hardware.Box()
# Move box to locked position.
box.lock()
print 'Running box...'
print 'Press button to lock (if unlocked), or unlock if the correct face is detected.'
print 'Press Ctrl-C to quit.'
while True:
# Check if capture should be made.
# TODO: Check if button is pressed.
if box.is_button_up():
if not box.is_locked:
# Lock the box if it is unlocked
box.lock()
print 'Box is now locked.'
else:
print 'Button pressed, looking for face...'
# Check for the positive face and unlock if found.
image = camera.read()
# Convert image to grayscale.
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Get coordinates of single face in captured image.
result = face.detect_single(image)
if result is None:
print 'Could not detect single face! Check the image in capture.pgm' \
' to see what was captured and try again with only one face visible.'
continue
x, y, w, h = result
# Crop and resize image to face.
crop = face.resize(face.crop(image, x, y, w, h))
# Test face against model.
label, confidence = model.predict(crop)
print 'Predicted {0} face with confidence {1} (lower is more confident).'.format(
'POSITIVE' if label == config.POSITIVE_LABEL else 'NEGATIVE',
confidence)
if label == config.POSITIVE_LABEL and confidence < config.POSITIVE_THRESHOLD:
print 'Recognized face!'
box.unlock()
else:
print 'Did not recognize face!'
Code: Select all
"""Raspberry Pi Face Recognition Treasure Box
Positive Image Capture Script
Copyright 2013 Tony DiCola
Run this script to capture positive images for training the face recognizer.
"""
import glob
import os
import sys
import select
import cv2
import hardware
import config
import face
# Prefix for positive training image filenames.
POSITIVE_FILE_PREFIX = 'positive_'
def is_letter_input(letter):
# Utility function to check if a specific character is available on stdin.
# Comparison is case insensitive.
if select.select([sys.stdin,],[],[],0.0)[0]:
input_char = sys.stdin.read(1)
return input_char.lower() == letter.lower()
return False
if __name__ == '__main__':
camera = config.get_camera()
box = hardware.Box()
# Create the directory for positive training images if it doesn't exist.
if not os.path.exists(config.POSITIVE_DIR):
os.makedirs(config.POSITIVE_DIR)
# Find the largest ID of existing positive images.
# Start new images after this ID value.
files = sorted(glob.glob(os.path.join(config.POSITIVE_DIR,
POSITIVE_FILE_PREFIX + '[0-9][0-9][0-9].pgm')))
count = 0
if len(files) > 0:
# Grab the count from the last filename.
count = int(files[-1][-7:-4])+1
print 'Capturing positive training images.'
print 'Press button or type c (and press enter) to capture an image.'
print 'Press Ctrl-C to quit.'
while True:
# Check if button was pressed or 'c' was received, then capture image.
if box.is_button_up() or is_letter_input('c'):
print 'Capturing image...'
image = camera.read()
# Convert image to grayscale.
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Get coordinates of single face in captured image.
result = face.detect_single(image)
if result is None:
print 'Could not detect single face! Check the image in capture.pgm' \
' to see what was captured and try again with only one face visible.'
continue
x, y, w, h = result
# Crop image as close as possible to desired face aspect ratio.
# Might be smaller if face is near edge of image.
crop = face.crop(image, x, y, w, h)
# Save image to file.
filename = os.path.join(config.POSITIVE_DIR, POSITIVE_FILE_PREFIX + '%03d.pgm' % count)
cv2.imwrite(filename, crop)
print 'Found face and wrote training image', filename
count += 1
Code: Select all
# Raspberry Pi Face Recognition Treasure Box Configuration
# Copyright 2013 Tony DiCola
# Edit the values below to configure the training and usage of the
# face recognition box.
# Pi GPIO port which is connected to the lock servo signal line.
LOCK_SERVO_PIN = 18
# Pulse width value (in microseconds) for the servo at the unlocked and locked
# position. Center should be a value of 1500, max left a value of 1000, and
# max right a value of 2000.
LOCK_SERVO_UNLOCKED = 2000
LOCK_SERVO_LOCKED = 1100
# Pi GPIO port which is connected to the button.
BUTTON_PIN = 25
# Down and up values for the button. The code expects to detect a down to up
# transition as an activation of the button. Therefore a normally open button
# should be False (low) when down and True (high) when up.
BUTTON_DOWN = False # Low signal
BUTTON_UP = True # High signal
# Threshold for the confidence of a recognized face before it's considered a
# positive match. Confidence values below this threshold will be considered
# a positive match because the lower the confidence value, or distance, the
# more confident the algorithm is that the face was correctly detected.
# Start with a value of 3000, but you might need to tweak this value down if
# you're getting too many false positives (incorrectly recognized faces), or up
# if too many false negatives (undetected faces).
POSITIVE_THRESHOLD = 2000.0
# File to save and load face recognizer model.
TRAINING_FILE = 'training.xml'
# Directories which contain the positive and negative training image data.
POSITIVE_DIR = './training/positive'
NEGATIVE_DIR = './training/negative'
# Value for positive and negative labels passed to face recognition model.
# Can be any integer values, but must be unique from each other.
# You shouldn't have to change these values.
POSITIVE_LABEL = 1
NEGATIVE_LABEL = 2
# Size (in pixels) to resize images for training and prediction.
# Don't change this unless you also change the size of the training images.
FACE_WIDTH = 92
FACE_HEIGHT = 112
# Face detection cascade classifier configuration.
# You don't need to modify this unless you know what you're doing.
# See: http://docs.opencv.org/modules/objdetect/doc/cascade_classification.html
HAAR_FACES = 'haarcascade_frontalface_alt.xml'
HAAR_SCALE_FACTOR = 1.3
HAAR_MIN_NEIGHBORS = 4
HAAR_MIN_SIZE = (30, 30)
# Filename to use when saving the most recently captured image for debugging.
DEBUG_IMAGE = 'capture.pgm'
def get_camera():
# Camera to use for capturing images.
# Use this code for capturing from the Pi camera:
import picam
return picam.OpenCVCapture()
# Use this code for capturing from a webcam:
# import webcam
# return webcam.OpenCVCapture(device_id=0)
Code: Select all
"""Raspberry Pi Face Recognition Treasure Box
Face Detection Helper Functions
Copyright 2013 Tony DiCola
Functions to help with the detection and cropping of faces.
"""
import cv2
import config
haar_faces = cv2.CascadeClassifier(config.HAAR_FACES)
def detect_single(image):
"""Return bounds (x, y, width, height) of detected face in grayscale image.
If no face or more than one face are detected, None is returned.
"""
faces = haar_faces.detectMultiScale(image,
scaleFactor=config.HAAR_SCALE_FACTOR,
minNeighbors=config.HAAR_MIN_NEIGHBORS,
minSize=config.HAAR_MIN_SIZE,
flags=cv2.CASCADE_SCALE_IMAGE)
if len(faces) != 1:
return None
return faces[0]
def crop(image, x, y, w, h):
"""Crop box defined by x, y (upper left corner) and w, h (width and height)
to an image with the same aspect ratio as the face training data. Might
return a smaller crop if the box is near the edge of the image.
"""
crop_height = int((config.FACE_HEIGHT / float(config.FACE_WIDTH)) * w)
midy = y + h/2
y1 = max(0, midy-crop_height/2)
y2 = min(image.shape[0]-1, midy+crop_height/2)
return image[y1:y2, x:x+w]
def resize(image):
"""Resize a face image to the proper size for training and detection.
"""
return cv2.resize(image,
(config.FACE_WIDTH, config.FACE_HEIGHT),
interpolation=cv2.INTER_LANCZOS4)
Code: Select all
"""Raspberry Pi Face Recognition Treasure Box
Treasure Box Class
Copyright 2013 Tony DiCola
"""
import time
import cv2
import RPIO
from RPIO import PWM
import picam
import config
import face
class Box(object):
"""Class to represent the state and encapsulate access to the hardware of
the treasure box."""
def __init__(self):
# Initialize lock servo and button.
self.servo = PWM.Servo()
RPIO.setup(config.BUTTON_PIN, RPIO.IN)
# Set initial box state.
self.button_state = RPIO.input(config.BUTTON_PIN)
self.is_locked = None
def lock(self):
"""Lock the box."""
self.servo.set_servo(config.LOCK_SERVO_PIN, config.LOCK_SERVO_LOCKED)
self.is_locked = True
def unlock(self):
"""Unlock the box."""
self.servo.set_servo(config.LOCK_SERVO_PIN, config.LOCK_SERVO_UNLOCKED)
self.is_locked = False
def is_button_up(self):
"""Return True when the box button has transitioned from down to up (i.e.
the button was pressed)."""
old_state = self.button_state
self.button_state = RPIO.input(config.BUTTON_PIN)
# Check if transition from down to up
if old_state == config.BUTTON_DOWN and self.button_state == config.BUTTON_UP:
# Wait 20 milliseconds and measure again to debounce switch.
time.sleep(20.0/1000.0)
self.button_state = RPIO.input(config.BUTTON_PIN)
if self.button_state == config.BUTTON_UP:
return True
return False
Code: Select all
"""Raspberry Pi Face Recognition Treasure Box
Pi Camera OpenCV Capture Device
Copyright 2013 Tony DiCola
Pi camera device capture class for OpenCV. This class allows you to capture a
single image from the pi camera as an OpenCV image.
"""
import io
import time
import cv2
import numpy as np
import picamera
import config
class OpenCVCapture(object):
def read(self):
"""Read a single frame from the camera and return the data as an OpenCV
image (which is a numpy array).
"""
# This code is based on the picamera example at:
# http://picamera.readthedocs.org/en/release-1.0/recipes1.html#capturing-to-an-opencv-object
# Capture a frame from the camera.
data = io.BytesIO()
with picamera.PiCamera() as camera:
camera.capture(data, format='jpeg')
data = np.fromstring(data.getvalue(), dtype=np.uint8)
# Decode the image data and return an OpenCV image.
image = cv2.imdecode(data, 1)
# Save captured image for debugging.
cv2.imwrite(config.DEBUG_IMAGE, image)
# Return the captured image data.
return image
Code: Select all
# Raspberry Pi Face Recognition Box Servo Calibration Sketch
# Copyright 2013 Tony DiCola
from RPIO import PWM
import config
servo = PWM.Servo()
print 'Servo Calibration'
print
print 'Use this tool to find the pulsewidth values which move the'
print 'lock latch to the locked and unlocked position. Update config.py'
print 'with the locked and unlocked servo pulsewidth values.'
print
print 'Values range from 1000 to 2000 (in microseconds), with 1500 being the center.'
print
print 'Press Ctrl-C to quit'
print
while True:
val = raw_input('Enter servo pulsewidth (1000 to 2000):')
try:
val = int(val)
except ValueError:
print 'Invalid value, must be between 1000 and 2000!'
continue
if val < 1000 or val > 2000:
print 'Invalid value, must be between 1000 and 2000!'
continue
servo.set_servo(config.LOCK_SERVO_PIN, val)
Code: Select all
"""Raspberry Pi Face Recognition Treasure Box
Face Recognition Training Script
Copyright 2013 Tony DiCola
Run this script to train the face recognition system with positive and negative
training images. The face recognition model is based on the eigen faces
algorithm implemented in OpenCV. You can find more details on the algorithm
and face recognition here:
http://docs.opencv.org/modules/contrib/doc/facerec/facerec_tutorial.html
"""
import fnmatch
import os
import cv2
import numpy as np
import config
import face
MEAN_FILE = 'mean.png'
POSITIVE_EIGENFACE_FILE = 'positive_eigenface.png'
NEGATIVE_EIGENFACE_FILE = 'negative_eigenface.png'
def walk_files(directory, match='*'):
"""Generator function to iterate through all files in a directory recursively
which match the given filename match parameter.
"""
for root, dirs, files in os.walk(directory):
for filename in fnmatch.filter(files, match):
yield os.path.join(root, filename)
def prepare_image(filename):
"""Read an image as grayscale and resize it to the appropriate size for
training the face recognition model.
"""
return face.resize(cv2.imread(filename, cv2.IMREAD_GRAYSCALE))
def normalize(X, low, high, dtype=None):
"""Normalizes a given array in X to a value between low and high.
Adapted from python OpenCV face recognition example at:
https://github.com/Itseez/opencv/blob/2.4/samples/python2/facerec_demo.py
"""
X = np.asarray(X)
minX, maxX = np.min(X), np.max(X)
# normalize to [0...1].
X = X - float(minX)
X = X / float((maxX - minX))
# scale to [low...high].
X = X * (high-low)
X = X + low
if dtype is None:
return np.asarray(X)
return np.asarray(X, dtype=dtype)
if __name__ == '__main__':
print "Reading training images..."
faces = []
labels = []
pos_count = 0
neg_count = 0
# Read all positive images
for filename in walk_files(config.POSITIVE_DIR, '*.pgm'):
faces.append(prepare_image(filename))
labels.append(config.POSITIVE_LABEL)
pos_count += 1
# Read all negative images
for filename in walk_files(config.NEGATIVE_DIR, '*.pgm'):
faces.append(prepare_image(filename))
labels.append(config.NEGATIVE_LABEL)
neg_count += 1
print 'Read', pos_count, 'positive images and', neg_count, 'negative images.'
# Train model
print 'Training model...'
model = cv2.createEigenFaceRecognizer()
model.train(np.asarray(faces), np.asarray(labels))
# Save model results
model.save(config.TRAINING_FILE)
print 'Training data saved to', config.TRAINING_FILE
# Save mean and eignface images which summarize the face recognition model.
mean = model.getMat("mean").reshape(faces[0].shape)
cv2.imwrite(MEAN_FILE, normalize(mean, 0, 255, dtype=np.uint8))
eigenvectors = model.getMat("eigenvectors")
pos_eigenvector = eigenvectors[:,0].reshape(faces[0].shape)
cv2.imwrite(POSITIVE_EIGENFACE_FILE, normalize(pos_eigenvector, 0, 255, dtype=np.uint8))
neg_eigenvector = eigenvectors[:,1].reshape(faces[0].shape)
cv2.imwrite(NEGATIVE_EIGENFACE_FILE, normalize(neg_eigenvector, 0, 255, dtype=np.uint8))
Code: Select all
"""Raspberry Pi Face Recognition Treasure Box
Webcam OpenCV Camera Capture Device
Copyright 2013 Tony DiCola
Webcam device capture class using OpenCV. This class allows you to capture a
single image from the webcam, as if it were a snapshot camera.
This isn't used by the treasure box code out of the box, but is useful to have
if running the code on a PC where only a webcam is available. The interface is
the same as the picam.py capture class so it can be used in the box.py code
without any changes.
"""
import threading
import time
import cv2
import config
# Rate at which the webcam will be polled for new images.
CAPTURE_HZ = 30.0
class OpenCVCapture(object):
def __init__(self, device_id=0):
"""Create an OpenCV capture object associated with the provided webcam
device ID.
"""
# Open the camera.
self._camera = cv2.VideoCapture(device_id)
if not self._camera.isOpened():
self._camera.open()
# Start a thread to continuously capture frames.
# This must be done because different layers of buffering in the webcam
# and OS drivers will cause you to retrieve old frames if they aren't
# continuously read.
self._capture_frame = None
# Use a lock to prevent access concurrent access to the camera.
self._capture_lock = threading.Lock()
self._capture_thread = threading.Thread(target=self._grab_frames)
self._capture_thread.daemon = True
self._capture_thread.start()
def _grab_frames(self):
while True:
retval, frame = self._camera.read()
with self._capture_lock:
self._capture_frame = None
if retval:
self._capture_frame = frame
time.sleep(1.0/CAPTURE_HZ)
def read(self):
"""Read a single frame from the camera and return the data as an OpenCV
image (which is a numpy array).
"""
frame = None
with self._capture_lock:
frame = self._capture_frame
# If there are problems, keep retrying until an image can be read.
while frame == None:
time.sleep(0)
with self._capture_lock:
frame = self._capture_frame
# Save captured image for debugging.
cv2.imwrite(config.DEBUG_IMAGE, frame)
# Return the capture image data.
return frame