【问题标题】:OpenCV not working in Google ColaboratoryOpenCV 在 Google Colaboratory 中不起作用
【发布时间】:2020-12-14 19:44:00
【问题描述】:

我在google colaboratory上练习OpenCV,因为我不知道如何在GPU上使用OpenCV,当我在我的硬件上运行OpenCV时,它需要很多CPU,所以我去了Google colaboratory。 我笔记本的链接是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 文件

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

【问题讨论】:

  • 今天我第二次看到同样的问题-_src.empty()-这意味着从相机获取帧有问题,imgNone,现在它尝试cvtColor(None, ...) which gives _src.empty ()` .您应该检查if img is not None,因为它在无法获取帧时不会引发错误。有时相机需要时间来预热,它可以提供很少的空帧 (None)。
  • 但我认为问题在于 VideoCapture(0) 从直接连接到运行此代码的计算机的相机读取帧 - 当您在服务器 Google Colaboratory 上运行代码时,这意味着直接连接到服务器 Google Colaboratory(不是您的本地摄像头),但此服务器没有摄像头 - 因此它无法在 Google Colaboratory 上运行。您无法将图像从本地摄像头发送到此服务器。您的网络浏览器可能可以访问您的相机,但它需要 JavaScript 来获取帧并将其发送到服务器 - 但服务器需要代码来获取此帧 - 但 Google Colaboratory 没有。
  • 我在 Google 中检查了 Google Colaboratory 是否可以访问本地网络摄像头,似乎他们为此创建了脚本 - Camera Capture - 在第一个单元格中是使用 JavaScript 的函数 take_photo(),在第二个单元格中是这个函数用于显示来自本地摄像头的图像。
  • @furas 我认为您的 cmets 应该是答案。此外,您关于 GPU 和 CUDA 的其他问题是另一个与当前问题无关的主题,因此您应该在另一篇文章中提问。

标签: python opencv google-colaboratory face-recognition


【解决方案1】:

_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 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

【讨论】:

  • 我添加了关于变量cv2.data.haarcascades的信息
  • 我创建的代码可以在不使用按钮 Capture 的情况下循环获取网络摄像头代码 - 请参阅答案中的代码 - 但它的运行速度非常慢。
【解决方案2】:

代码中可能存在的问题是,在使用类似 Haar 的功能时,需要给出完整路径作为目录。

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)

一个可能的答案:

Insert Camera Capture sn -p,方法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

【讨论】:

  • 顺便说一句:有文件夹名称的特殊变量,其中包含 haarcascade 文件 - cv2.data.haarcascades
  • 真的吗?感谢您的信息。 Colab 使用名为take_photo() 的javascript 方法管理视频流,因此要使用haar 功能,应使用take_photo() 输出。
  • 我知道 - 我已经在回答中提到了take_photo()。但我认为它需要一些更改,因为 take_photo() 等待点击按钮 Capture 并保存在文件中,但原始代码需要与相机流一起运行 - 所以它不能等待点击,它不需要保存和读取文件。现在我试着改变它。
猜你喜欢
  • 2018-05-03
  • 2020-12-04
  • 2020-10-19
  • 2011-11-17
  • 1970-01-01
  • 2021-02-18
  • 2018-07-28
  • 2018-08-13
  • 1970-01-01
相关资源
最近更新 更多