Create openCV VideoCapture from interface name instead of camera numbers
Asked Answered
P

5

15

The normal way to create a videocapture is this:

cam = cv2.VideoCapture(n)

where n corresponds to the number of /dev/video0, dev/video1

But because I'm building a robot that uses multiple cameras for different things, I needed to make sure that it was being assigned to the correct camera, I created udev rules that created devices with symbolic links to the correct port whenever a specific camera was plugged in.

They appear to be working because when I look in the /dev directory I can see the link:

/dev/front_cam -> video1

However I'm not sure how to actually use this now.

I thought I could just open it from the filename, as if it was a video, but cam = cv2.VideoCapture('/dev/front_cam') doesn't work.

Neither does cv2.VideoCapture('/dev/video1')

It doesn't throw an error, it does return a VideoCapture object, just not one that's opened (cam.isOpened() returns False).

Predictory answered 6/3, 2016 at 0:47 Comment(2)
can you read the symbolic link from your program so that you can get the string '/dev/video1' programmatically? If yes, can you extract the number from that string (1) and feed it to your openCV capture object?Agostino
I upvoted your question, nice question I think.Barbarism
B
2
import re
import subprocess
import cv2
import os

device_re = re.compile("Bus\s+(?P<bus>\d+)\s+Device\s+(?P<device>\d+).+ID\s(?P<id>\w+:\w+)\s(?P<tag>.+)$", re.I)
df = subprocess.check_output("lsusb", shell=True)
for i in df.split('\n'):
    if i:
        info = device_re.match(i)
        if info:
            dinfo = info.groupdict()
            if "Logitech, Inc. Webcam C270" in dinfo['tag']:
                print "Camera found."
                bus = dinfo['bus']
                device = dinfo['device']
                break

device_index = None
for file in os.listdir("/sys/class/video4linux"):
    real_file = os.path.realpath("/sys/class/video4linux/" + file)
    print real_file
    print "/" + str(bus[-1]) + "-" + str(device[-1]) + "/"
    if "/" + str(bus[-1]) + "-" + str(device[-1]) + "/" in real_file:
        device_index = real_file[-1]
        print "Hurray, device index is " + str(device_index)


camera = cv2.VideoCapture(int(device_index))

while True:
    (grabbed, frame) = camera.read() # Grab the first frame
    cv2.imshow("Camera", frame)
    key = cv2.waitKey(1) & 0xFF

First search for desired string in USB devices list. Get BUS and DEVICE number.

Find symbolic link under video4linux directory. Extract device index from realpath and pass it to VideoCapture method.

Barbarism answered 6/3, 2016 at 1:56 Comment(0)
G
8

Each of my video4linux devices creates 2 device nodes. For example, /dev/video0 and /dev/video1 are both related to my internal webcam. When I plug in a second USB webcam, /dev/video2 and /dev/video3 both appear. However, I can only use the lower-numbered device of each pair for video capture (i.e. /dev/video0 and /dev/video2).

I watched my device arrival with udevadm monitor, and then inspected each of the camera devices with udevadm info --path=$PATH_FROM_UDEVADM_MONITOR --attribute-walk. The devices which work for video capture have ATTR{index}=="0".

Maybe instead of trying to open /dev/video1, you just need to open /dev/video0:

cam = cv2.CaptureVideo("/dev/video0")
Graces answered 31/10, 2019 at 20:0 Comment(0)
M
4

If you know the model of the camera, you can look it up in /dev/v4l/by-id/.... We are using an HDMI-USB video converter, and we connect to it like this:

#! /usr/bin/env python
import os
import re
import cv2

DEFAULT_CAMERA_NAME = '/dev/v4l/by-id/usb-AVerMedia_Technologies__Inc._Live_Gamer_Portable_2_Plus_5500114600612-video-index0'

device_num = 0
if os.path.exists(DEFAULT_CAMERA_NAME):
    device_path = os.path.realpath(DEFAULT_CAMERA_NAME)
    device_re = re.compile("\/dev\/video(\d+)")
    info = device_re.match(device_path)
    if info:
        device_num = int(info.group(1))
        print("Using default video capture device on /dev/video" + str(device_num))
cap = cv2.VideoCapture(device_num)

This follows the device name symlink to the /dev/video name, then parses that for the device number.

Magbie answered 21/8, 2019 at 17:8 Comment(0)
P
3

Instead of the suggested solution I found a shorter one, that feels a little bit hacky.

I just look at where the symbolic link points, find the integer in it, and then use that.

import subprocess

cmd = "readlink -f /dev/CAMC"
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)

# output of form /dev/videoX
out = process.communicate()[0]

# parse for ints
nums = [int(x) for x in out if x.isdigit()]

cap = cv2.VideoCapture(nums[0])
Predictory answered 7/3, 2016 at 4:8 Comment(0)
B
2
import re
import subprocess
import cv2
import os

device_re = re.compile("Bus\s+(?P<bus>\d+)\s+Device\s+(?P<device>\d+).+ID\s(?P<id>\w+:\w+)\s(?P<tag>.+)$", re.I)
df = subprocess.check_output("lsusb", shell=True)
for i in df.split('\n'):
    if i:
        info = device_re.match(i)
        if info:
            dinfo = info.groupdict()
            if "Logitech, Inc. Webcam C270" in dinfo['tag']:
                print "Camera found."
                bus = dinfo['bus']
                device = dinfo['device']
                break

device_index = None
for file in os.listdir("/sys/class/video4linux"):
    real_file = os.path.realpath("/sys/class/video4linux/" + file)
    print real_file
    print "/" + str(bus[-1]) + "-" + str(device[-1]) + "/"
    if "/" + str(bus[-1]) + "-" + str(device[-1]) + "/" in real_file:
        device_index = real_file[-1]
        print "Hurray, device index is " + str(device_index)


camera = cv2.VideoCapture(int(device_index))

while True:
    (grabbed, frame) = camera.read() # Grab the first frame
    cv2.imshow("Camera", frame)
    key = cv2.waitKey(1) & 0xFF

First search for desired string in USB devices list. Get BUS and DEVICE number.

Find symbolic link under video4linux directory. Extract device index from realpath and pass it to VideoCapture method.

Barbarism answered 6/3, 2016 at 1:56 Comment(0)
M
1

A possibility that was not explored in the other answers is to use the "name" file in the /sys/class/video4linux/video*/ directories.

Example :

def get_camera(camera_name):
    cam_num = None
    for file in os.listdir("/sys/class/video4linux"):
        real_file = os.path.realpath("/sys/class/video4linux/" + file + "/name")
        with open(real_file, "rt") as name_file:
            name = name_file.read().rstrip()
        if camera_name in name:
            cam_num = int(re.search("\d+$", file).group(0))
            found = "FOUND!"
        else:
            found = "      "
        print("{} {} -> {}".format(found, file, name))
    return cam_num

Which gives :

get_camera('HUE')
FOUND! video1 -> HUE HD Pro Camera: HUE HD Pro C
    video0 -> HP HD Camera: HP HD Camera
Meda answered 31/10, 2020 at 18:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.