3 minutes
Access Raspberry Pi Camera using Python and OpenCV
In this article we are going to see how to install OpenCV on a Raspberry PI using Bookworm.
Prerequisites
Install dependencies on Raspberry Pi
sudo apt update
sudo apt install python3-picamera2
sudo apt install libcamera-apps
sudo apt install python3-opencv
Install dependencies on Macbook
pip install opencv-python numpy
Code
import cv2
import numpy as np
import time
from datetime import datetime
import os
# ==========================
# CONSTANTS (Configuration)
# ==========================
BLUR_SIZE = (7, 7) # Larger → less sensitive (e.g., (7,7) or (9,9))
THRESHOLD_SENSITIVITY = 50 # Higher → less sensitive (e.g., 60, 70)
MIN_CONTOUR_AREA = 2000 # Higher → only detects larger movements
FRAME_WAIT_TIME = 0.1 # Time to wait between frames (in seconds)
SAVE_DIR = "captures" # Directory to save captured images
# ==========================
# Camera Initialization
# ==========================
def initialize_camera():
try:
from picamera2 import Picamera2
picam2 = Picamera2()
config = picam2.create_preview_configuration(main={"format": "RGB888", "size": (640, 480)})
picam2.configure(config)
picam2.start()
time.sleep(2)
print("[INFO] Raspberry Pi camera initialized.")
return picam2, True
except ImportError:
cap = cv2.VideoCapture(0)
if not cap.isOpened():
raise Exception("[ERROR] Cannot open webcam.")
time.sleep(2)
print("[INFO] Webcam initialized.")
return cap, False
# ==========================
# Frame Capture
# ==========================
def capture_frame(camera, is_picam):
if is_picam:
return camera.capture_array()
else:
ret, frame = camera.read()
if not ret:
raise Exception("[ERROR] Failed to capture frame.")
return frame
# ==========================
# Frame Processing
# ==========================
def process_frame(current_frame, previous_frame, is_picam):
# Apply Gaussian blur
current_blurred = cv2.GaussianBlur(current_frame, BLUR_SIZE, 0)
previous_blurred = cv2.GaussianBlur(previous_frame, BLUR_SIZE, 0)
# Convert to grayscale
if is_picam:
gray_current = cv2.cvtColor(current_blurred, cv2.COLOR_RGB2GRAY)
gray_previous = cv2.cvtColor(previous_blurred, cv2.COLOR_RGB2GRAY)
else:
gray_current = cv2.cvtColor(current_blurred, cv2.COLOR_BGR2GRAY)
gray_previous = cv2.cvtColor(previous_blurred, cv2.COLOR_BGR2GRAY)
# Compute difference and threshold
diff = cv2.absdiff(gray_previous, gray_current)
_, thresh = cv2.threshold(diff, THRESHOLD_SENSITIVITY, 255, cv2.THRESH_BINARY)
thresh = cv2.dilate(thresh, None, iterations=2)
return thresh
# ==========================
# Movement Detection
# ==========================
def detect_movement(thresh):
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
if cv2.contourArea(contour) >= MIN_CONTOUR_AREA:
return True
return False
# ==========================
# Save Frame
# ==========================
def save_frame(frame, is_picam):
now = datetime.now()
timestamp = now.strftime("%Y-%m-%d_%H-%M-%S")
filename = os.path.join(SAVE_DIR, f"capture_{timestamp}.jpg")
if is_picam:
frame_to_save = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
else:
frame_to_save = frame # Already BGR
cv2.imwrite(filename, frame_to_save)
print(f"[INFO] Image saved: {filename}")
# ==========================
# Main Function
# ==========================
def main():
os.makedirs(SAVE_DIR, exist_ok=True)
camera, is_picam = initialize_camera()
previous_frame = capture_frame(camera, is_picam)
print("[INFO] Surveillance started... (Press Ctrl+C to exit)")
try:
while True:
current_frame = capture_frame(camera, is_picam)
thresh = process_frame(current_frame, previous_frame, is_picam)
if detect_movement(thresh):
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Movement detected!")
save_frame(current_frame, is_picam)
previous_frame = current_frame.copy()
time.sleep(FRAME_WAIT_TIME)
except KeyboardInterrupt:
print("\n[INFO] Surveillance stopped by the user.")
finally:
if is_picam:
camera.stop()
else:
camera.release()
# ==========================
# Entry Point
# ==========================
if __name__ == "__main__":
main()
BLUR_SIZE
: larger → less sensitive (e.g., (7,7) or (9,9))THRESHOLD_SENSITIVITY
: higher → less sensitive (e.g., 60, 70)MIN_CONTOUR_AREA
: higher → only detects larger movements