| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
1 |
# Written by Sudharshan 'Sup3rkiddo' S |
|
2 |
# released under WTFPL |
|
3 |
|
|
4 |
# I know a simple mirror would suffice. But screw mirrors. |
|
5 |
# The sound file taken from http://www.freesound.org/samplesViewSingle.php?id=72557 |
|
6 |
|
|
7 |
import datetime |
| a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
8 |
import time |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
9 |
from datetime import datetime |
|
10 |
from optparse import OptionParser |
|
11 |
|
|
12 |
|
|
13 |
NOTIFICATIONS = True |
|
14 |
try: |
|
15 |
import pynotify |
|
16 |
pynotify.init("Boss Alert") |
|
17 |
except: |
|
18 |
print "Pynotify not installed. Notifications will be disabled" |
|
19 |
NOTIFICATIONS = False |
|
20 |
|
|
21 |
|
|
22 |
import pygame |
|
23 |
import pygame.camera |
|
24 |
import pygame.font |
|
25 |
import pygame.mixer |
|
26 |
|
|
27 |
from pygame.locals import * |
|
28 |
|
|
29 |
|
|
30 |
class BossAlert(object): |
|
31 |
|
| a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
32 |
def __init__(self, soundpath=None, waitfor=0): |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
33 |
self.reference_color = None |
|
34 |
size = (640,480) |
|
35 |
pygame.camera.init() |
|
36 |
|
|
37 |
self.display = pygame.display.set_mode(size, 0) |
|
38 |
cams = pygame.camera.list_cameras() |
|
39 |
assert cams, "No Cameras detected. Get a mirror instead" |
|
40 |
|
|
41 |
self.notify = None |
|
42 |
if NOTIFICATIONS: |
|
43 |
self.notify = pynotify.Notification("BossAlert", "Your Boss just walked down the hallway. Back to work") |
|
44 |
self.notify.set_urgency(pynotify.URGENCY_CRITICAL) |
|
45 |
self.notify.set_timeout(5) |
|
46 |
|
|
47 |
self.cam = pygame.camera.Camera(cams[0], size, "RGB24") |
|
48 |
self.cam.start() |
|
49 |
print "Initializing Camera" |
|
50 |
|
|
51 |
self.surface = pygame.surface.Surface(size, 0, self.display) |
|
52 |
self.font = pygame.font.SysFont("Courier New", 30, bold=True) |
|
53 |
|
|
54 |
self.sound = None |
|
55 |
if soundpath: |
|
56 |
print "Init pygame mixer" |
|
57 |
pygame.mixer.init() |
|
58 |
self.sound = pygame.mixer.Sound(soundpath) |
| a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
59 |
self.waitfor = waitfor |
|
60 |
self.starttime = time.time() |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
61 |
self.refresh() |
|
62 |
|
|
63 |
|
|
64 |
def check_movement(self, rect, threshold=30): |
|
65 |
"""Really dumb way to check for movement. |
|
66 |
Find out the average color within the rectangular area. If it differs from |
|
67 |
the reference colour by a certain threshold, we have movement. |
|
68 |
Simple but effective in cases where the background colour is uniform. |
|
69 |
""" |
|
70 |
color = pygame.transform.average_color(self.surface, rect) |
|
71 |
diff = [a - b for a,b in zip(color, self.reference_color)] |
|
72 |
for d in diff: |
|
73 |
if abs(d) > threshold: |
|
74 |
return True |
|
75 |
return False |
|
76 |
|
|
77 |
|
|
78 |
def get_image(self, surface): |
|
79 |
success = self.cam.query_image() |
|
80 |
if success: |
|
81 |
return self.cam.get_image(surface) |
|
82 |
return None |
|
83 |
|
|
84 |
|
|
85 |
def refresh(self): |
|
86 |
snapshot = self.get_image(self.surface) |
| a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
87 |
if not snapshot: |
|
88 |
return |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
89 |
# Change the position of the rectangular window here. Or position your webcam accordingly |
|
90 |
rect = pygame.draw.rect(snapshot, (255, 0, 0), (10, 40, 100, 100), 2) |
|
91 |
|
|
92 |
if not self.reference_color: |
| a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
93 |
if time.time() - self.starttime < self.waitfor: |
|
94 |
return |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
95 |
self.reference_color = pygame.transform.average_color(snapshot, rect) |
| 4948d14 by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
96 |
|
|
97 |
# Play a sound and pop up a desktop notification if any motion is detected within the rect window |
|
98 |
if self.check_movement(rect): |
|
99 |
print "Movement detected at ", datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S") |
|
100 |
if not pygame.mixer.get_busy() and self.sound: |
|
101 |
self.sound.play(loops=-1) |
|
102 |
if NOTIFICATIONS: |
|
103 |
self.notify.show() |
|
104 |
snapshot.fill((255, 0, 0), rect) |
|
105 |
ren = self.font.render("Boss Alert", 30, (255, 0, 0)) |
|
106 |
snapshot.blit(ren, (400, 200)) |
|
107 |
else: |
|
108 |
if self.sound: |
|
109 |
self.sound.fadeout(500) |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
110 |
self.display.blit(snapshot, (0,0)) |
|
111 |
self.surface = snapshot |
|
112 |
pygame.display.flip() |
|
113 |
|
|
114 |
|
|
115 |
def startloop(self): |
|
116 |
while True: |
|
117 |
events = pygame.event.get() |
|
118 |
for e in events: |
|
119 |
if e.type == QUIT: |
|
120 |
self.cam.stop() |
|
121 |
exit() |
|
122 |
self.refresh() |
|
123 |
|
|
124 |
|
|
125 |
if __name__ == '__main__': |
|
126 |
pygame.init() |
|
127 |
oparser = OptionParser() |
|
128 |
oparser.add_option("-s", "--sound", dest="soundpath", |
|
129 |
help="Path to the Sound file to be played when movement is detected", metavar="FILE") |
|
130 |
oparser.add_option("-x", "--disable-notifications", dest="no_notify", action="store_true", default=False, |
|
131 |
help="Disable desktop notifications (Need pynotify to be installed)") |
| a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
132 |
oparser.add_option("-w", "--wait-for", dest="wait_for", default=0, |
|
133 |
help="The time (milliseconds) after which the reference color should be captured") |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
134 |
(options, args) = oparser.parse_args() |
|
135 |
NOTIFICATIONS = not options.no_notify |
| a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 |
136 |
alert = BossAlert(soundpath=options.soundpath, waitfor=int(options.wait_for)) |
| 6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 |
137 |
alert.startloop() |