Hi, I'm Andy!

CS + HCI at Carnegie Mellon, open for collaboration.

Privacy screen

Published September 09, 2020


Howdy! I saw a meme when I was in high school that was a picture of a man. Superimposed onto his bottom text was the phrase "This meme is a red square when you're not looking," the joke being that you wouldn't be able to see it if you weren't looking. Anyway, since then I've had this desire to really implement it, to make some kind of billboard/house sign that nonresidents wouldn't be able to see, perhaps to display the weekly dinner schedule or WiFi password.

Using a Raspberry Pi, raspicam, and downloading dlib's face recognition models, we were able to get it to run at 0.5 FPS. When someone it recognized from the "VIP Faces" folder was in view of the camera, it would turn the screen back on. Otherwise, the Raspberry Pi would black out the screen using some display power management functions invoked from the command.

This FPS is a little useless since it can't black out the screen fast enough, so I ported it over to my Mac. Here, it runs at \~10 FPS, so it can feasibly block out someone trying to view your screen. Another benefit is it blacks out the screen while you aren't looking at it (since it can't see your face), saving power (Assuming the display turn off and turn on don't cost any extra energy, which they probably do). Anyway, it only took a few hours to write, so here's all the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import face_recognition
import cv2
import subprocess as sp
import os
import glob
import numpy as np
from functools import reduce

# Start webcam stream
video_capture = cv2.VideoCapture(0)

# Load approved users
known_face_encodings = dict()

rootdir = os.path.dirname(__file__) + "/faces/"
for fl in os.listdir(rootdir) :
    if fl.endswith(".jpg") or fl.endswith(".jpeg") or fl.endswith(".png") :
        image = face_recognition.load_image_file(rootdir + fl)
        known_face_encodings[fl] = face_recognition.face_encodings(image)[0]
known_face_encodings = list(known_face_encodings.values())

# Initialize some variables
new_face_locations = []
new_face_encodings = []
turned_off = False
print('Starting privacy screen')

while True:
    # Grab a single frame of video
    ret, frame = video_capture.read()

    # Resize frame of video to 1/4 size for faster face recognition processing
    small_frame = cv2.resize(frame, (0, 0), fx=0.2, fy=0.2)

    # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
    rgb_small_frame = small_frame[:, :, ::-1]

    # Find all the faces and face encodings in the current frame of video
    new_face_locations = face_recognition.face_locations(rgb_small_frame)
    new_face_encodings = face_recognition.face_encodings(rgb_small_frame, new_face_locations)

    matched = True

    # Loop over each face found in the frame to see if it's someone we know.
    for new_face_encoding in new_face_encodings:
        match = face_recognition.compare_faces(known_face_encodings, new_face_encoding)
        matched = matched and bool(sum(match))

    matched = matched and (len(new_face_locations) > 0) # Make sure someone's there


    # Depending on the state of matched, toggle the screen on or off
    if not matched:
        # sp.run(["xset","dpms","force","off"]) # Debian, for Raspi
        sp.run(["pmset", "displaysleepnow"]) # MacOS
        turned_off = True
    elif turned_off:
        turned_off = False
        # sp.run(["xset","dpms","force","on"]) # Debian, for Raspi

        sp.run(["caffeinate", "-u", "-t", "1"]) # MacOS, has to run twice to be reliable
        sp.run(["caffeinate", "-u", "-t", "1"])


That's all! Pretty simple program, but taught me some stuff about display power management and the trickiness of installing dlib. Also, writing this post taught me about HTML formatting (hilite.me is kinda nice)