Raspberry / Intruder Alert Device

Intruder Alert Device With Raspberry Pi and Python

By Marcelo Fernandes Nov 22, 2017



Making your Intruder-Alert with Raspberry Pi and Python.

In this tutorial we are going to build up an intruder-alert system that is going to send you an e-mail, as soon as someone is detected invading your house in a certain period of the day.



What will you Learn

  • 1 - What is haar cascade classifiers and how to use it for object detection.
  • 2 - How to use OpenCV for Body Detection
  • 3 - How to Fire up automatic emails via Gmail directly from python code using the standard library


What will you Need

  • 1 - A Raspberry PI (of course!).
  • 2 - An USB camera.
  • 3 - A gmail account to use as e-mail provider.


Introduction

Raspberry PI has grown into a very powerful board over time, and now its image processing speed has become decent enough to support this kind of application. I hope you are excited about it, so let's skip the words and dive into it!



Installing the Dependencies

For this project, there is only one dependency: Opencv 3.0.0. But this library is kinda tricky to be installed on the Raspberry. I've seen people being frustrated about the OpenCV installation, then I created a tutorial that will guide you to install the OpenCV library flawlessly. You won't have any problems following those steps, but if you do, leave me a note and I will try to help you with that.


After following the tutorial for the OpenCV installation, let's go back to the next steps.



Haar Cascade

Haar-based cascade classifier, is a very effective object detection method. It is a machine learning approach that consists on training the algorithm from a lot of positive and negative images.

That is what we will be using here in this project. There are tons of different Haar Cascades already trained for you, so you actually don't have to go through all this trouble. For this particular case, we are going to use a face-detector Haar Cascade, once we want to take a picture of the intruder face and send to you via e-mail, so you can send it to the police ;)

Therefore, your first task is to download the Frontal Face Haar Cascade file, that can be found in the OpenCV repository itself. It is a .xml file, and it is very very very small in size.

After doing it, place your cascade on the folder that you are going to use for the project, and lets get it going.



Coding our Detector

First things first. We have to detect whether there is someone on the screen or not. For that, take a look a this script:

project/detector.py

# STANDARD LIBRARY IMPORT
import os
import sys
# THIRD PARTY LIBRARIES
import cv2


def detector():

    # VideoCapture will literally capture the first camera that it sees
    # available on your raspberry!
    cap = cv2.VideoCapture(0)

    # Loads the face_cascade classifier.
    face_cascade = cv2.CascadeClassifier(os.path.dirname(sys.argv[0]) +
                                         '/haarcascade_frontalface_alt2.xml')

    # Starts the infinite loop!
    while True:
        # capture frame-by-frame
        ret, frame = cap.read()

        # If there is any face on the camera, our cascade classifier will return
        # the position of the faces (if more than one) that are in the screen
        # else returns None.
        faces = face_cascade.detectMultiScale(frame, 1.3, 5)

        # Loop over the faces and draws a Green Square of 2px on the face!
        for (x, y, w, h) in faces:
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

        # Display the resulting frame
        cv2.imshow('frame', frame)

        # If you press q, the screen will close and the script will leave the loop.
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    # When everything is done release the capture
    cap.release()
    cv2.destroyAllWindows()

detector()

Once you release the script, if you have access to your raspberry pi interface and run it, you might see something like that:



That's an ilustrative image, so you guys don't have to see my ugly face :D

Firing up an Email via Gmail

The next step is being able to fire up an e-mail once there is an intruder in your house, and of course, you want a picture to be attached on the email, so you can take a look at the intruder!

But first of all, there is one thing that we want to keep in mind: We want to have some sort of "e-mail timeout", it means that once an e-mail was sent to you, there must have a certain amount of time until the script can send you another e-mail, otherwise gmail will figure it out that you are abusing and will start treating your email as SPAM.

In order to avoid that, we are going to be waiting for 1 minute before sending the next e-mail (if the intrude keeps walking on your house!)

Take a look at the code:

project/send_mail.py

# STANDARD LIBRARY IMPORTS
import smtplib
from datetime import datetime, timedelta
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
import sys

# THIRD PARTY IMPORTS
import cv2

# gmail must be set to allow less secure apps, so you can send emails.
# set it up on:
# https://myaccount.google.com/u/1/lesssecureapps?pli=1


def send_mail(frame):

    path = os.path.dirname(sys.argv[0])
    log_file = path + '/email.log'

    # Check if the last email was sent in an 1 minute tolerance.
    # We are going to use a .log file to keep track of it.
    if os.path.isfile(log_file): # First check if file exists.
        with open(log_file, 'r') as f:
            date = f.read()
            date_to_datetime = datetime.strptime(date, "%Y-%m-%d %H:%M:%S.%f")

            if datetime.now() < date_to_datetime + timedelta(minutes=1):
                return

    # Update the email log.
    with open(log_file, 'w') as f:
        f.write(str(datetime.now()))

    # Create the jpg picture to attach.
    cv2.imwrite("project/intrude.jpg", frame)

    gmail_user = 'user'
    gmail_password = 'pass'
    recipient = 'recipient'
    message = 'Hey! It appears that someone is at home!!!'

    msg = MIMEMultipart()
    msg['From'] = gmail_user
    msg['To'] = recipient
    msg['Subject'] = "Someone is at Home!"
    msg.attach(MIMEText(message))

    # Attachment
    file = path + '/intrude.jpg'
    filename = 'intrude.jpg'
    attachment = open(file, "rb")

    part = MIMEBase('application', 'octet-stream')
    part.set_payload(attachment.read())
    encoders.encode_base64(part)
    part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
    msg.attach(part)

    mail_server = smtplib.SMTP('smtp.gmail.com', 587)
    mail_server.ehlo()
    mail_server.starttls()
    mail_server.ehlo()
    mail_server.login(gmail_user, gmail_password)
    mail_server.sendmail(gmail_user, recipient, msg.as_string())
    mail_server.close()

What we did here:

  1. Created a Function that receives a frame, this frame is going to be passed from our detector function later, which is going to be the picture of the intruder!
  2. We used a email.log file to keep trace of the time when the last e-mail was sent, so we don't mess up with Gmail sending tons of e-mails every 10 seconds.
  3. Created a .jpg image that is going to be attached on the e-mail with cv2.imwrite
  4. We used the email library to send an e-mail with an attachment via gmail

Make sure you change the e-mail variables for your own gmail account.

Now we gotta do the modifications on our project/detector.py to support our project/send_email.py script.



Modifying our Detector to Send Emails.

Let's make few modifications on our detector to support e-mail sending:

project/detector.py

# ...
from project.send_mail import send_mail

# ...

def detector():

    # ...

    while True:  # check !
    # capture frame-by-frame
    ret, frame = cap.read()

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)

    for (x, y, w, h) in faces:
        cv2.rectangle(gray, (x, y), (x + w, y + h), (255, 0, 0), 2)
        send_mail(frame=frame)

    # ...

So... What have we got here? We turned our RGB frame in a Gray frame, once our face cascade works better detecting objects on a gray frame. And the second thing that we did was calling the send_email function with the frame in which a face was detected.


Once you execute the script and someone passes in front of your Raspberry camera, you should receive an e-mail like that:


Further Improvement...

Now you have everything on your plate to dig into a more advanced Intruder-Detector project. Those are some stuff that you can improve in this project:

  • Add a working time for the script: You should be able to run the script only when you are not at house. You can create a new function that checks your calendar to have a sense wheter you are at home or not, so the intruder email will only be fired during hours when you are not at home.
  • Improve the code: You see that code up here? Yes... There are places where you can improve it, Try to log information using the logging python library
  • Improve the haarcascade:There are some parameters that you can pass to the detectMultiScale function, play around with them and check if you can improve the image recognition.
  • Play Around with other cascades: Maybe you are not interested only in the face detection, so why not grab other cascades and see how they perform? A good one for this project might be the full-body cascade. ;)