Introduction
In this tutorial, we’ll walk through the process of creating a real-time surveillance system using Python. The system captures video from a webcam, detects motion, and plays an alert sound when motion is detected. It also saves snapshots of the motion events and logs them with timestamps. We’ll use libraries such as OpenCV for video capture and motion detection, Tkinter for the GUI, and Pygame for playing alert sounds.
Prerequisites
Before we begin, ensure you have the following prerequisites installed on your system:
- Python 3.x
- pip (Python package installer)
Installation
To get started, you’ll need to install several Python libraries. Open your terminal or command prompt and run the following commands:
pip install opencv-python
pip install Pillow
pip install pygame
Project Structure
Create a new directory for your project and organize it as follows:
surveillance_app/
│
├── surveillance_app.py # Main application code
├── alert_sound.mp3 # Alert sound file (replace with your own sound file)
└── snap/ # Directory to save snapshots
Ensure you have an alert_sound.mp3
file in the project directory and create a snap
directory to store the snapshots.
Code
Here’s the complete code for the surveillance system. Save this code in a file named surveillance_app.py
:
import tkinter as tk
from tkinter import ttk
import cv2
import PIL.Image, PIL.ImageTk
import pygame
import os
import datetime
class SurveillanceApp:
def __init__(self, window, window_title):
self.window = window
self.window.title(window_title)
self.window.geometry("800x800")
self.window.configure(bg='#2e2e2e')
# OpenCV setup for capturing video
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
raise RuntimeError("Failed to open video capture.")
# Set capture resolution
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
# Create a canvas that can fit the video source size
self.canvas = tk.Canvas(window, width=self.cap.get(cv2.CAP_PROP_FRAME_WIDTH), height=self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT), bg='#000000', highlightthickness=0)
self.canvas.pack(padx=20, pady=20)
# Initialize pygame mixer for sound
pygame.mixer.init()
self.alert_sound = pygame.mixer.Sound('alert_sound.mp3') # Replace with your sound file
self.delay = 10 # Refresh delay in milliseconds for updating video frames
self.playing_alert = False # Flag to track if alert sound is currently playing
self.snapshot_counter = 0 # Counter to keep track of snapshots taken
self.log_file = 'detection_log.txt' # Log file to store detection details
self.previous_frame = None # Variable to store the previous frame for motion detection
self.motion_threshold = 25 # Initial motion detection sensitivity
self.monitoring = False # Flag to control motion detection
# Status bar
self.status_var = tk.StringVar()
self.status_bar = ttk.Label(window, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W, background='#3c3c3c', foreground='white', font=("Helvetica", 12))
self.status_bar.pack(fill=tk.X, side=tk.BOTTOM, ipady=5)
# Settings frame
self.settings_frame = tk.Frame(window, bg='#2e2e2e')
self.settings_frame.pack(fill=tk.X, pady=5)
tk.Label(self.settings_frame, text="Motion Sensitivity:", bg='#2e2e2e', fg='white', font=("Helvetica", 12)).pack(side=tk.LEFT, padx=5)
self.sensitivity_slider = tk.Scale(self.settings_frame, from_=0, to=100, orient=tk.HORIZONTAL, command=self.update_sensitivity, bg='#2e2e2e', fg='white', troughcolor='#3c3c3c', highlightthickness=0, font=("Helvetica", 12))
self.sensitivity_slider.set(self.motion_threshold)
self.sensitivity_slider.pack(side=tk.LEFT, padx=5)
# Control frame
self.control_frame = tk.Frame(window, bg='#2e2e2e')
self.control_frame.pack(fill=tk.X, pady=10)
# Custom button style
self.style = ttk.Style()
self.style.configure("TButton", font=("Helvetica", 12), padding=10, background="#FF5733", foreground="white", borderwidth=0)
self.style.map("TButton", background=[("active", "#DAF7A6")])
# Start and Stop buttons
self.start_button = ttk.Button(self.control_frame, text="Start", command=self.start_monitoring, style="TButton")
self.start_button.pack(side=tk.LEFT, padx=10)
self.stop_button = ttk.Button(self.control_frame, text="Stop", command=self.stop_monitoring, style="TButton")
self.stop_button.pack(side=tk.LEFT, padx=10)
# Start updating the video frames
self.update()
def update(self):
try:
# Read a frame from the video source
ret, frame = self.cap.read()
if ret:
if self.monitoring:
# Perform motion detection
motion_detected = self.detect_motion(frame)
if motion_detected:
# Log detection
self.log_detection()
# Save the frame as an image
self.save_snapshot(frame)
# Check if alert sound is not playing and play it
if not self.playing_alert:
self.play_alert_sound()
self.playing_alert = True
self.status_var.set("Motion detected")
else:
self.status_var.set("Monitoring...")
# Reset playing_alert flag when no motion is detected
if not motion_detected:
self.playing_alert = False
# Convert the frame to RGB format and display it on the canvas
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)))
self.canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)
# Schedule the next update in `self.delay` milliseconds
self.window.after(self.delay, self.update)
except Exception as e:
self.display_error(str(e))
def detect_motion(self, frame):
# Convert frame to grayscale for motion detection
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# Initialize previous_frame if it's None
if self.previous_frame is None:
self.previous_frame = gray
return False
# Compute the absolute difference between the current frame and previous frame
frame_delta = cv2.absdiff(self.previous_frame, gray)
self.previous_frame = gray
# Threshold the delta image to find regions with significant changes
thresh = cv2.threshold(frame_delta, self.motion_threshold, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# If contours are found, motion is detected
return len(contours) > 0
def update_sensitivity(self, value):
self.motion_threshold = int(value)
def play_alert_sound(self):
# Play the alert sound
self.alert_sound.play()
def log_detection(self):
# Log detection details with timestamp
now = datetime.datetime.now()
log_entry = f"{now.strftime('%Y-%m-%d %H:%M:%S')} - Motion detected\n"
# Append log entry to the log file
with open(self.log_file, 'a') as log:
log.write(log_entry)
def save_snapshot(self, frame):
# Save the current frame as an image file
now = datetime.datetime.now()
filename = f"snap/snapshot_{now.strftime('%Y%m%d_%H%M%S')}.png"
cv2.imwrite(filename, frame)
def display_error(self, message):
# Display error message in a dialog box or console
print(f"Error: {message}")
def start_monitoring(self):
self.monitoring = True
self.status_var.set("Monitoring started")
def stop_monitoring(self):
self.monitoring = False
self.status_var.set("Monitoring stopped")
self.alert_sound.stop()
self.playing_alert = False
# Create the main application window
if __name__ == '__main__':
root = tk.Tk()
app = SurveillanceApp(root, "Surveillance System")
root.mainloop()
Code Explanation
Imports and Initialization
The program starts by importing essential libraries:
tkinter
andttk
for creating the graphical user interface.cv2
(OpenCV) for video capture and processing.PIL
for image handling and display.pygame
for playing alert sounds.os
anddatetime
for file handling and timestamp management.
SurveillanceApp Class
Initialization
The SurveillanceApp
class is responsible for setting up the entire application. The __init__
method initializes the main window, video capture device, and other components.
- Main Window Setup: Sets the window title, size, and background color.
- Video Capture Setup: Initializes the webcam for video capture. Sets the capture resolution and verifies that the camera is working.
- Canvas Creation: Creates a
tkinter
canvas to display the video frames. - Pygame Initialization: Initializes
pygame
for playing alert sounds. Loads the alert sound file. - Control Variables: Initializes variables for controlling the refresh delay, motion detection, and logging.
GUI Components
Several GUI components are set up to interact with the user:
- Status Bar: Displays the current status of the system (e.g., “Monitoring…” or “Motion detected”).
- Settings Frame: Contains a slider to adjust the motion detection sensitivity.
- Control Frame: Contains buttons to start and stop the motion detection.
Video Frame Update
The update
method is called periodically to read a new frame from the video source and process it:
- Frame Reading: Captures a frame from the webcam.
- Motion Detection: If monitoring is enabled, the captured frame is checked for motion.
- Motion Handling: If motion is detected, logs the event, saves a snapshot, and plays an alert sound.
- Frame Display: Converts the frame to a format suitable for displaying on the
tkinter
canvas and updates the canvas.
Motion Detection
The detect_motion
method processes the current frame to detect motion:
- Grayscale Conversion: Converts the frame to grayscale for easier processing.
- Blurring: Applies a Gaussian blur to reduce noise.
- Frame Differencing: Computes the absolute difference between the current frame and the previous frame.
- Thresholding: Applies a binary threshold to highlight significant changes.
- Contour Detection: Finds contours in the thresholded image to detect regions with significant changes.
Sensitivity Adjustment
The update_sensitivity
method updates the motion detection sensitivity based on the slider value.
Alert Handling
The play_alert_sound
method plays an alert sound when motion is detected.
Event Logging
The log_detection
method logs the motion detection event with a timestamp in a log file.
Snapshot Saving
The save_snapshot
method saves the current frame as an image file.
Error Handling
The display_error
method displays error messages.
Monitoring Control
The start_monitoring
and stop_monitoring
methods control the motion detection process.
Main Application
Finally, the main application window is created, and an instance of the SurveillanceApp
class is initialized to start the surveillance system.
This setup creates a functional surveillance system that captures video, detects motion, logs events, saves snapshots, and alerts the user with a sound when motion is detected.
Running the Application
To run the application, navigate to the project directory in your terminal or command prompt and execute the following command:
python surveillance_app.py
Conclusion
When you run the code, the application window will open, displaying the live video feed from your webcam. Use the “Start” button to begin motion detection. When motion is detected, an alert sound will play, a snapshot will be saved in the snap
directory, and the event will be logged in the detection_log.txt
file. You can adjust the motion sensitivity using the slider provided in the settings frame.
This project demonstrates how to integrate video capture, motion detection, and alert mechanisms in a simple GUI application using Python. It’s a great starting point for building more advanced surveillance and security systems.
For more tutorials on Python and Django, be sure to subscribe to our YouTube channel and read our blogs. We regularly post content to help you learn and improve your programming skills.
Happy coding!