# 下載範例程式
~$ wget http://max543.com/debugger/class/python02/人臉辨識與物體辨識/code/ch5.zip
~$ unzip ch5.zip
### Lab5-1 ###
# 啟動 RTSP Server
~$ raspivid -o - -t 0 -hf --rotation 180 -w 320 -h 240 -fps 15 | cvlc -vvv stream:///dev/stdin --sout '#rtp{sdp=rtsp://:8554}' :demux=h264
# 找出佔用的 Port Number 的 PID
~$ sudo netstat -lpn |grep 8554
# 刪除佔用的 PID
~$ sudo kill -9 PID
### Lab5-2 ###
# VLC media player
https://get.videolan.org/vlc/3.0.14/win64/vlc-3.0.14-win64.exe
### Lab5-3 ###
# 安裝輕量級的 RTSP Server
~$ cd ~
~$ git clone https://github.com/mpromonet/h264_v4l2_rtspserver.git
~$ sudo apt-get install liblivemedia-dev libv4l-dev cmake
~$ cd h264_v4l2_rtspserver
~/h264_v4l2_rtspserver$ cmake .
~/h264_v4l2_rtspserver$ make -j4
~/h264_v4l2_rtspserver$ sudo ./v4l2rtspserver -F 15 -W 800 -H 600 -P 8554 /dev/video0
# 沒有支援旋轉鏡頭參數的指令,也可以用以下的指令:
~$ v4l2-ctl --set-ctrl vertical_flip=1
### Lab5-4 ###
~$ pip install Flask
### Lab5-5 (app.py) ###
from flask import Flask
app = Flask(__name__) # Flask 類別初始化時傳入的 __name__ 參數,代表當前模組的名稱。是固定用法,以便讓 Flask 知道在哪裡尋找資源 (template__folder 或 static_folder 資料夾位置)
@app.route('/') # 定義路由路徑,告訴 Flask,哪個 URL 應該觸發我們的函式。/ 指的是 web root (URL) (又稱為 Controller)
def hello_world(): # 定義一個接著要觸發的函式
return 'Hello, World!' # 函式回傳的文字字串 (又稱為 View)
### Lab5-6 (5-1-app-hello.py) ###
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello Flask'
if __name__ == '__main__': # 是直接運行的才會執行 app.run() 這個方法
app.run(host = '0.0.0.0', port = 80, debug = True)
### Lab5-7 ###
# 查找哪一個 python 程序正在執行中
$ ps -fA | grep python
# 刪除 root 占用的程序
$ sudo kill -9 PID
### Lab5-8 ###
# 新增一個資料夾 templates,與一個網頁檔 link.html
~$ mkdir templates && cd templates
~/templates$ nano link.html
# link.html 的內容
Hello Template
foo
# 再新增一個 bar.html
~/templates$ nano bar.html
# bar.html 的內容
{% for ext in extns %} # 走訪容器
- {{ ext }}
{% endfor %}
### Lab5-9 (5-2-app-route.py) ###
from flask import Flask, render_template, Response # render_template 用來使用 html
app = Flask(__name__)
@app.route('/')
def index():
return render_template('link.html') # View 可由自訂的樣板產生
@app.route('/foo')
def foo():
extns = ['Flask', 'Jinja2', 'Awesome'] # 樣板內可塞變數
return render_template('bar.html', extns = extns) # View 的超連結,並顯示變數
if __name__ == '__main__':
app.run(host = '0.0.0.0', port = 80, debug = True)
### Lab5-10 (stream_pi.py) ###
from time import time
class Camera(object):
def __init__(self):
self.frames = [open('static/' + f + '.jpg', 'rb').read() for f in ['1', '2', '3']]
def get_frame(self): # get_frame 每三秒讀取一張圖片,並回傳該張 frame
return self.frames[int(time()) % 3]
### Lab5-11 (5-3-app-stream.py) ###
from flask import Flask, render_template, Response
from stream_pi import Camera
app = Flask(__name__)
@app.route('/')
def index():
return render_template('stream.html')
def gen(camera):
while True:
frame = camera.get_frame() # Camera 類別
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
@app.route('/video_feed')
def video_feed():
return Response(gen(Camera()), # 回傳內容
mimetype = 'multipart/x-mixed-replace; boundary = frame')
if __name__ == '__main__':
app.run(host = '0.0.0.0', port = 80, debug = True)
### Lab5-12 ###
# 新增一個 stream.html
$ nano templates/stream.html
# stream.html 的內容
Hello Stream
### Lab5-13 ###
import cv2
class Camera(object):
def __init__(self):
self.video = cv2.VideoCapture(0) # 開啟 /dev/videoX,預設的 camera 是 0
def __del__(self):
self.video.release()
def get_frame(self):
success, image = self.video.read() # V4L2 的 API
ret, jpeg = cv2.imencode('.jpg', image)
return jpeg.tostring()
### Lab5-14 (5-4-app-camera.py) ###
from flask import Flask, render_template, Response
from camera_pi import Camera
app = Flask(__name__)
@app.route('/')
def index():
return render_template('stream.html')
def gen(camera):
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
@app.route('/video_feed')
def video_feed():
return Response(gen(Camera()),
mimetype = 'multipart/x-mixed-replace; boundary = frame')
if __name__ == '__main__':
app.run(host = '0.0.0.0', port = 80, debug = True)
### Lab5-15 ###
~$ sudo pip install numpy==1.21.4
~$ sudo pip install opencv-python==4.5.4.60
~$ sudo pip install opencv-contrib-python==4.5.4.60
### Lab5-16 (camera_pi_v2.py) ###
import cv2
class Camera(object):
def __init__(self):
if cv2.__version__.startswith('2'): # 若是 OpenCV 是 2.X 的版本
PROP_FRAME_WIDTH = cv2.cv.CV_CAP_PROP_FRAME_WIDTH
PROP_FRAME_HEIGHT = cv2.cv.CV_CAP_PROP_FRAME_HEIGHT
elif cv2.__version__.startswith('3'): # 若是 OpenCV 是 3.X 的版本
PROP_FRAME_WIDTH = cv2.CAP_PROP_FRAME_WIDTH
PROP_FRAME_HEIGHT = cv2.CAP_PROP_FRAME_HEIGHT
else: # 若是 OpenCV 是其他的版本
PROP_FRAME_WIDTH = cv2.CAP_PROP_FRAME_WIDTH
PROP_FRAME_HEIGHT = cv2.CAP_PROP_FRAME_HEIGHT
self.video = cv2.VideoCapture(0)
#self.video = cv2.VideoCapture(1)
#self.video.set(PROP_FRAME_WIDTH, 640)
#self.video.set(PROP_FRAME_HEIGHT, 480)
self.video.set(PROP_FRAME_WIDTH, 320) # 改變視窗的寬
self.video.set(PROP_FRAME_HEIGHT, 240) # 改變視窗的高
def __del__(self):
self.video.release()
def get_frame(self):
success, image = self.video.read()
ret, jpeg = cv2.imencode('.jpg', image) # 調整 JPG 壓縮品質
return jpeg.tostring()
### Lab5-17 ###
~$ cd ~/ch05/project_opencv
~/project_opencv$ sudo python3 app-opencv.py
### Lab5-18 ###
~$ cd ~/ch05/project_opencv+line
~/project_opencv+line$ sudo python3 app-opencv+line.py