Видеозахват OpenCV возвращает False при вызове isOpened (), хотя другой пример кода работает хорошо - PullRequest
0 голосов
/ 27 марта 2020

Итак, у меня есть приложение Django, которое управляет некоторыми веб-камерами USB (все Logitech C525)
Это приложение берет кадр из объекта VideoCapture, когда запрос GET приходит на указанную конечную точку API и отправляет этот кадр на сервер, чтобы выполнить некоторую классификацию объекта на изображении (обычно это фрукты / овощи)
Приложение получает запросы от клиентов, IP-адрес каждого клиента (приложение и клиенты находятся в одной локальной сети) привязан к уникальной веб-камере ID
Вот фрагмент, который показывает, как я ищу привязку между идентификатором камеры и логическим устройством в /dev/video*, чтобы создать VideoCapture объект:

import re
import subprocess

class CameraBinder(object):

    def __init__(self):
        self.cam = 'C525'
        self.cam_id_list_in_dev_video = []
        self.binding_between_udev_dev = None

    def find_cam_in_dev_video(self):
        cmd = ['/usr/bin/v4l2-ctl', '--list-devices']
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = process.communicate()
        out, err = out.strip(), err.strip()
        for line in [i.split("\n\t".encode()) for i in out.split("\n\n".encode())]:
            if self.cam.encode() in line[0]:
                cam_id_from_dev = re.search(r'(?<=/dev/video).*', line[1].decode('utf-8'))
                self.cam_id_list_in_dev_video.append(cam_id_from_dev.group(0))
        process.kill()
        return self.cam_id_list_in_dev_video

    def bind_cam_between_dev_udev(self, cam_id):
        cmd = ['/bin/udevadm', 'info', '--attribute-walk', f'--name=/dev/video{cam_id}']
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = process.communicate()
        out, err = out.strip(), err.strip()
        kernel_for_udev_rules = re.search(r'(?<=KERNELS==\")[^\"]*', out.decode('utf-8')).group(0)
        serial = re.search(r'(?<=ATTRS{serial}==\")[^\"]*', out.decode('utf-8')).group(0)
        self.binding_between_udev_dev = f'{kernel_for_udev_rules} {serial}'
        process.kill()
        return self.binding_between_udev_dev

Чем, у меня есть некоторые Django команд управления, которые конфигурируют мою систему (сбор идентификаторов камер при первой настройке и привязка локальных IP-адресов к этим идентификаторам)
В моем views.py у меня есть эта конечная точка:

from camera.utils import VideoCamera, CameraBinder
from camera.models import Binding

cam_binder = CameraBinder()
cam_list = cam_binder.find_cam_in_dev_video()
for cam_id in cam_list:
    index = cam_binder.bind_cam_between_dev_udev(cam_id)
    streams[index] = VideoCamera(cam_id)
    print(cam_id, index)
    streams[index].update_frame()


def detect(request):
    if request.method == "GET":
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[-1].strip()
        else:
            ip = request.META.get('REMOTE_ADDR')
        camera_id = Binding.objects.get(ip_address=ip).port_id
        print(camera_id, streams[camera_id])
        logger.info('detect method started')
        s_time = time.clock()
        image = streams[camera_id].get_frame()

VideoCamera class:

from threading import Thread
import cv2

def start_new_thread(function):
    def decorator(*args, **kwargs):
        t = Thread(target=function, args=args, kwargs=kwargs)
        t.daemon = True
        t.start()
    return decorator


class VideoCamera(Thread):
    def __init__(self, cam_id):
        Thread.__init__(self)
        self.current_frame = None
        self.stream = cv2.VideoCapture(cam_id)
        self.stream.set(cv2.CAP_PROP_FRAME_WIDTH, CAMERA_SETTINGS['WIDTH'])
        self.stream.set(cv2.CAP_PROP_FRAME_HEIGHT, CAMERA_SETTINGS['HEIGHT'])
        self.stream.set(cv2.CAP_PROP_FPS, CAMERA_SETTINGS['FPS'])
        self.stream.set(cv2.CAP_PROP_AUTOFOCUS, CAMERA_SETTINGS['AUTOFOCUS'])
        self.stream.set(cv2.CAP_PROP_FOCUS, CAMERA_SETTINGS['FOCUS'])
        self.stream.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))

    @start_new_thread
    def update_frame(self):
        while True:
            ret, self.current_frame = self.stream.read()
            if self.current_frame is None:
                time.sleep(0.100)
                continue

    def get_frame(self):
        return self.current_frame

Когда я запускаю сервер разработки, кажется, что все идет хорошо, но когда я пытаюсь отправить запрос на мою конечную точку API, я получаю следующее:

Watching for file changes with StatReloader
2020-03-27 19:25:25,674 | INFO | Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 27, 2020 - 19:25:26
Django version 3.0.2, using settings 'sw_client.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
2020-03-27 19:25:29,941 | INFO | detect method started
Internal Server Error: /detect/
Traceback (most recent call last):
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/user/PycharmProjects/sw_client/camera/views.py", line 45, in detect
    cv2.imwrite(DEBUG_PATH + f'/grabbed{camera_id}.jpg', image)
cv2.error: OpenCV(4.2.0) /io/opencv/modules/imgcodecs/src/loadsave.cpp:715: error: (-215:Assertion failed) !_img.empty() in function 'imwrite'

2020-03-27 19:25:29,995 | ERROR | Internal Server Error: /detect/
Traceback (most recent call last):
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/user/smart_weights/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/user/PycharmProjects/sw_client/camera/views.py", line 45, in detect
    cv2.imwrite(DEBUG_PATH + f'/grabbed{camera_id}.jpg', image)
cv2.error: OpenCV(4.2.0) /io/opencv/modules/imgcodecs/src/loadsave.cpp:715: error: (-215:Assertion failed) !_img.empty() in function 'imwrite'

[27/Mar/2020 19:25:29] "GET /detect/ HTTP/1.1" 500 79218
* 1 022 * Если я закомментирую эту строку кода, я получу сообщение об ошибке через 2 строки после кодирования изображения, чтобы отправить его через JSON.
Как я понял, изображение, которое приходит к imwrite(), пусто. Я пытался распечатать self.stream.isOpened() внутри VideoCamera() класса. И вывод был False. Но в то же время я могу запустить это:
import cv2


cap1 = cv2.VideoCapture(4)
cap2 = cv2.VideoCapture(2)
cap3 = cv2.VideoCapture(0)
# cap4 = cv2.VideoCapture(6)
while 1:

    ret1, img1 = cap1.read()
    ret2, img2 = cap2.read()
    ret3, img3 = cap3.read()
    # ret4, img4 = cap4.read()

    if ret1 and ret2:

        cv2.imshow('img1', img1)
        cv2.imshow('img2', img2)
        cv2.imshow('img3', img3)
        # cv2.imshow('img4', img4)

        k = cv2.waitKey(100)
        if k == 27: #press Esc to exit
            break

cap1.release()
cap2.release()
cap3.release()
# cap4.release()
cv2.destroyAllWindows()

И это работает, выводя мне 3 windows с видеопотоком из моих камер.
Итак, как я могу локализовать проблему? Я думаю, это не из-за версий OpenCV / Python.

1 Ответ

0 голосов
/ 27 марта 2020

Ошибка в методе find_cam_in_dev_video, он возвращает список strings, VideoCapture нуждается в int в качестве аргумента. Приведение к int in:

for cam_id in cam_list:
    index = cam_binder.bind_cam_between_dev_udev(cam_id)
    streams[index] = VideoCamera(int(cam_id))

решает проблему

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...