{"id":157,"date":"2018-08-18T10:13:00","date_gmt":"2018-08-18T10:13:00","guid":{"rendered":"http:\/\/doctor-dark.co.uk\/blog\/2018\/08\/18\/recent-python-door-cam-code-ugly-but-works\/"},"modified":"2023-12-18T12:19:02","modified_gmt":"2023-12-18T12:19:02","slug":"recent-python-door-cam-code-ugly-but-works","status":"publish","type":"post","link":"https:\/\/doctor-dark.co.uk\/blog\/recent-python-door-cam-code-ugly-but-works\/","title":{"rendered":"Recent python door cam code &#8211; ugly, but works."},"content":{"rendered":"<div dir=\"ltr\" style=\"text-align: left;\">\n<b>Updated version of the code&#8230;<\/b><br \/>\n<b>It deals with the occasional hangs in the camera capture process by<\/b><br \/>\n<b>rebooting when they happen. A line in \/etc\/rc.local runs the program again.<\/b><\/p>\n<p># Program for PiDoorCam<\/p>\n<p># Detects motion, and when it spots some, takes a high resolution<br \/>\n# picture, and sends the picture to PiScreen<\/p>\n<p>import io<br \/>\nimport os<br \/>\nimport picamera<br \/>\nimport ftplib<br \/>\nimport time<br \/>\nfrom datetime import datetime<br \/>\nfrom PIL import Image<br \/>\nimport requests<\/p>\n<p>camera = picamera.PiCamera()<br \/>\npicamera.PiCamera.CAPTURE_TIMEOUT = 30<\/p>\n<p># If we detect 100 pixels that changed by 30, we have seen movement.<br \/>\n#difference = 30<br \/>\n#pixels = 100<br \/>\n# Desensitise!<br \/>\n#difference = 30<br \/>\ndifference = 50<br \/>\npixels = 150<\/p>\n<p># May as well use the maximum resolution of the camera.<br \/>\n# This is for V1. V2 is 3280 x 2464.<br \/>\nwidth = 2592<br \/>\nheight = 1944<\/p>\n<p># I copied this voodoo motion detection from somewhere. Changed the timeout<br \/>\n#&nbsp; setting above to prevent the occasional failures to complete captures.<br \/>\ndef compare():<br \/>\n&nbsp; &nbsp;camera.resolution = (100, 75)<br \/>\n&nbsp; &nbsp;stream = io.BytesIO()<br \/>\n&nbsp; &nbsp;format = &#8216;bmp&#8217;<br \/>\n&nbsp; &nbsp;# Handle occasional &#8216;Timed out waiting for capture to end&#8217;<br \/>\n&nbsp; &nbsp;try:<br \/>\n&nbsp; &nbsp; &nbsp; camera.capture(stream, format)<br \/>\n&nbsp; &nbsp;except:<br \/>\n&nbsp; &nbsp; &nbsp; print (&#8220;Camera timed out, reboot needed!&#8221;)<br \/>\n&nbsp; &nbsp; &nbsp; os.system(&#8220;sudo reboot now&#8221;)<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; <br \/>\n&nbsp; &nbsp;stream.seek(0)<br \/>\n&nbsp; &nbsp;im = Image.open(stream)<br \/>\n&nbsp; &nbsp;buffer = im.load()<br \/>\n&nbsp; &nbsp;stream.close()<br \/>\n&nbsp; &nbsp;return im, buffer<\/p>\n<p># Function to take a new high resolution picture, send it to PiScreen<br \/>\n# send it to my phone, and then delete it.<br \/>\ndef newimage(width, height):<br \/>\n&nbsp; &nbsp; when = datetime.now()<br \/>\n&nbsp; &nbsp; filename = &#8220;door-%04d%02d%02d-%02d%02d%02d.jpg&#8221; % (when.year, when.month, when.day, when.hour, when.minute, when.second)<br \/>\n&nbsp; &nbsp; camera.resolution = (width, height)<br \/>\n&nbsp; &nbsp; camera.capture(filename)<\/p>\n<p>&nbsp; &nbsp; connected = True<br \/>\n&nbsp; &nbsp; ftp = ftplib.FTP()<br \/>\n&nbsp; &nbsp; ftp.connect(&#8220;PiScreen&#8221;)<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; try:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ftp.login(&#8220;pi&#8221;,&#8221;******************&#8221;)<br \/>\n&nbsp; &nbsp; except ftplib.all_errors:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; connected = False<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print (&#8220;Failed to connect to server %s&#8221; % e)&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; if connected:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ftp.storbinary(&#8216;STOR &#8216;+filename, open(filename, &#8220;rb&#8221;))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print (&#8220;Sent &#8220;, filename)<\/p>\n<p>&nbsp; &nbsp; ftp.quit()<\/p>\n<p># Code to send the Pushover message. Make picture smaller first.<br \/>\n&nbsp; &nbsp; im = Image.open(filename)<br \/>\n&nbsp; &nbsp; im.resize((324,243),Image.ANTIALIAS)<br \/>\n&nbsp; &nbsp; im.save(filename)<br \/>\n&nbsp; &nbsp;<br \/>\n&nbsp; &nbsp; r = requests.post(&#8220;https:\/\/api.pushover.net\/1\/messages.json&#8221;, data = {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;token&#8221;: &#8220;***********************************&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;user&#8221;: &#8220;*************************************&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;device&#8221;: &#8220;***********&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;sound&#8221;: &#8220;intermission&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;message&#8221;: filename<br \/>\n&nbsp; &nbsp; },<br \/>\n&nbsp; &nbsp; files = {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;attachment&#8221;: (filename, open(filename, &#8220;rb&#8221;), &#8220;image\/jpeg&#8221;)<br \/>\n&nbsp; &nbsp; })<br \/>\n# Check r for problems &#8211; maybe put a delay here?<br \/>\n&nbsp; &nbsp; if r.status_code != 200:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print(&#8220;Pushover message failed.&#8221;)<br \/>\n&nbsp; &nbsp; else:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print(&#8220;Pushover accepted the message.&#8221;)<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; <br \/>\n# Now delete the file.<br \/>\n&nbsp; &nbsp; os.remove(filename)<br \/>\n&nbsp; &nbsp; # Delay to avoid being nasty to Pushover server.<br \/>\n&nbsp; &nbsp; time.sleep(5)<\/p>\n<p># Main program.<\/p>\n<p>camera.rotation = 180<br \/>\nprint(&#8220;Running door.py&#8221;)<br \/>\nimage1, buffer1 = compare()<\/p>\n<p>newimage(width, height)<\/p>\n<p>while (True):<\/p>\n<p>&nbsp; &nbsp;image2, buffer2 = compare()<\/p>\n<p>&nbsp; &nbsp;changedpixels = 0<br \/>\n&nbsp; &nbsp;for x in range(0, 100):<br \/>\n&nbsp; &nbsp; &nbsp; for y in range(0, 75):<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pixdiff = abs(buffer1[x,y][1] &#8211; buffer2[x,y][1])<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if pixdiff &gt; difference:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; changedpixels += 1<\/p>\n<p>&nbsp; &nbsp;if changedpixels &gt; pixels:<br \/>\n&nbsp; &nbsp; &nbsp; newimage(width, height)<\/p>\n<p>&nbsp; &nbsp;image1 = image2<br \/>\n&nbsp; &nbsp;buffer1 = buffer2<\/p>\n<p>\n<b><br \/><\/b><br \/>\n<b>Code below is an older version, the &#8220;error handling&#8221; doesn&#8217;t actually work.<\/b><\/p>\n<p># Program for PiDoorCam<\/p>\n<p># Detects motion, and when it spots some, takes a high resolution<br \/>\n# picture, and sends the picture to PiScreen<\/p>\n<p>import io<br \/>\nimport os<br \/>\nimport picamera<br \/>\nimport ftplib<br \/>\nimport time<br \/>\nfrom datetime import datetime<br \/>\nfrom PIL import Image<br \/>\nimport requests<\/p>\n<p>camera = picamera.PiCamera()<br \/>\npicamera.PiCamera.CAPTURE_TIMEOUT = 30<\/p>\n<p># If we detect 100 pixels that changed by 30, we have seen movement.<br \/>\n#difference = 30<br \/>\n#pixels = 100<br \/>\n# Desensitise!<br \/>\ndifference = 30<br \/>\npixels = 150<\/p>\n<p># May as well use the maximum resolution of the camera.<br \/>\n# This is for V1. V2 is 3280 x 2464.<br \/>\nwidth = 2592<br \/>\nheight = 1944<\/p>\n<p># I copied this voodoo motion detection from somewhere. Changed the timeout<br \/>\n#&nbsp; setting above to prevent the occasional failures to complete captures.<br \/>\ndef compare():<br \/>\n&nbsp; &nbsp;camera.resolution = (100, 75)<br \/>\n&nbsp; &nbsp;stream = io.BytesIO()<br \/>\n&nbsp; &nbsp;format = &#8216;bmp&#8217;<br \/>\n&nbsp; &nbsp;# Handle occasional &#8216;Timed out waiting for capture to end&#8217;<br \/>\n&nbsp; &nbsp;try:<br \/>\n&nbsp; &nbsp; &nbsp; camera.capture(stream, format)<br \/>\n&nbsp; &nbsp;except:<br \/>\n&nbsp; &nbsp; &nbsp; print (&#8220;Retrying camera.capture()&#8221;)<br \/>\n&nbsp; &nbsp; &nbsp; camera.capture(stream, format)<br \/>\n&nbsp; <br \/>\n&nbsp; &nbsp;stream.seek(0)<br \/>\n&nbsp; &nbsp;im = Image.open(stream)<br \/>\n&nbsp; &nbsp;buffer = im.load()<br \/>\n&nbsp; &nbsp;stream.close()<br \/>\n&nbsp; &nbsp;return im, buffer<\/p>\n<p># Function to take a new high resolution picture, send it to PiScreen<br \/>\n# send it to my phone, and then delete it.<br \/>\ndef newimage(width, height):<br \/>\n&nbsp; &nbsp; when = datetime.now()<br \/>\n&nbsp; &nbsp; filename = &#8220;door-%04d%02d%02d-%02d%02d%02d.jpg&#8221; % (when.year, when.month, when.day, when.hour, when.minute, when.second)<br \/>\n&nbsp; &nbsp; camera.resolution = (width, height)<br \/>\n&nbsp; &nbsp; camera.capture(filename)<\/p>\n<p>&nbsp; &nbsp; connected = True<br \/>\n&nbsp; &nbsp; ftp = ftplib.FTP()<br \/>\n&nbsp; &nbsp; ftp.connect(&#8220;PiScreen&#8221;)<br \/>\n&nbsp; <br \/>\n&nbsp; &nbsp; try:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ftp.login(&#8220;pi&#8221;,&#8221;**********************************&#8221;)<br \/>\n&nbsp; &nbsp; except ftplib.all_errors:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; connected = False<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print (&#8220;Failed to connect to server %s&#8221; % e)&nbsp; <br \/>\n&nbsp; &nbsp; &nbsp; <br \/>\n&nbsp; &nbsp; if connected:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; ftp.storbinary(&#8216;STOR &#8216;+filename, open(filename, &#8220;rb&#8221;))<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print (&#8220;Sent &#8220;, filename)<\/p>\n<p>&nbsp; &nbsp; ftp.quit()<\/p>\n<p># Code to send the Pushover message. Make picture smaller first.<br \/>\n&nbsp; &nbsp; im = Image.open(filename)<br \/>\n#&nbsp; &nbsp; im.resize((648,486),Image.ANTIALIAS)<br \/>\n&nbsp; &nbsp; im.resize((324,243),Image.ANTIALIAS)<br \/>\n&nbsp; &nbsp; im.save(filename)<br \/>\n&nbsp; <br \/>\n&nbsp; &nbsp; r = requests.post(&#8220;https:\/\/api.pushover.net\/1\/messages.json&#8221;, data = {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;token&#8221;: &#8220;**********************************&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;user&#8221;: &#8220;********************************&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;device&#8221;: &#8220;************&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;sound&#8221;: &#8220;intermission&#8221;,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;message&#8221;: filename<br \/>\n&nbsp; &nbsp; },<br \/>\n&nbsp; &nbsp; files = {<br \/>\n#&nbsp; &nbsp; &nbsp; &nbsp; &#8220;attachment&#8221;: (&#8220;image.jpg&#8221;, open(filename, &#8220;rb&#8221;), &#8220;image\/jpeg&#8221;)<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &#8220;attachment&#8221;: (filename, open(filename, &#8220;rb&#8221;), &#8220;image\/jpeg&#8221;)<br \/>\n&nbsp; &nbsp; })<br \/>\n# Check r for problems &#8211; maybe put a delay here?<br \/>\n&nbsp; &nbsp; if r.status_code != 200:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print(&#8220;Pushover message failed.&#8221;)<br \/>\n&nbsp; &nbsp; else:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; print(&#8220;Pushover accepted the message.&#8221;)<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; <br \/>\n# Now delete the file.<br \/>\n&nbsp; &nbsp; os.remove(filename)<br \/>\n&nbsp; &nbsp; # Delay to avoid being nasty to Pushover server.<br \/>\n&nbsp; &nbsp; time.sleep(5)<\/p>\n<p># Main program.<\/p>\n<p>camera.rotation = 180<br \/>\nprint(&#8220;Running door.py&#8221;)<br \/>\nimage1, buffer1 = compare()<\/p>\n<p>newimage(width, height)<\/p>\n<p>while (True):<\/p>\n<p>&nbsp; &nbsp;image2, buffer2 = compare()<\/p>\n<p>&nbsp; &nbsp;changedpixels = 0<br \/>\n&nbsp; &nbsp;for x in range(0, 100):<br \/>\n&nbsp; &nbsp; &nbsp; for y in range(0, 75):<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pixdiff = abs(buffer1[x,y][1] &#8211; buffer2[x,y][1])<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if pixdiff &gt; difference:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; changedpixels += 1<\/p>\n<p>&nbsp; &nbsp;if changedpixels &gt; pixels:<br \/>\n&nbsp; &nbsp; &nbsp; newimage(width, height)<\/p>\n<p>&nbsp; &nbsp;image1 = image2<br \/>\n&nbsp; &nbsp;buffer1 = buffer2<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Updated version of the code&#8230; It deals with the occasional hangs in the camera capture process by rebooting when they happen. A line in \/etc\/rc.local runs the program again. # Program for PiDoorCam # Detects motion, and when it spots some, takes a high resolution # picture, and sends the picture to PiScreen import io &hellip; <a href=\"https:\/\/doctor-dark.co.uk\/blog\/recent-python-door-cam-code-ugly-but-works\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Recent python door cam code &#8211; ugly, but works.&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-157","post","type-post","status-publish","format-standard","hentry","category-raspberry-pi"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/posts\/157","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/comments?post=157"}],"version-history":[{"count":1,"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/posts\/157\/revisions"}],"predecessor-version":[{"id":426,"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/posts\/157\/revisions\/426"}],"wp:attachment":[{"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/media?parent=157"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/categories?post=157"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/doctor-dark.co.uk\/blog\/wp-json\/wp\/v2\/tags?post=157"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}