cvtColor 失败,断言 scn == 3 || scn == 4,但图像确实有 3 个通道且存在

2 投票
1 回答
1001 浏览
提问于 2025-04-18 07:07

我正在尝试在树莓派上进行人脸检测,所以我想找到最快的方法来完成所有操作。(在700MHz的处理器上,每毫秒都很重要!)

我做了一个快速的速度测试,记录了我需要检查的各种选项的时间,这些选项包括:

  • 获取网络摄像头的图片(可以选择pygame.camera或openCV的cv2.VideoCapture)
  • 调整图片大小,转换为灰度图,如果需要的话,还要转换为numpy格式并旋转。

听起来有点奇怪,但在树莓派上,使用pygame.camera捕捉图片比使用openCV快大约60毫秒。

假设从pygame表面转换到numpy,再加上90度的旋转,所花的时间少于60毫秒,那么混合使用这两者是值得的。

这里的问题是,我遇到了一个奇怪的cvtColor错误,提示我的numpy ndarray(目前是opencv 2.0的后端)没有3个或4个通道。

这很奇怪,因为在我调用cvtColor之前,我打印了ndarray的形状,它确实有3个通道。我只能假设我漏掉了一些非常明显的东西。

这是简化后的代码。

import pygame
import cv2
import numpy
from pygame import camera

pygame.init()
pygame.camera.init()

#get one shot from pygame camera
cam_list = pygame.camera.list_cameras()
pywebcam = pygame.camera.Camera(cam_list[0],(640,480))
pywebcam.start()
image = pywebcam.get_image()

for z in xrange(50):
    image = pywebcam.get_image()

pywebcam.stop()

if image:
    #numpy (convert to numpy as fast as possible)
    numpy_image=pygame.surfarray.array3d(image)

    print(numpy_image.shape)
    numpy_image=cv2.cvtColor(numpy_image,cv2.COLOR_BGR2GRAY)
    numpy_image = cv2.resize(numpy_image, (0,0), fx=0.25, fy=0.25)
    numpy_image = numpy.rot90(numpy_image,3)

编辑:删掉了一堆代码。新代码去掉了所有的性能分析。

我从这个程序得到的输出是:

Average time in range: 58.64 ms.

Pausing to ensure webcam is freed.

Average time in range: 115.56 ms.

(480, 640, 3)
Pure NumPy
  0     ms to printing shape.
  39    ms to convert to grayscale.
  16    ms to resize.
  0     ms to rotate.
  58 total ms.

(640, 480, 3)
OpenCV Error: Assertion failed (scn == 3 || scn == 4) in cvtColor, file /build/opencv-XZa2gn/opencv-2.3.1/modules/imgproc/src/color.cpp, line 2834
Traceback (most recent call last):
  File "profile.py", line 72, in <module>
    numpy_image=cv2.cvtColor(numpy_image,cv2.COLOR_BGR2GRAY)
cv2.error: /build/opencv-XZa2gn/opencv-2.3.1/modules/imgproc/src/color.cpp:2834: error: (-215) scn == 3 || scn == 4 in function cvtColor

这到底是怎么回事?

1 个回答

1

找到了答案。其实,numpy在处理ndarray的时候并不会直接更新它,而是把临时的变化记在心里。这样做速度很快。

虽然C++版本的opencv 2.0可以处理这个问题,但Python的绑定却不行。而且因为pygame的surfarray代码是基于numpy的,所以任何从pygame到opencv的操作都会遇到这个问题。

所以,你只需要在调用pygame.surfarray.array3d()之后,调用numpy.copy()就可以了。

这样新复制的内容就不会有这些临时的变化。因此,不用纠结于array3d(),直接用pixels3d()就行了——反正你都得做一个复制,那就别做两个了。

关于更多信息,我记得这些临时变化被称为“strides”之类的东西。

撰写回答