_src.empty() 表示从相机获取帧有问题,img 是None,当它尝试cvtColor(None, ...) 时,它会给出_src.empty()。
您应该检查if img is not None:,因为cv2 在无法从相机获取帧或从文件中读取图像时不会引发错误。有时相机需要时间“热身”,它可以提供很少的空帧 (None)。
VideoCapture(0) 从直接连接到运行此代码的计算机的摄像头读取帧 - 当您在服务器 Google Colaboratory 上运行代码时,这意味着摄像头直接连接到服务器 Google Colaboratory(不是您的本地摄像头),但此服务器没有'没有摄像头,所以VideoCapture(0) 不能在Google Colaboratory 上工作。
cv2 在服务器上运行时无法从本地摄像头获取图像。您的网络浏览器可能可以访问您的相机,但它需要 JavaScript 来获取帧并发送到服务器 - 但服务器需要代码来获取此帧
如果Google Colaboratory 可以访问本地网络摄像头,我在 Google 中进行了检查,似乎他们为此创建了脚本 - Camera Capture - 在第一个单元格中是函数 take_photo(),它使用 JavaScript 访问您的相机并在浏览器中显示,在第二个单元格中,此功能用于显示来自本地摄像头的图像并截取屏幕。
您应该使用此功能而不是 VideoCapture(0) 来使用本地摄像头在服务器上工作。
顺便说一句: Belove take_photo() 还有关于 cv2.im_show() 的信息,因为它也仅适用于直接连接到运行此代码的计算机的显示器(并且此计算机必须运行像 @987654343 这样的 GUI @ 在 Windows 上,X11 在 Linux 上)-当您在服务器上运行它时,它希望在直接连接到服务器的监视器上显示-但服务器通常在没有监视器(并且没有 GUI)的情况下工作
Google Colaboratory 有特殊替换,显示在网络浏览器中
from google.colab.patches import cv2_imshow
顺便说一句:如果您在加载 haarcascades .xml 时遇到问题,那么您可能需要文件夹到文件名。 cv2 有这个 cv2.data.haarcascades 的特殊变量
path = os.path.join(cv2.data.haarcascades, 'haarcascade_frontalface_default.xml')
cv2.CascadeClassifier( path )
您还可以查看此文件夹中的内容
import os
filenames = os.listdir(cv2.data.haarcascades)
filenames = sorted(filenames)
print('\n'.join(filenames))
编辑:
我创建了可以从本地网络摄像头逐帧获取的代码,无需使用button,也无需保存在文件中。问题是它很慢 - 因为它仍然必须将帧从本地网络浏览器发送到谷歌 colab 服务器,然后再返回到本地网络浏览器
带有 JavaScript 函数的 Python 代码
#
# based on: https://colab.research.google.com/notebooks/snippets/advanced_outputs.ipynb#scrollTo=2viqYx97hPMi
#
from IPython.display import display, Javascript
from google.colab.output import eval_js
from base64 import b64decode, b64encode
import numpy as np
def init_camera():
"""Create objects and functions in HTML/JavaScript to access local web camera"""
js = Javascript('''
// global variables to use in both functions
var div = null;
var video = null; // <video> to display stream from local webcam
var stream = null; // stream from local webcam
var canvas = null; // <canvas> for single frame from <video> and convert frame to JPG
var img = null; // <img> to display JPG after processing with `cv2`
async function initCamera() {
// place for video (and eventually buttons)
div = document.createElement('div');
document.body.appendChild(div);
// <video> to display video
video = document.createElement('video');
video.style.display = 'block';
div.appendChild(video);
// get webcam stream and assing to <video>
stream = await navigator.mediaDevices.getUserMedia({video: true});
video.srcObject = stream;
// start playing stream from webcam in <video>
await video.play();
// Resize the output to fit the video element.
google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);
// <canvas> for frame from <video>
canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
//div.appendChild(input_canvas); // there is no need to display to get image (but you can display it for test)
// <img> for image after processing with `cv2`
img = document.createElement('img');
img.width = video.videoWidth;
img.height = video.videoHeight;
div.appendChild(img);
}
async function takeImage(quality) {
// draw frame from <video> on <canvas>
canvas.getContext('2d').drawImage(video, 0, 0);
// stop webcam stream
//stream.getVideoTracks()[0].stop();
// get data from <canvas> as JPG image decoded base64 and with header "data:image/jpg;base64,"
return canvas.toDataURL('image/jpeg', quality);
//return canvas.toDataURL('image/png', quality);
}
async function showImage(image) {
// it needs string "data:image/jpg;base64,JPG-DATA-ENCODED-BASE64"
// it will replace previous image in `<img src="">`
img.src = image;
// TODO: create <img> if doesn't exists,
// TODO: use `id` to use different `<img>` for different image - like `name` in `cv2.imshow(name, image)`
}
''')
display(js)
eval_js('initCamera()')
def take_frame(quality=0.8):
"""Get frame from web camera"""
data = eval_js('takeImage({})'.format(quality)) # run JavaScript code to get image (JPG as string base64) from <canvas>
header, data = data.split(',') # split header ("data:image/jpg;base64,") and base64 data (JPG)
data = b64decode(data) # decode base64
data = np.frombuffer(data, dtype=np.uint8) # create numpy array with JPG data
img = cv2.imdecode(data, cv2.IMREAD_UNCHANGED) # uncompress JPG data to array of pixels
return img
def show_frame(img, quality=0.8):
"""Put frame as <img src="data:image/jpg;base64,...."> """
ret, data = cv2.imencode('.jpg', img) # compress array of pixels to JPG data
data = b64encode(data) # encode base64
data = data.decode() # convert bytes to string
data = 'data:image/jpg;base64,' + data # join header ("data:image/jpg;base64,") and base64 data (JPG)
eval_js('showImage("{}")'.format(data)) # run JavaScript code to put image (JPG as string base64) in <img>
# argument in `showImage` needs `" "`
以及在循环中使用它的代码
#
# based on: https://colab.research.google.com/notebooks/snippets/advanced_outputs.ipynb#scrollTo=zo9YYDL4SYZr
#
#from google.colab.patches import cv2_imshow # I don't use it but own function `show_frame()`
import cv2
import os
face_cascade = cv2.CascadeClassifier(os.path.join(cv2.data.haarcascades, 'haarcascade_frontalface_default.xml'))
# init JavaScript code
init_camera()
while True:
try:
img = take_frame()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#cv2_imshow(gray) # it creates new image for every frame (it doesn't replace previous image) so it is useless
#show_frame(gray) # it replace previous image
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)
#cv2_imshow(img) # it creates new image for every frame (it doesn't replace previous image) so it is useless
show_frame(img) # it replace previous image
except Exception as err:
print('Exception:', err)
我不使用from google.colab.patches import cv2_imshow,因为它总是在页面上添加新图片而不是替换现有图片。
与 Google Colab 上的 Notebook 代码相同:
https://colab.research.google.com/drive/1j7HTapCLx7BQUBp3USiQPZkA0zBKgLM0?usp=sharing