OpenCV的VideoCapture(0)是從執行程式的電腦開啟攝像機,當你在Colab伺服器上執行程式時,Colab伺服器並沒有攝像機,因此VideoCapture(0)無法在Colab中運行,無法從本機攝影機獲取圖像。因為Colab是在瀏覽器中運行,可以利用JavaScript來開啟本機攝影機。
擷取攝影機圖像
Colab有提供擷取本機攝影機圖像的示範程式碼:在`Colab專案頁面點選「程式碼片斷」鈕,再點選「Camera Capture」項目右方的箭頭,就會將擷取本機攝影機圖像的程式碼加入新的程式碼儲存格。
執行後會開啟攝影機,使用者按「Capture」鈕會擷取當時攝影機畫面圖像。
擷取的圖像存於Colab的<photo.jpg>檔,點選右方功能鈕再按「下載」可將圖形檔存於本機。
程式碼為:
1 from IPython.display import display, Javascript
2 from google.colab.output import eval_js
3 from base64 import b64decode
4
5 def take_photo(filename='photo.jpg', quality=0.8):
6 js = Javascript('''
7 async function takePhoto(quality) {
8 const div = document.createElement('div');
9 const capture = document.createElement('button');
10 capture.textContent = 'Capture';
11 div.appendChild(capture);
12
13 const video = document.createElement('video');
14 video.style.display = 'block';
15 const stream = await navigator.mediaDevices.getUserMedia({video: true});
16
17 document.body.appendChild(div);
18 div.appendChild(video);
19 video.srcObject = stream;
20 await video.play();
21
22 // Resize the output to fit the video element.
23 google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);
24
25 // Wait for Capture to be clicked.
26 await new Promise((resolve) => capture.onclick = resolve);
27
28 const canvas = document.createElement('canvas');
29 canvas.width = video.videoWidth;
30 canvas.height = video.videoHeight;
31 canvas.getContext('2d').drawImage(video, 0, 0);
32 stream.getVideoTracks()[0].stop();
33 div.remove();
34 return canvas.toDataURL('image/jpeg', quality);
35 }
36 ''')
37 display(js)
38 data = eval_js('takePhoto({})'.format(quality))
39 binary = b64decode(data.split(',')[1])
40 with open(filename, 'wb') as f:
41 f.write(binary)
42 return filename
43
44 from IPython.display import Image
45 try:
46 filename = take_photo()
47 print('Saved to {}'.format(filename))
48
49 # Show the image which was just taken.
50 display(Image(filename))
51 except Exception as err:
52 # Errors will be thrown if the user does not have a webcam or if they do not
53 # grant the page permission to access it.
54 print(str(err))
第5-42列為擷取攝影機圖像的函式:參數filename為儲存圖片的檔名,quality為圖像品質。
第6-36列為擷取攝影機圖像並傳回圖像的Javascript程式。
第8-11列建立「Capture」按鈕。
第13-15列開啟攝影機。
第17-20列等待攝影機啟動完成。
第23列調整攝影機畫面顯示區域,此時就會顯示攝影機動態畫面。
第26列等待使用者按「Capture」鈕,使用者按「Capture」鈕後才會繼續執行28-34列。
第28-31列擷取攝影機畫面。
第32-33列關閉攝影機畫面。
第34列傳回擷取的攝影機畫面。
第37-38列執行Javascript程式碼並取得傳回的擷取攝影機圖像。
第39列解碼擷取的攝影機圖像成二進位資料。
第40-41列儲存圖片檔案。
第42列傳回圖片檔名。
第46-47列在主程式取得並顯示圖片檔名。
第50列顯示擷取的攝影機圖像。
應用:錄影及播放影片
攝影機最重要的功能就是錄製影片,因此利用前面範例程式撰寫錄製影片程式。
執行後會開啟攝影機,使用者按「開始錄影」鈕開始錄製影片,同時按鈕文字變為「停止錄影」,使用者按「停止錄影」鈕結束錄製影片。
擷取的圖像存於Colab的<record.mp4>檔,點選右方功能鈕再按「下載」可將錄影檔存於本機。
錄影程式碼為:
1 from IPython.display import display, Javascript,HTML
2 from google.colab.output import eval_js
3 from base64 import b64decode
4
5 def record_video(filename):
6 js=Javascript("""
7 async function recordVideo() {
8 const options = { mimeType: "video/webm; codecs=vp9" };
9 const div = document.createElement('div');
10 const capture = document.createElement('button');
11 capture.textContent = "開始錄影";
12 capture.style.background = "orange";
13 capture.style.color = "white";
14 div.appendChild(capture);
15
16 const stopCapture = document.createElement("button");
17 stopCapture.textContent = "停止錄影";
18 stopCapture.style.background = "red";
19 stopCapture.style.color = "white";
20
21 const video = document.createElement('video');
22 const recordingVid = document.createElement("video");
23 video.style.display = 'block';
24 const stream = await navigator.mediaDevices.getUserMedia({audio:true, video: true});
25
26 let recorder = new MediaRecorder(stream, options);
27 document.body.appendChild(div);
28 div.appendChild(video);
29 video.srcObject = stream;
30 video.muted = true;
31 await video.play();
32 google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);
33
34 await new Promise((resolve) => {capture.onclick = resolve; });
35 recorder.start();
36 capture.replaceWith(stopCapture);
37
38 await new Promise((resolve) => stopCapture.onclick = resolve);
39 recorder.stop();
40 let recData = await new Promise((resolve) => recorder.ondataavailable = resolve);
41 let arrBuff = await recData.data.arrayBuffer();
42 stream.getVideoTracks()[0].stop();
43 div.remove();
44
45 let binaryString = "";
46 let bytes = new Uint8Array(arrBuff);
47 bytes.forEach((byte) => {
48 binaryString += String.fromCharCode(byte);
49 })
50 return btoa(binaryString);
51 }
52 """)
53 try:
54 display(js)
55 data=eval_js('recordVideo({})')
56 binary=b64decode(data)
57 with open(filename,"wb") as video_file:
58 video_file.write(binary)
59 print(f"錄影檔案:{filename}")
60 except Exception as err:
61 print(str(err))
62
63 video_path = "record.mp4"
64 record_video(video_path)
第6-52列錄影Javascript程式碼。
第10-14列建立「開始錄影」按鈕。
第16-19列建立「停止錄影」按鈕。
第21-32列啟動攝影機拍攝。
第26列以MediaRecorder建立錄製影片物件。
第34列等待使用者按「開始錄影」鈕。
第35列開始錄影。
第36列將按鈕變為「停止錄影」鈕。
第38列等待使用者按「停止錄影」鈕。
第39列停止錄影。
第40-41列取得錄影資料。
第42-43列關閉攝影機畫面。
第45-49列將錄影資料轉為二進位。
第50列傳回錄影資料。
第54-55列執行Javascript程式碼並取得傳回的錄影資料。
第56-61列儲存錄影檔案。
下面程式會播放錄製的影片檔:
1 from IPython.display import HTML
2 from base64 import b64encode
3
4 video_path = "record.mp4"
5 video_width = 600
6 video_file = open(video_path, "r+b").read()
7 video_url = f"data:video/mp4;base64,{b64encode(video_file).decode()}"
8 HTML(f"""<video width={video_width} controls><source src="{video_url}"></video>""")
逐一處理CAM畫面
實務上常要處理攝影機每幀畫面,例如臉部辨識時要對每一個畫面進行辨識,以框選出臉部。下面程式會取得每一個攝影畫面進行處理(不再解說程式),但因為程式是在Colab執行,必須將每一個攝影畫面上傳才能處理,因此處理速度很慢,畫面停格的很嚴重。
臉部辨識程式碼為:
from IPython.display import display, Javascript
from google.colab.output import eval_js
from base64 import b64decode, b64encode
import numpy as np
import cv2
def init_camera():
js = Javascript('''
var div = null;
var video = null;
var stream = null;
var canvas = null;
var img = null;
async function initCamera() {
div = document.createElement('div');
document.body.appendChild(div);
video = document.createElement('video');
video.style.display = 'block';
div.appendChild(video);
stream = await navigator.mediaDevices.getUserMedia({video: true});
video.srcObject = stream;
await video.play();
google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);
canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
img = document.createElement('img');
img.width = video.videoWidth;
img.height = video.videoHeight;
div.appendChild(img);
}
async function takeImage(quality) {
canvas.getContext('2d').drawImage(video, 0, 0);
return canvas.toDataURL('image/jpeg', quality);
}
async function showImage(image) {
img.src = image;
}
''')
display(js)
eval_js('initCamera()')
def take_frame(quality=0.8):
data = eval_js('takeImage({})'.format(quality))
data = data.split(',')[1]
data = b64decode(data)
data = np.frombuffer(data, dtype=np.uint8)
img = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
return img
def show_frame(img, quality=0.8):
ret, data = cv2.imencode('.jpg', img)
data = b64encode(data)
data = data.decode()
data = 'data:image/jpg;base64,' + data
eval_js('showImage("{}")'.format(data))
face_cascade = cv2.CascadeClassifier(os.path.join(cv2.data.haarcascades, 'haarcascade_frontalface_default.xml'))
init_camera()
while True:
try:
img = take_frame()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
show_frame(img)
except Exception as err:
print('Exception:', err)
沒有留言:
張貼留言