Here’s a common question I get asked on the PyImageSearch blog:
How do I make a Python + OpenCV script start as soon as my system boots up?
There are many ways to accomplish. By my favorite is to use crontab and the @reboot option.
The main reason I like this method so much is because crontab exists on nearly every Unix machine (plus, crontab is a really neat utility that I think everyone should have at least some experience with).
It doesn’t matter if you’re on Raspbian, Linux, or OSX — crontab is likely to be installed on your system.
In the remainder of this blog post, I’ll demonstrate how to utilize crontab to start a Python + OpenCV script when your system boots up.
Looking for the source code to this post?
Jump Right To The Downloads SectionRunning a Python + OpenCV script on reboot
As I mentioned in the introduction to this blog post, we’ll be using crontab to launch a script on system reboot.
I’ll be using my Raspberry Pi to accomplish, but the same general instructions apply for other Linux distributions and OSX as well — all you need to do is change the paths to your scripts.
An example application
In last week’s post, I demonstrated how to create an “alarm” program that detects this green ball in a video stream:
If this green ball is detected, an alarm is raised by activating a buzzer and lighting up an LED on the TrafficHAT module (which is connected to a Raspberry Pi):
An example of the “activated alarm” can be seen below:
Here we can see the green ball is in view of the camera. Our program is able to detect the presence of the ball, light up an LED on the board, and if there was sound, you could hear the buzzer going off as well.
Today, we are going to take this example alarm program and modify it so that it can be started automatically when the Raspberry Pi boots up — we will not have to manually execute any command to start our alarm program.
Creating the launcher
Before we can execute our Python script on reboot, we first need to create a shell script that performs two important tasks:
- (Optional) Accesses our Python virtual environment. I’ve marked this step as optional only because in some cases, you may not be using a Python virtual environment. But if you’ve followed any of the OpenCV install tutorials on this blog, then this step is not optional since your OpenCV bindings are stored in a virtual environment.
- Executes our Python script. This is where all the action happens. We need to (1) change directory to where our Python script lives and (2) execute it.
Accomplishing both these tasks is actually quite simple.
Below I have included the contents of my on_reboot.sh
shell script which I have placed in /home/pi/pi-reboot
:
#!/bin/bash source /home/pi/.profile workon cv cd /home/pi/pi-reboot python pi_reboot_alarm.py
When we reboot our Pi, the on_reboot.sh
script will be running as the root user (provided that you edit the root crontab, of course; which we’ll cover in the next section).
However, we first need to access the cv
virtual environment (or whatever Python virtual environment you are using), so we’ll call source /home/pi/.profile
to setup the virtual environment scripts, followed by workon cv
to drop us into the cv
environment.
Note: To learn about Python virtual environments, please refer to this post.
After we have setup our environment, we change directory to /home/pi/pi-reboot
, which is where I have stored the pi_reboot_alarm.py
script.
Finally, we are ready to execute pi_reboot_alarm.py
— executing this script will be done within the cv
virtual environment (thanks to the source
and workon
commands).
Note: Again, make sure you have read both the accessing RPi.GPIO and GPIO Zero with OpenCV post and the OpenCV, RPi.GPIO, and GPIO Zero on the Raspberry Pi post before continuing with this tutorial. Both of these posts contain important information on configuring your development environment and installing required Python packages.
After adding these lines to to your on_reboot.sh
, save the file and then. Then, to make it executable, you’ll need to chmod
it:
$ chmod +x on_reboot.sh
After changing the permissions of the file to executable, you’re ready to move on to the next step!
Updating crontab
Now that we have defined the on_reboot.sh
shell script, let’s update the crontab to call it on system reboot.
Simply start by executing the following command to edit the root user’s crontab:
$ sudo crontab -e
This command should bring up the crontab file, which should look something like this:
You should then enter the following lines at the bottom of the file:
@reboot /home/pi/pi-reboot/on_reboot.sh
This command instructs the system to execute the on_reboot.sh
script whenever our system is rebooted.
Note: You can obviously replace the path to on_reboot.sh
with your own shell script.
Once you have finished editing the crontab, save the file and exit the editor — the changes to crontab will be automatically applied. Then at next reboot, the on_reboot.sh
script will be automatically executed.
Creating our Python script
The contents of pi_reboot_alarm.py
are near identical to last week’s blog post on OpenCV, RPi.GPIO, and GPIO Zero on the Raspberry Pi, but I’ve included the contents of the script as a matter of completeness:
# import the necessary packages from imutils.video import VideoStream from gpiozero import TrafficHat import argparse import datetime import logging import imutils import time import cv2 # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-p", "--picamera", type=int, default=-1, help="whether or not the Raspberry Pi camera should be used") ap.add_argument("-l", "--log", type=str, default="log.txt", help="path to output log file") args = vars(ap.parse_args())
Lines 2-9 handle importing our required Python packages. We’ll be using VideoStream
to seamlessly access either the Raspberry Pi camera module or USB camera module. The TrafficHat
class from gpiozero
will allow us to easily manipulate the TrafficHAT board. And the imutils library will be used for some OpenCV convenience functions.
If you don’t already have imutils
installed, let pip
install it for you:
$ pip install imutils
Lines 12-17 parse our command line arguments. The first argument, --picamera
is used to indicate whether or not the Raspberry Pi camera module should be used. By default, a USB webcam is assumed to be connected to the Pi. But if you want to use the Raspberry Pi camera module instead, simply supply --picamera 1
as a command line argument. The second switch, --log
, is used to control the path to the output log file which can be used for debugging.
Our next code block handles performing a series of initializations, including accessing the VideoStream
class and setting up the TrafficHat
module:
# open the logging file logging.basicConfig(filename=args["log"], level=logging.DEBUG) # initialize the video stream and allow the cammera sensor to # warmup logging.info("[{}] waiting for camera to warmup".format( datetime.datetime.now())) vs = VideoStream(usePiCamera=args["picamera"] > 0).start() time.sleep(2.0) # define the lower and upper boundaries of the "green" # ball in the HSV color space greenLower = (29, 86, 6) greenUpper = (64, 255, 255) # initialize the TrafficHat and whether or not the LED is on th = TrafficHat() ledOn = False
We can now move on to the main video processing pipeline of our script, where we read frames from the VideoStream
and process each of them, looking for a green ball:
# loop over the frames from the video stream while True: # grab the next frame from the video stream, resize the # frame, and convert it to the HSV color space frame = vs.read() frame = imutils.resize(frame, width=500) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # construct a mask for the color "green", then perform # a series of dilations and erosions to remove any small # blobs left in the mask mask = cv2.inRange(hsv, greenLower, greenUpper) mask = cv2.erode(mask, None, iterations=2) mask = cv2.dilate(mask, None, iterations=2) # find contours in the mask and initialize the current # (x, y) center of the ball cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) center = None
If we find the green ball, then we’ll buzz the buzzer and light up the green LED on the TrafficHAT:
# only proceed if at least one contour was found if len(cnts) > 0: # find the largest contour in the mask, then use # it to compute the minimum enclosing circle and # centroid c = max(cnts, key=cv2.contourArea) ((x, y), radius) = cv2.minEnclosingCircle(c) M = cv2.moments(c) center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) # only proceed if the radius meets a minimum size if radius > 10: # draw the circle and centroid on the frame cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 2) cv2.circle(frame, center, 5, (0, 0, 255), -1) # if the led is not already on, raise an alarm and # turn the LED on if not ledOn: logging.info("[{}] alarm ON".format( datetime.datetime.now())) th.buzzer.blink(0.1, 0.1, 10, background=True) th.lights.green.on() ledOn = True
Finally, if the green ball is not found, we’ll turn off the LED on the TrafficHAT:
# if the ball is not detected, turn off the LED elif ledOn: logging.info("[{}] alarm OFF".format( datetime.datetime.now())) th.lights.green.off() ledOn = False # do a bit of cleanup logging.info("[{}] cleaning up".format( datetime.datetime.now())) cv2.destroyAllWindows() vs.stop()
Again, for a more comprehensive review of this code, please refer to last week’s blog post.
Assuming you’ve read through last week’s post, you might notice an interesting modification — I’ve removed the call to cv2.imshow
, which is used to display output frames to our screen.
Why would I do this?
Mainly because our pi_reboot_alarm.py
script is meant to run in the background when our Pi is rebooted — the output is never meant to be displayed to our screen. All we care about is the alarm being properly raised if the green ball enters our video stream.
Furthermore, removing calls to cv2.imshow
reduces I/O latency, thereby allowing our Python script to run faster and process frames quicker (you can read more about I/O latency related to video streams in this post).
Executing a Python script at reboot
All that’s left to do now is test our crontab installation by rebooting our system. To restart my Raspberry Pi, I execute the following command:
$ sudo reboot
And as the following video demonstrates, as soon as my Pi boots up, the on_reboot.sh
shell script is called, thereby executing the pi_reboot_alarm.py
Python program and arming the alarm:
You can see from the following screenshot that once the green ball enters the view of the camera, the green LED of the TrafficHAT is illuminated:
And if you watch the video above, you can also hear the buzzer going off at the same time.
What's next? I recommend PyImageSearch University.
30+ total classes • 39h 44m video • Last updated: 12/2021
★★★★★ 4.84 (128 Ratings) • 3,000+ Students Enrolled
I strongly believe that if you had the right teacher you could master computer vision and deep learning.
Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?
That’s not the case.
All you need to master computer vision and deep learning is for someone to explain things to you in simple, intuitive terms. And that’s exactly what I do. My mission is to change education and how complex Artificial Intelligence topics are taught.
If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to successfully and confidently apply computer vision to your work, research, and projects. Join me in computer vision mastery.
Inside PyImageSearch University you'll find:
- ✓ 30+ courses on essential computer vision, deep learning, and OpenCV topics
- ✓ 30+ Certificates of Completion
- ✓ 39h 44m on-demand video
- ✓ Brand new courses released every month, ensuring you can keep up with state-of-the-art techniques
- ✓ Pre-configured Jupyter Notebooks in Google Colab
- ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
- ✓ Access to centralized code repos for all 500+ tutorials on PyImageSearch
- ✓ Easy one-click downloads for code, datasets, pre-trained models, etc.
- ✓ Access on mobile, laptop, desktop, etc.
Summary
In this blog post, I demonstrated how to use crontab to launch a Python + OpenCV script on reboot.
To accomplish this task, I utilized my Raspberry Pi; however, crontab is installed on nearly all Unix machines, so no matter if you’re on Linux or OSX, crontab is likely available for you to use.
Anyway, I hope you enjoyed this series of blog posts on utilizing the Raspberry Pi, OpenCV, and GPIO libraries. If you would like to see more blog posts about these topics, please leave a comment in the comments section at the bottom of this post.
And before you go, don’t forget to enter your email address in the form below to be notified when new blog posts are published!
Download the Source Code and FREE 17-page Resource Guide
Enter your email address below to get a .zip of the code and a FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning. Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!