From 7c940fba67e28a4101ec51f055d6214d2d90ce70 Mon Sep 17 00:00:00 2001 From: Floris Date: Thu, 5 May 2022 07:08:12 -0400 Subject: [PATCH 1/2] Added functionality to take screenshots during stream --- .gitignore | 4 +++ README.md | 11 ++++---- camera.py | 23 +++++++++++---- main.py | 11 ++++---- templates/index.html | 66 ++++++++++++++++++++++++++++++++++---------- 5 files changed, 83 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index cf4e265..747fae6 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,7 @@ dev.env #_pycache __pycache__/ +#image_files +*.jpg +*.jpeg +*.png diff --git a/README.md b/README.md index bebd740..653cdc0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Create your own live stream from a Raspberry Pi using the Pi camera module. Buil The Pi streams the output of the camera module over the web via Flask. Devices connected to the same network would be able to access the camera stream via ``` - + ``` ## Screenshots @@ -24,12 +24,12 @@ The Pi streams the output of the camera module over the web via Flask. Devices c Install the following dependencies to create camera stream. ``` -sudo apt-get update +sudo apt-get update sudo apt-get upgrade sudo apt-get install libatlas-base-dev sudo apt-get install libjasper-dev -sudo apt-get install libqtgui4 +sudo apt-get install libqtgui4 sudo apt-get install libqt4-test sudo apt-get install libhdf5-dev @@ -39,10 +39,9 @@ sudo pip3 install opencv-contrib-python sudo pip3 install imutils sudo pip3 install opencv-python - ``` -Note: This installation of opencv may take a while depending on your pi model. +Note: This installation of opencv may take a while depending on your pi model. OpenCV alternate installation (in the event of failed opencv builds): @@ -79,7 +78,7 @@ Go the end of the and add the following (from above): sudo python3 /home/pi/pi-camera-stream-flask/main.py ``` -This would cause the following terminal command to auto-start each time the Raspberry Pi boots up. This in effect creates a headless setup - which would be accessed via SSH. +This would cause the following terminal command to auto-start each time the Raspberry Pi boots up. This in effect creates a headless setup - which would be accessed via SSH. Note: make sure SSH is enabled. ## More Projects / Next Steps diff --git a/camera.py b/camera.py index 6992fa0..7f30692 100644 --- a/camera.py +++ b/camera.py @@ -2,16 +2,19 @@ #Date: 27.09.20 #Desc: This scrtipt script.. -import cv2 +import cv2 as cv from imutils.video.pivideostream import PiVideoStream import imutils import time +from datetime import datetime import numpy as np class VideoCamera(object): - def __init__(self, flip = False): - self.vs = PiVideoStream().start() - self.flip = flip + def __init__(self, flip = False, file_type = ".jpg", photo_string= "stream_photo"): + self.vs = PiVideoStream(resolution=(1920, 1080), framerate=30).start() + self.flip = flip # Flip frame vertically + self.file_type = file_type # image type i.e. .jpg + self.photo_string = photo_string # Name to save the photo time.sleep(2.0) def __del__(self): @@ -24,5 +27,13 @@ class VideoCamera(object): def get_frame(self): frame = self.flip_if_needed(self.vs.read()) - ret, jpeg = cv2.imencode('.jpg', frame) - return jpeg.tobytes() \ No newline at end of file + ret, jpeg = cv.imencode(self.file_type, frame) + self.previous_frame = jpeg + return jpeg.tobytes() + + # Take a photo, called by camera button + def take_picture(self): + frame = self.flip_if_needed(self.vs.read()) + ret, image = cv.imencode(self.file_type, frame) + today_date = datetime.now().strftime("%m%d%Y-%H%M%S") # get current time + cv.imwrite(str(self.photo_string + "_" + today_date + self.file_type), frame) diff --git a/main.py b/main.py index 91547d5..5ccfe87 100644 --- a/main.py +++ b/main.py @@ -5,8 +5,6 @@ # import the necessary packages from flask import Flask, render_template, Response, request from camera import VideoCamera -import time -import threading import os pi_camera = VideoCamera(flip=False) # flip pi camera if upside down. @@ -30,9 +28,12 @@ def video_feed(): return Response(gen(pi_camera), mimetype='multipart/x-mixed-replace; boundary=frame') +# Take a photo when pressing camera button +@app.route('/picture') +def take_picture(): + pi_camera.take_picture() + return "None" + if __name__ == '__main__': app.run(host='0.0.0.0', debug=False) - - - diff --git a/templates/index.html b/templates/index.html index 2c7aaf3..b7dc167 100644 --- a/templates/index.html +++ b/templates/index.html @@ -25,7 +25,7 @@ body { text-align: center; padding: 14px 16px; text-decoration: none; - font-size: 17px; + font-size: 17px; } .navbar a:hover { @@ -43,7 +43,7 @@ body { } -.camera-movement{ +.camera-movement{ float: none; position: absolute; top: 50%; @@ -60,7 +60,7 @@ i.fa { border-radius: 60px; box-shadow: 0px 0px 2px #888; padding: 0.5em 0.6em; - + } @@ -79,12 +79,12 @@ button { overflow: hidden; outline:none; } - + //CSS .camera-bg { - position: fixed; - top: 0; - left: 0; + position: fixed; + top: 0; + left: 0; /* Preserve aspet ratio */ min-width: 100%; @@ -132,11 +132,11 @@ body {
- +
@@ -144,20 +144,29 @@ body { + + - + + + - From 71e26f8b5df08ef319b791525ecb948df5b2571d Mon Sep 17 00:00:00 2001 From: Floris Date: Thu, 5 May 2022 07:40:27 -0400 Subject: [PATCH 2/2] added icon for extra fun --- camera.py | 3 ++- main.py | 2 +- static/favicon.ico | Bin 0 -> 15086 bytes templates/index.html | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 static/favicon.ico diff --git a/camera.py b/camera.py index 7f30692..f70338e 100644 --- a/camera.py +++ b/camera.py @@ -11,7 +11,8 @@ import numpy as np class VideoCamera(object): def __init__(self, flip = False, file_type = ".jpg", photo_string= "stream_photo"): - self.vs = PiVideoStream(resolution=(1920, 1080), framerate=30).start() + # self.vs = PiVideoStream(resolution=(1920, 1080), framerate=30).start() + self.vs = PiVideoStream().start() self.flip = flip # Flip frame vertically self.file_type = file_type # image type i.e. .jpg self.photo_string = photo_string # Name to save the photo diff --git a/main.py b/main.py index 5ccfe87..603aa34 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ #Desc: This web application serves a motion JPEG stream # main.py # import the necessary packages -from flask import Flask, render_template, Response, request +from flask import Flask, render_template, Response, request, send_from_directory from camera import VideoCamera import os diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..dcdebc4e892387070ebf34f60324dd862f89a1a1 GIT binary patch literal 15086 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 bnpIt1a!Vxg^BL9Ua^!0C5{p(7^ilo?hq&Ej literal 0 HcmV?d00001 diff --git a/templates/index.html b/templates/index.html index b7dc167..07bc3e7 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,6 +1,7 @@ +