Leveraging computer vision to mitigate false-alarms in CCTV motion-detection with Python and OpenCV

The problem with motion detection

When a modern CCTV camera/NVR detects motion, it can save still frames of the image and, if configured to do so, will email those images to one or more addresses. This can lead to many false-alarms, however, which will often result in the recipient disregarding what could be, at any given moment, a very serious problem.

My solution so far

To that end, I’ve taken up the task of writing a Python script, utilizing OpenCV’s computer vision libraries, that will process and curate the images generated by an NVR. The script will determine if the generated images contain faces of people. If the images do not contain faces, then I won’t be sent an email alert. If the image does include a face, then I will indeed receive an email with that image attached. This should make alerts more useful, and result in fewer false-alarms so that we can avoid a boy-who-cried-wolf scenario.

The benefits of Python

Some NVRs and IP Cameras already feature facial-detection capabilities, but what if yours does not? This script runs independent of your CCTV equipment, providing modularity and saving you from hardware upgrade expenses. Given the nature of Python, this can be run from virtually any operating system you might be using.

Things I’d still like to add

  • Improve file-selection and saving
  • Implement batch processing
  • Add capability to detect facial profiles
  • Add capability to detect full body, lower body, and upper body
  • Add graphical user interface
Input Image
Output Image @ 1.01
Output Image @ 1.05
Output Image @ 1.10
Output Image @ 1.15
import numpy as np
import sys
import cv2
import matplotlib

face_cascade = cv2.CascadeClassifier('c:\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('c:\\opencv\\build\\etc\\haarcascades\\haarcascade_eye.xml')
full_body = cv2.CascadeClassifier('c:\\opencv\\build\\etc\\haarcascades\\haarcascade_fullbody.xml')
upper_body = cv2.CascadeClassifier('c:\\opencv\\build\\etc\\haarcascades\\haarcascade_upperbody.xml')

selected_image = raw_input('Enter a file for processing: ')

img = cv2.imread(selected_image)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
    img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_gray)
    for (ex,ey,ew,eh) in eyes:
        cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)

cv2.imshow('img',img)

k = cv2.waitKey(0)
if k == 27:         # wait for ESC key to exit
    cv2.destroyAllWindows()
elif k == ord('s'): # wait for 's' key to save and exit
    saveas = raw_input('Save file as: ')
    cv2.imwrite(saveas+'.jpg',img)
    cv2.destroyAllWindows()

sys.exit()

IPnotifier.py

In an effort to maintain connectivity with my LAN while traveling, I’ve written a python script that, when run, will notify myself of any changes to the Public IP address of the localhost. It notifies me in two ways: 1.) An email will be sent to myself for a more immediate notification; 2.) A Google Sheet will be updated to reflect the changed Public IP.

The Google Sheet serves more than just a means of passively reviewing my Public IP, however. It also populates other cells in the spreadsheet, which I embed into my private dashboard online, so that I can better access the services I run at home.

This is definitely a work in progress, and I’m open to any suggestions on how to improve the functionality and security.

"""
IPnotifier.py

new: This program will check my Pubic IP address. It writes the Public IP address to a Google Sheet. If this program finds a Public IP address that's different from the value in the Google Sheet, it will send me an email notifying me of the change and update the Spreadsheet accordingly. I have this set to run every hour via Windows Task Scheduler.

old: This program will monitor my Public IP. When it notices that my Public IP has changed, it will send myself an email to let me know of the updated IP address. This will help keep me informed should my ISP issue me a new IP address over time, allowing me to re-establish communications with my home network while remote.
"""
# Import gspread for interfacing with Google Sheets.
import gspread

from oauth2client.service_account import ServiceAccountCredentials

# Import socket for gethostname() functionality.
import socket

# Import smtplib for the actual sending function
import smtplib

import json

# Import the email modules we'll need
from email.mime.text import MIMEText

from urllib2 import urlopen

scopes = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']

###################################
# USER-ENTERED DATA
###################################
credentials = ServiceAccountCredentials.from_json_keyfile_name ('YOUR_CREDENTIALS_FILE.json', scopes)

gc = gspread.authorize(credentials)

###################################
# USER-ENTERED DATA
# Define the worksheet
###################################
wks = gc.open("YOUR_WORKBOOK").sheet1 ### USER-ENTERED DATA!
# Define the hostname of this computer
hostname = socket.gethostname()
# Define email sender 'me'
me = hostname
###################################
# USER-ENTERED DATA
# Define email recipient 'you'
###################################
you = "YOUR EMAIL ADDRESS" ### USER-ENTERED DATA!
text_file = open("public_ip.txt", "w")

# Use the following URL to obtain my Public IP address, and define it as "my_ip".
my_ip = urlopen('https://ip.42.pl/raw').read()

####################################
# USER-ENTERED DATA
# Define the cell in the worksheet.
####################################
ip_cell = wks.acell('YOUR CELL').value # This cell contains the Public IP Address value.

"""
-- This section will compare the public IP to the recorded IP in the Google Sheet.
-- If the IPs are different, then an email will be sent notifying the user of the change.
-- Finally, we will write the public IP Address to the Google Spreadsheet to keep it current.
"""

if ip_cell != my_ip:

text_file.write("%s: \n" % hostname)
text_file.write("%s" % my_ip)
text_file.close()

# Open a plain text file for reading. For this example, the text file contains only ASCII characters.
with open("public_ip.txt", "rb") as fp:
# Create a text/plain message
msg = MIMEText(fp.read())
msg['Subject'] = 'Public IP for %s is %s' % (hostname, my_ip)
msg['From'] = me
msg['To'] = you
msg['Body'] = 'The Public IP for the computer %s is %s' % (hostname, my_ip)

#############################
# USER-ENTERED DATA
# This is where we keep our email login credentials.
#############################
gmail_user = 'YOUR GMAIL USER'
gmail_password = 'YOUR GMAIL PASS'

#############################
# USER-ENTERED DATA
# Send via our SMTP server, without envelope header.
#############################
s = smtplib.SMTP('smtp.gmail.com', 587)
s.ehlo()

# Upgrade to a secure TLS connection.
s.starttls()
s.login(gmail_user, gmail_password)
s.sendmail(me, [you], msg.as_string())
s.quit()

############################
# USER-ENTERED DATA
# Write localhost's public IP to the Google Sheet cell 'H1'
############################
wks.update_acell('YOUR CELL', my_ip)