Building Surveillance System Using USB Camera and Wireless-Connected Raspberry Pi

Read this post to learn how to build a surveillance system using a USB camera plugged into Raspberry Pi (RPi) which is connected a PC using its wireless interface.



3. Capturing Images using PyGame

 
The “fswebcam” package is useful for quickly testing whether the camera is working well or not. After making sure it is functioning well, we can start building a Python script that accesses the camera to capture images continuously using the PyGame library. The following code uses PyGame for capturing a single image, opens a window for displaying that image, and finally save such image.

import pygame
import pygame.camera

# Captured image dimensions. It should be less than or equal to the maximum dimensions acceptable by the camera.
width = 320
height = 240

# Initializing PyGame and the camera.
pygame.init()
pygame.camera.init()

# Specifying the camera to be used for capturing images. If there is a single camera, then it have the index 0.
cam = pygame.camera.Camera("/dev/video0",(width,height))

# Preparing a resizable window of the specified size for displaying the captured images.
window = pygame.display.set_mode((width,height),pygame.RESIZABLE)

# Starting the camera for capturing images.
cam.start()

# Capturing an image.
image = cam.get_image()

# Stopping the camera.
cam.stop()

# Displaying the image on the window starting from the top-left corner.
window.blit(image,(0,0))

# Refreshing the window.
pygame.display.update()

# Saving the captured image.
pygame.image.save(window,'PyGame_image.jpg')


Assume that the above code is saved in a Python file named “im_cap.py”. To execute such code, we can issue the following command from the terminal:

pi@raspberry:~ $ python3 im_cam.py


Here is the window displayed after executing such file.

We can modify the previous code to capture more than one image. For example, we can use a for loop to capture a number of previously specified images. We can also use a while loop that is not limited to a number of images. Here is the modified code that captures 2,000 images using a for loop.

import pygame
import pygame.camera

# Captured image dimensions. It should be less than or equal to the maximum dimensions acceptable by the camera.
width = 320
height = 240

# Initializing PyGame and the camera.
pygame.init()
pygame.camera.init()

# Specifying the camera to be used for capturing images. If there is a single camera, then it has the index 0.
cam = pygame.camera.Camera("/dev/video0", (width, height))

# Preparing a resizable window of the specified size for displaying the captured images.
window = pygame.display.set_mode((width, height), pygame.RESIZABLE)

# Starting the camera for capturing images.
cam.start()

for im_num in range(0, 2000):
    print("Image : ", im_num)

    # Capturing an image.
    image = cam.get_image()

    # Displaying the image on the window starting from the top-left corner.
    window.blit(image, (0, 0))

    # Refreshing the window.
    pygame.display.update()

    # Saving the captured image.
    pygame.image.save(window, './pygame_images/image_' + str(im_num) + '.jpg')

# Stopping the camera.
cam.stop()


Here are 8 captured images. Note that the camera position is changed a bit.

 

4. Building the Background Model

 
Up to this point, we successfully built a simple surveillance system in which a camera captures images which are saved in the SD card of RPi. We can extend that to automatically detect changes to the scene. This is done by building a background model for the scene. Any change to such a model will indicate a change. For example, if someone is passing through the scene will cause a change to the background.

The background model can be simply created by averaging multiple captured images to the scene background without any object in them. Because we are interested in the color information, the images will be converted into binary. Here is the Python code used to build the background model.

import skimage.io
import os
import numpy

dir_files = os.listdir('./pygame_images/')

bg_image = skimage.io.imread(fname=dir_files[0], as_grey=True)

for k in range(1, len(dir_files)):
    fname = dir_files[k]
    im = skimage.io.imread(fname=fname, as_grey=True)
    bg_image = bg_image + im

bg_image = bg_image/(len(dir_files))
bg_image_bin = bg_image > 0.5

skimage.io.imsave(fname='bg_model.jpg', arr=bg_image)
skimage.io.imsave(fname='bg_model_bin.jpg', arr=bg_image_bin*255)


Here is the background model in both gray and binary after averaging 500 images.

 

5. Detecting Changes to the Background Model

 
After building the background model, we can test a new image to check if there is a change to the background or not. This is done simply by converting the new image into binary. Then the number of white pixels is compared in both images. If the number exceeds a given threshold, this indicates a change from the background. The threshold changes from scene to scene. Here is the code used for testing a new image.

bg_num_ones = numpy.sum(bg_image_bin)
test = skimage.io.imread(fname="./pygame_images/image_800.jpg", 
                         as_grey=True)
test_bin = test > 0.5
test_num_ones = numpy.sum(test_bin)
print("Num 1s in BG   :", bg_num_ones)
print("Num 1s in Test :", test_num_ones)

if(abs(test_num_ones-bg_num_ones) < 5000):
    print("Change.")


Here is a test image in both color, gray, and binary in which there is a change from the background due to the appearance of an object (person) in the scene.

 

6. Building a Simple Circuit that Lights a Led When a Change Occurs

 
As an indication of a change to the background model, we can build a simple circuit in which a led lights when a change occurs. This circuit will be connected to the GPIO (General Purpose Input Output) bins of the RPi. The circuit needs the following components:

  • One breadboard.
  • One led.
  • One resistor (more than or equal to 100 ohms). I use a 178.8 ohms resistor.
  • Two male/male jumper wires.
  • Two male/female jumper wires.

It is recommended to test the circuit before connecting it to the GPIO pins. This is because if the resistor value was not selected properly, this might lead to not only burning the led but also damaging the GPIO pins. To do the test, we need a battery for supplying the breadboard by power. Here is the circuit after connecting all components correctly.

After that, we can remove the battery and connect the breadboard the GPIO pins of RPi. Based on the breadboard numbering of the GPIO pins, the ground is connected to bin number 20 and the high voltage is connected to the output bin number 22. The following figure illustrates the connections between the breadboard and the RPi. The RPi is also connected to both the charger and the USB camera.

The output GPIO bin is controlled using the Python script given below. Its default state is LOW meaning the led is turned off. When there is a change to the background, the state will be changed to HIGH meaning the led is turned on. The led remains on for 0.1 seconds then its state returns back to off. When another input image is different from the background, the led returns back to on for another 0.1 seconds.

import time
import RPi.GPIO
import skimage.io
import numpy
import os
import pygame.camera
import pygame

#####GPIO#####
# Initializing the GPIO pins. The numbering using is board.
RPi.GPIO.setmode(RPi.GPIO.BOARD)

# Configuring the GPIO bin number 22 to be an output bin.
RPi.GPIO.setup(22, RPi.GPIO.OUT)

#####PyGame#####
# Initializing PyGame and the camera.
pygame.init()
pygame.camera.init()

# Captured image dimensions. It should be less than or equal to the maximum dimensions acceptable by the camera.
width = 320
height = 240

# Preparing a resizable window of the specified size for displaying the captured images.
window = pygame.display.set_mode((width, height), pygame.RESIZABLE)

# Specifying the camera source and the image dimensions.
cam = pygame.camera.Camera("/dev/video0",(width,height))
cam.start()

#####Background Model#####
# Reading the background model.
bg_image = skimage.io.imread(fname='bg_model_bin.jpg', as_grey=True)
bg_image_bin = bg_image > 0.5
bg_num_ones = numpy.sum(bg_image_bin)

im_dir = '/home/pi/pygame_images/'

for im_num in range(0, 2000):
    print("Image : ", im_num)

    im = cam.get_image()

    # Displaying the image on the window starting from the top-left corner.
    window.blit(im, (0, 0))

    # Refreshing the window.
    pygame.display.update()

    pygame.image.save(window, im_dir+'image'+str(im_num)+'.jpg')
    im = pygame.surfarray.array3d(window)

    test_bin = im > 0.5
    test_num_ones = numpy.sum(test_bin)

    # Checking if there is a change in the test image.
    if (abs(test_num_ones - bg_num_ones) < 5000):
        print("Change.")
        try:
            RPi.GPIO.output(22, RPi.GPIO.HIGH)
            time.sleep(0.1)
            RPi.GPIO.output(22, RPi.GPIO.LOW)
        except KeyboardInterrupt:  # CTRL+C
            print("Keyboard Interrupt.")
        except:
            print("Error occurred.")

# Stopping the camera.
cam.stop()

# cleanup all GPIO pins.
print("Clean Up GPIO.")
RPi.GPIO.cleanup()


The following figure is shown one input image which is different from the background image due to the existence of a person. As a result, the led will be turned on for 0.1 seconds.

The following video (https://youtu.be/WOUG-vjg3A4) shows the led state for multiple frames captured using the camera. The led is turned on when the input image is different from the background model according to the used threshold.

 
For More Details

Ahmed Fawzy Gad, “Building an Image Classifier Running on Raspberry Pi”, September 2018, https://www.linkedin.com/pulse/building-image-classifier-running-raspberry-pi-ahmed-gad

For Contacting the Author

 
Bio: Ahmed Gad received his B.Sc. degree with excellent with honors in information technology from the Faculty of Computers and Information (FCI), Menoufia University, Egypt, in July 2015. For being ranked first in his faculty, he was recommended to work as a teaching assistant in one of the Egyptian institutes in 2015 and then in 2016 to work as a teaching assistant and a researcher in his faculty. His current research interests include deep learning, machine learning, artificial intelligence, digital signal processing, and computer vision.

Original. Reposted with permission.

Related: