OpenCV 在 Google Colaboratory 中不工作

OpenCV not working in Google Colaboratory

我在 google colaboratory 上练习 OpenCV 因为我不知道如何在 GPU 上使用 OpenCV,当我 运行 在我的硬件上使用 OpenCV 时,需要很多 CPU,所以我去了 Google colaboratory。 我笔记本的 link 是 here.

如果你不想看,那么这里是代码:

import cv2

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
cap = cv2.VideoCapture(0)

while True:
    _, img = cap.read()
    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)

    cv2.imshow('img', img)

    k = cv2.waitKey(30) & 0xff
    if k==27:
        break
    
cap.release()

相同的代码在我的 PC 上运行良好,但在 Google Colaboratory 上运行不正常。错误是:

---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-5-0d9472926d8c> in <module>()
      6 while True:
      7         _, img = cap.read()
----> 8         gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
      9         faces = face_cascade.detectMultiScale(gray, 1.1, 4)
     10         for (x, y, w, h) in faces:

error: OpenCV(4.1.2) /io/opencv/modules/imgproc/src/color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cvtColor'

PS~我在 Google Colaboratory

笔记本的同一目录中有 haarcascade 文件

如何处理?如果没有,那么在我启用 CUDA 的 GPU 而不是 CPU 上是否有针对 运行 OpenCV 的任何“concrete”解决方案?提前致谢!

_src.empty() 表示从相机获取帧有问题,imgNone,当它尝试 cvtColor(None, ...) 时,它给出 _src.empty()

您应该检查 if img is not None:,因为 cv2 在无法从相机获取帧或无法从文件读取图像时不会引发错误。有时相机需要时间“预热”,它可以提供很少的空帧 (None)。


VideoCapture(0) 从直接连接到计算机的相机读取帧,其中 运行 是此代码 - 当您在服务器 Google Colaboratory 上 运行 代码时,这意味着相机直接连接到服务器 Google Colaboratory(不是你的本地摄像头)但是这个服务器没有摄像头所以 VideoCapture(0) 不能在 Google Colaboratory.

上工作

cv2 当 运行 在服务器上时,无法从您的本地相机获取图像。您的网络浏览器可能可以访问您的相机,但它需要 JavaScript 来获取帧并发送到服务器 - 但服务器需要代码来获取此帧


我检查了 Google 如果 Google Colaboratory 可以访问本地网络摄像头,他们似乎为此创建了脚本 - Camera Capture - 第一个单元格中的函数 take_photo() 使用JavaScript 访问您的摄像头并在浏览器中显示,在第二个单元格中此功能用于显示本地摄像头的图像并截取屏幕截图。

您应该使用此功能而不是 VideoCapture(0) 来使用本地摄像头在服务器上工作。


顺便说一句: 亲爱的 take_photo() 还有关于 cv2.im_show() 的信息,因为它也只适用于直接连接到 运行 计算机的显示器s 这段代码(并且这台计算机必须 运行 GUI,如 Windows 上的 Windows,Linux 上的 X11) - 当你 运行 它在服务器上然后它想显示在直接连接到服务器的显示器上 - 但服务器通常在没有显示器(并且没有 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 且不保存在文件中的情况下逐帧从本地网络摄像头获取。问题是它很慢 - 因为它仍然需要将帧从本地网络浏览器发送到 google colab 服务器,然后再返回到本地网络浏览器

Python 具有 JavaScript 功能的代码

#
# 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 上的笔记本相同的代码:

https://colab.research.google.com/drive/1j7HTapCLx7BQUBp3USiQPZkA0zBKgLM0?usp=sharing

代码中可能存在的问题是,在使用Haar-like功能时需要将full-path作为目录。

face_cascade = cv2.CascadeClassifier('/User/path/to/opencv/data/haarcascades/haarcascade_frontalface_default.xml')

opencvcolab 问题已经知道很长一段时间了,同样的问题也被问到 here

here所述,您可以使用cv2_imshow显示图像,但您想要处理相机帧。

from google.colab.patches import cv2_imshow
img = cv2.imread('logo.png', cv2.IMREAD_UNCHANGED)
cv2_imshow(img)

一个可能的答案:

插入相机捕捉片段,方法take_photo但您需要修改方法。

face_cascade = cv2.CascadeClassifier('/opencv/data/haarcascades/haarcascade_frontalface_default.xml')

try:
    filename = take_photo()
    img = Image(filename)
    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)
    cv2_imshow("img", img)
      
except Exception as err:
    print(str(err))

以上代码需要编辑,因为没有直接使用的方法VideoCapture你必须修改take_photo