Motion Detection App With Alarm, Image Capture and Log: SentrySense

by FuzzyPotato in Circuits > Software

54 Views, 0 Favorites, 0 Comments

Motion Detection App With Alarm, Image Capture and Log: SentrySense

ezgif.com-video-to-gif.gif

Transform Your Laptop into a Powerful Security Sentinel with SentrySense - Easy and Effective Motion Detection Alarm

This guide will show you how to create a motion detection application using Python and OpenCV. The application will detect motion in a video stream from a webcam and trigger an alarm that will write a timestamp to a text file, sound an alarm and capture an image when motion is detected. The sensitivity of motion detection can be adjusted, and you can enable or disable the alarm sound and image capture features. The text file and images will be saved to the directory that the code is run from. Let's get started!


Supplies

Before we start we will need:

  1. Windows computer with Python installed
  2. IDE (I'm using PyCharm)
  3. OpenCV (cv2)
  4. Simpleaudio (simpleaudio)
  5. PySimpleGUI (PySimpleGUI)

Install the Required Libraries

OpenCV_logo_black-2.png

Before we begin, make sure you have the following libraries installed:

  • OpenCV (cv2)
  • Simpleaudio (simpleaudio)
  • PySimpleGUI (PySimpleGUI)


  1. Open the pyCharm development environment (IDE).
  2. Navigate to File>Settings>Project>Python Interpreter
  3. Search for the library.
  4. Install the library.
  5. Wait for the installation process to complete. You should see a message indicating a successful installation.
  6. Repeat for the process for the other libraries.

Code

Now that we have the libraries installed, it's time for the Python code.


  1. Create a new Python script.
  2. Copy and paste the following code.
  3. Download the "AlarmSound.wav" file attached below and save in the same directory that you save the code.


import cv2
import simpleaudio as sa
import PySimpleGUI as sg
from datetime import datetime, timedelta

class MotionDetectionApp:
def __init__(self, initial_threshold_value):
self.video = cv2.VideoCapture(0)
self.previous_frame = None
self.threshold_value = initial_threshold_value
self.alarm_sound = sa.WaveObject.from_wave_file('AlarmSound.wav')
self.alarm_playing = False
self.play_obj = None
self.trigger_count = 0
self.image_capture_enabled = False
self.alarm_sound_enabled = False
self.last_trigger_time = datetime.now()

def process_frame(self, frame, sensitivity):
# Convert the current frame to grayscale
current_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

if self.previous_frame is None:
self.previous_frame = current_frame
return

# Compute the absolute difference between the current and previous frame
frame_diff = cv2.absdiff(current_frame, self.previous_frame)

# Apply a threshold to the frame difference
_, threshold = cv2.threshold(frame_diff, sensitivity, 255, cv2.THRESH_BINARY)

# Find contours of the thresholded image
contours, _ = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Draw rectangles around the detected contours (movement)
motion_detected = False
for contour in contours:
if cv2.contourArea(contour) > sensitivity:
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
motion_detected = True

# Check if enough time has passed since the last trigger
elapsed_time = datetime.now() - self.last_trigger_time
if elapsed_time > timedelta(seconds=3):
if motion_detected:
self.log_alarm_trigger()
self.trigger_count += 1
if self.alarm_sound_enabled:
self.activate_alarm_sound()
if self.image_capture_enabled:
self.capture_image(frame)
self.last_trigger_time = datetime.now()

# Reset the alarm sound if no motion is detected and the alarm has stopped playing
if not motion_detected and self.play_obj is not None and not self.play_obj.is_playing():
self.alarm_playing = False

# Update the previous frame
self.previous_frame = current_frame

def activate_alarm_sound(self):
if not self.alarm_playing:
self.play_obj = self.alarm_sound.play()
self.alarm_playing = True

def capture_image(self, frame):
timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
photo_filename = f"alarm_photo_{timestamp}.png"
cv2.imwrite(photo_filename, frame)

def log_alarm_trigger(self):
timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
log_message = f"Alarm Triggered: {timestamp}\n"
with open("alarm_log.txt", "a") as file:
file.write(log_message)

def run(self):
layout = [
[sg.Image(filename='', key='image')],

[
sg.Column([
[sg.Text("Alarm Triggers: ", key="counter_text")],
]),
sg.Column([
[sg.Text('Sensitivity', size=(10, 1), pad=((60, 5), (10, 0)))],
[sg.Slider(range=(0, 100), default_value=self.threshold_value, orientation='h', key='sensitivity',
pad=((60, 60), (0, 10)))],
], justification='center'),
sg.Column([
[sg.Checkbox('Enable Image Capture', default=False, key='image_capture')],
[sg.Checkbox('Enable Alarm Sound', default=False, key='alarm_sound')],
], justification='center'),
]
]

window = sg.Window('Sentry Sense', layout, finalize=True)
image_elem = window['image']
counter_text_elem = window['counter_text']

while True:
event, values = window.read(timeout=0)

if event == 'exit' or event == sg.WINDOW_CLOSED:
break

# Read the current frame
ret, frame = self.video.read()

# Process the frame and detect motion
self.process_frame(frame, values['sensitivity'])

# Update the counter text
counter_text_elem.update(f"Alarm Triggers: {self.trigger_count}")

# Update toggle states
self.image_capture_enabled = values['image_capture']
self.alarm_sound_enabled = values['alarm_sound']

# Convert the frame to RGB for PySimpleGUI compatibility
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

# Update the GUI image element with the current frame
imgbytes = cv2.imencode('.png', frame_rgb)[1].tobytes()
image_elem.update(data=imgbytes)

# Release the video capture object
self.video.release()
window.close()


if __name__ == '__main__':
MotionDetectionApp(initial_threshold_value=500).run()

Downloads

Motion Detection Alarm System

MotionCapture.PNG
alarm_photo_2023-07-16 16-57-03.png
LogCapture.PNG
LogCaptureV2.PNG

In the final step, we will execute the code and witness its functionality in action. Let's explore how it works:.


  1. Initialize the required libraries: OpenCV, Simpleaudio, and PySimpleGUI.
  2. Create a class called MotionDetectionApp:
  3. Initialize variables and settings for motion detection, including the video capture object, previous frame, threshold value, alarm sound, trigger count, and flags for enabling image capture and alarm sound.
  4. Define methods:process_frame(frame, sensitivity):Convert the current frame to grayscale.
  5. Compute the absolute difference between the current and previous frame.
  6. Apply a threshold to the frame difference to create a binary image.
  7. Find contours of the thresholded image.
  8. Draw rectangles around the detected contours if their area exceeds the sensitivity threshold.
  9. Check if enough time has passed since the last trigger:If motion is detected, log the trigger, increment the trigger count, activate the alarm sound if enabled, capture an image if enabled, and update the last trigger time.
  10. Reset the alarm sound if no motion is detected and the alarm has stopped playing.
  11. Update the previous frame.
  12. activate_alarm_sound():Play the alarm sound if it's not already playing.
  13. capture_image(frame):Capture an image from the frame and save it with a timestamp as a PNG file.
  14. log_alarm_trigger():Log the alarm trigger with a timestamp in a log file.
  15. run():Set up the GUI using PySimpleGUI, including an image element for displaying the video stream, a counter text for alarm triggers, and checkboxes for enabling image capture and the alarm sound.
  16. Enter a loop:Read the current frame from the video stream.
  17. Process the frame using the process_frame method.
  18. Update the GUI elements, including the trigger count and checkbox states.
  19. Convert the frame to RGB format for GUI compatibility.
  20. Update the GUI image element with the current frame.
  21. Handle user events and exit the loop if the window is closed or the 'exit' event is triggered.
  22. Release the video capture object and close the GUI window.
  23. Instantiate the MotionDetectionApp class with an initial threshold value.
  24. Call the run method of the MotionDetectionApp instance to start the motion detection application.


In summary, the code initializes the required libraries and defines a class for the motion detection application. The class contains methods for processing frames, activating the alarm sound, capturing images, and logging triggers. The run method sets up the GUI and enters a loop to continuously read and process frames, update the GUI, and handle user events. The application terminates when the user closes the window or triggers the 'exit' event.


By following these steps, you will have the power of SentrySense at your fingertips: a Motion Detection App with Alarm, Image Capture, and Log.

Celebrate-free-celebration-clip-art-pictures-2.jpg

If you found this Instructable helpful in any way, I would greatly appreciate it if you could show your support by hitting the like button. Your feedback makes a significant difference!

Happy Tinkering!