简体   繁体   English

OpenCV 在 Google Colaboratory 中不起作用

[英]OpenCV not working in Google Colaboratory

I was practicing OpenCV on google colaboratory becasuse I don't know how to use OpenCV on GPU, when I run OpenCV on my hardware, It takes a lot of CPU, so I went to Google colaboratory.我在google colaboratory上练习OpenCV,因为我不知道如何在GPU上使用OpenCV,当我在我的硬件上运行OpenCV时,它需要很多CPU,所以我去了Google colaboratory。 The link to my notebook is here .我的笔记本的链接在这里

If you don't want to watch it, then here is the code:如果不想看的话,代码如下:

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()

The same code worked fine on my PC, but not on Google Colaboratory.相同的代码在我的 PC 上运行良好,但在 Google Colaboratory 上不起作用。 The error is:错误是:

---------------------------------------------------------------------------
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~I have the haarcascade file inside the same directory of my notebook in Google Colaboratory PS~我在 Google Colaboratory 的 notebook 的同一目录中有 haarcascade 文件

How to deal with it?如何处理? If not then is there any " concrete " solution to run OpenCV on my CUDA enabled GPU instead of CPU?如果没有,那么是否有任何“具体”解决方案可以在我启用 CUDA 的 GPU 而不是 CPU 上运行 OpenCV? Thanks in advance!提前致谢!

_src.empty() means that it had problem to get frame from camera and img is None and when it tries cvtColor(None, ...) then it gives _src.empty() . _src.empty()意味着从相机获取帧有问题, imgNone ,当它尝试cvtColor(None, ...)然后它给出_src.empty()

You should check if img is not None: because cv2 doesn't raise error when it can't get frame from camera or read image from file.您应该检查if img is not None:因为cv2在无法从相机获取帧或从文件读取图像时不会引发错误。 And sometimes camera needs time to "warm up" and it can gives few empty frames ( None ).有时相机需要时间来“预热”,它可以提供很少的空帧( None )。


VideoCapture(0) reads frame from camera directly connected to computer which runs this code - and when you run code on server Google Colaboratory then it means camera connected directly to server Google Colaboratory (not your local camera) but this server doesn't have camera so VideoCapture(0) can't work on Google Colaboratory . VideoCapture(0)从直接连接到运行此代码的计算机的相机读取帧 - 当您在服务器Google Colaboratory上运行代码时,这意味着相机直接连接到服务器Google Colaboratory (不是您的本地相机),但该服务器没有相机所以VideoCapture(0)不能在Google Colaboratory上工作。

cv2 can't get image from your local camera when it runs on server. cv2在服务器上运行时无法从本地相机获取图像。 Your web browser may have access to your camera but it needs JavaScript to get frame and send to server - but server needs code to get this frame您的网络浏览器可能可以访问您的相机,但它需要 JavaScript 来获取帧并发送到服务器 - 但服务器需要代码来获取此帧


I checked in Google if Google Colaboratory can access local webcam and it seems they created script for this - Camera Capture - in first cell is function take_photo() which uses JavaScript to access your camera and display in browser, and in second cell this function is used to display image from local camera and to take screenshot.我在 Google 中检查了 Google Google Colaboratory可以访问本地网络摄像头,似乎他们为此创建了脚本 - Camera Capture - 在第一个单元格中是函数take_photo() ,它使用JavaScript访问您的相机并在浏览器中显示,在第二个单元格中该函数是用于显示来自本地相机的图像并截取屏幕截图。

You should use this function instead of VideoCapture(0) to work on server with your local camera.您应该使用此功能而不是VideoCapture(0)来使用本地相机在服务器上工作。


BTW: Belove take_photo() there is also information about cv2.im_show() because it also works only with monitor directly connected to computer which runs this code (and this computer has to run GUI like Windows on Windows , X11 on Linux) - and when you run it on server then it want to display on monitor directly connected to server - but server usually works without monitor (and without GUI)顺便说一句: Belove take_photo()还有关于cv2.im_show()信息,因为它也只适用于直接连接到运行此代码的计算机的显示器(并且这台计算机必须像Windows上的 Windows 一样运行 GUI,Linux 上的X11 ) - 和当您在服务器上运行它时,它希望在直接连接到服务器的监视器上显示 - 但服务器通常在没有监视器(并且没有 GUI)的情况下工作

Google Colaboratory has special replacement which displays in web browser Google Colaboratory具有特殊的替代品,可显示在网络浏览器中

 from google.colab.patches import cv2_imshow

BTW: If you will have problem with loading haarcascades .xml then you may need folder to filename.顺便说一句:如果您在加载 haarcascades .xml遇到问题,那么您可能需要文件夹到文件名。 cv2 has special variable for this cv2.data.haarcascades cv2有这个cv2.data.haarcascades特殊变量

path = os.path.join(cv2.data.haarcascades, 'haarcascade_frontalface_default.xml')

cv2.CascadeClassifier( path )

You can also see what is in this folder您还可以查看此文件夹中的内容

import os

filenames = os.listdir(cv2.data.haarcascades)
filenames = sorted(filenames)
print('\n'.join(filenames))

EDIT:编辑:

I created code which can get from local webcam frame by frame without using button and without saving in file.我创建了可以在不使用button且不保存文件的情况下逐帧从本地网络摄像头获取的代码。 Problem is that it is slow - because it still have to send frame from local web browser to google colab server and later back to local web browser问题是它很慢 - 因为它仍然需要将帧从本地网络浏览器发送到 google colab 服务器,然后再发送回本地网络浏览器

Python code with JavaScript functions带有 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 `" "` 

And code which uses it in loop以及在循环中使用它的代码

# 
# 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)

I don't use from google.colab.patches import cv2_imshow because it always add new image on page instead of replacing existing image.我不使用from google.colab.patches import cv2_imshow因为它总是在页面上添加新图像而不是替换现有图像。


The same code as Notebook on Google Colab:与 Google Colab 上的 Notebook 相同的代码:

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

The possible problem in the code is, you need to give full-path as the directory when using Haar-like features.代码中可能存在的问题是,在使用类Haar特性时,需要提供全路径作为目录。

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

The colab issue with opencv has been known for quite some time, also the same question asked here opencvcolab问题已经为人所知有一段时间了, 这里也提出了同样的问题

As stated here , you can use the cv2_imshow to display the image, but you want to process Camera frames.如前所述在这里,你可以使用cv2_imshow来显示图像,但要处理相机帧。

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

One possible answer:一种可能的答案:

Insert Camera Capture snippet, the method take_photo but you need to modify the method.插入 Camera Capture 片段,方法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))

The above code requires editing since there is no direct way to use VideoCapture you have to modify take_photo上面的代码需要编辑,因为没有直接使用VideoCapture你必须修改take_photo

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM