Захват изображения с веб-камеры с помощью CV2 и Pyglet в Python

Я использую CV2 (OpenCV) для Python и библиотеки Pyglet Python для создания небольшого приложения, которое будет отображать живое видео с веб-камеры и накладывать текст или статические изображения. Я уже сделал приложение с CV2, которое просто отображает изображение с веб-камеры в кадре, но теперь я хотел бы получить этот кадр в окне pyglet.

Вот что я пока собрал:

import pyglet
from pyglet.window import key
import cv2
import numpy


window = pyglet.window.Window()

camera=cv2.VideoCapture(0)

def getCamFrame(color,camera):
    retval,frame=camera.read()
    if not color:
        frame=cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
    frame=numpy.rot90(frame)
    return frame


frame=getCamFrame(True,camera)
video = pyglet.resource.media(frame, streaming=True)

@window.event
def on_key_press(symbol, modifiers):
    if symbol == key.ESCAPE:
        print 'Application Exited with Key Press'
        window.close()

@window.event
def on_draw():
    window.clear()
    video.blit(10,10)

pyglet.app.run()

При запуске получаю следующую ошибку:

Traceback, line 20 in <module>
  video = pyglet.resource.media(frame, streaming=True)
TypeError: unhashable type: 'numpy.ndarray'

Я также открыт для других вариантов, которые позволили бы мне отображать текст поверх моего живого видео. Первоначально я использовал pygame, но в конце концов мне понадобится поддержка нескольких мониторов, поэтому я использую pyglet.


person Photovor    schedule 24.06.2015    source источник


Ответы (4)


С вашим подходом есть ряд проблем, но самое сложное - преобразовать массивы numpy в текстуры. Я использую подход ниже, который я обнаружил в какой-то момент в другом месте на SO. Короче говоря, вы должны использовать типы и структуры ctypes, предоставляемые pyglet.gl, чтобы сгенерировать массив GLubytes, а затем поместить в него содержимое изображения (массив numpy). Затем, поскольку у вас есть одномерный массив значений, вы должны указать, как Pyglet должен создать изображение, pImage здесь, указав формат пикселя и шаг.

Если вы заработаете приведенный ниже пример, вы сможете заставить pImg обновляться при каждом вызове on_draw, и все готово.

import pyglet
from pyglet.gl import *
from pyglet.window import key
import cv2
import numpy
import sys

window = pyglet.window.Window()

camera=cv2.VideoCapture(0)

retval,img = camera.read()
sy,sx,number_of_channels = img.shape
number_of_bytes = sy*sx*number_of_channels

img = img.ravel()

image_texture = (GLubyte * number_of_bytes)( *img.astype('uint8') )
# my webcam happens to produce BGR; you may need 'RGB', 'RGBA', etc. instead
pImg = pyglet.image.ImageData(sx,sy,'BGR',
       image_texture,pitch=sx*number_of_channels)

@window.event
def on_key_press(symbol, modifiers):
    if symbol == key.ESCAPE:
        print 'Application Exited with Key Press'
        window.close()

@window.event
def on_draw():
    window.clear()
    pImg.blit(0,0)

pyglet.app.run()
person rjonnal    schedule 24.06.2015

Хотя это работает, я обнаружил, что загрузка изображений из массивов numpy была довольно медленной, когда изображение было в высоком разрешении. pygarrrayimage, модуль python на github, может загружать массивы numpy напрямую в видеокарту, не делая копии:

https://github.com/motmot/pygarrayimage

Это удерживало мое приложение Python, которое загружает изображения из видео с высоким разрешением, от отставания. Посмотрите папку с примерами того, как быстро выводить изображения на экран.

person Dylan Ray    schedule 26.07.2015

Вы можете преобразовать каждое изображение opencv в изображение pyglet, используя ImageData конструктор. Идея состоит в том, чтобы преобразовать изображение opencv в массив PIL, который, в свою очередь, преобразуется в строку байтов, а затем передается конструктору в виде необработанных данных.

from PIL import Image
def cv2glet(img):
    '''Assumes image is in BGR color space. Returns a pyimg object'''
    rows, cols, channels = img.shape
    raw_img = Image.fromarray(img).tobytes()

    top_to_bottom_flag = -1
    bytes_per_row = channels*cols
    pyimg = pyglet.image.ImageData(width=cols, 
                                   height=rows, 
                                   format='BGR', 
                                   data=raw_img, 
                                   pitch=top_to_bottom_flag*bytes_per_row)
    return pyimg
person Sanfer    schedule 10.07.2017

person    schedule
comment
Можете ли вы также объяснить, как этот фрагмент кода решает проблему? - person Harshal Parekh; 10.12.2019