目标
从摄像头捕获视频
通常,我们需要使用摄像头捕获实时视频流。在 OpenCV.js 中,我们使用 WebRTC 和 HTML canvas 元素来实现这一点。让我们从摄像头(内置或 USB)捕获视频,将其转换为灰度视频并显示出来。
要捕获视频,您需要在网页中添加一些 HTML 元素
- 一个 <video> 用于直接显示来自摄像头的视频
- 一个 <canvas> 用于逐帧将视频传输到 canvas ImageData
- 另一个 <canvas> 用于显示 OpenCV.js 获取的视频
首先,我们使用 WebRTC navigator.mediaDevices.getUserMedia 获取媒体流。
let video = document.getElementById("videoInput"); // video 是 video 标签的 id
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("发生错误! " + err);
});
- 注意
- 当您从视频文件捕获视频时,此函数是不必要的。但请注意,HTML video 元素仅支持 Ogg(Theora)、WebM(VP8/VP9) 或 MP4(H.264) 视频格式。
播放视频
现在,浏览器获取了摄像头流。然后,我们使用 Canvas 2D API 的 CanvasRenderingContext2D.drawImage() 方法将视频绘制到 canvas 上。最后,我们可以使用 图片入门 中的方法来读取和显示 canvas 中的图像。对于播放视频,应该每隔一定毫秒执行一次 cv.imshow()。我们建议使用 setTimeout() 方法。如果视频是 30fps,则延迟的毫秒数应该是 (1000/30 - processing_time)。
let canvasFrame = document.getElementById("canvasFrame"); // canvasFrame 是 <canvas> 的 id
let context = canvasFrame.getContext("2d");
let src = new cv.Mat(height, width, cv.CV_8UC4);
let dst = new cv.Mat(height, width, cv.CV_8UC1);
const FPS = 30;
function processVideo() {
let begin = Date.now();
context.drawImage(video, 0, 0, width, height);
src.data.set(context.getImageData(0, 0, width, height).data);
cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
cv.imshow("canvasOutput", dst); // canvasOutput 是另一个 <canvas> 的 id;
// 安排下一个。
let delay = 1000/FPS - (Date.now() - begin);
setTimeout(processVideo, delay);
}
// 安排第一个。
setTimeout(processVideo, 0);
OpenCV.js 使用上述方法实现了 cv.VideoCapture (videoSource)。您无需手动添加隐藏的 canvas 元素。
- 参数
-
- 返回
- cv.VideoCapture 实例
我们使用 read (image) 来获取视频的一帧。出于性能原因,图像应该使用 cv.CV_8UC4 类型构造,并且大小与视频相同。
- 参数
-
| image | 图像,cv.CV_8UC4 类型,大小与视频相同。 |
上述播放视频的代码可以简化为如下。
let src = new cv.Mat(height, width, cv.CV_8UC4);
let dst = new cv.Mat(height, width, cv.CV_8UC1);
let cap = new cv.VideoCapture(videoSource);
const FPS = 30;
function processVideo() {
let begin = Date.now();
cap.read(src);
cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
cv.imshow("canvasOutput", dst);
// 安排下一个。
let delay = 1000/FPS - (Date.now() - begin);
setTimeout(processVideo, delay);
}
// 安排第一个。
setTimeout(processVideo, 0);
- 注意
- 记住在停止后删除 src 和 dst。
试试看