Доступ к камерам UVC через OpenCV, Python и GStreamer
Камеры Arducam UVC работают по стандарту UVC, поэтому драйверы не нужны и камеры сразу работают с приложениями для веб-камер. Но опытным пользователям для задач машинного зрения и ИИ может понадобиться собственный код на Python и OpenCV с расширенным управлением камерой. Ниже подробно описано, как открыть камеру Arducam UVC и управлять ею.
Windows
1. Захват видео с веб-камеры UVC в Windows через Media Foundation
1.1. Скачайте SimpleCapture.exe
Скачайте архив SimpleCapture-exe.zip с сайта CodeProject.
1.2. Распакуйте архив и запустите SimpleCapture.exe

Выберите File → Choose device → USB → OK.

Выберите один из вариантов в списке, как на рисунке, и нажмите OK.

1.3. Захват видео

2. Доступ к веб-камере UVC через OpenCV
Примеры кода есть на Stack Overflow и GeeksforGeeks.

2.1. Как установить Python и OpenCV
python3 -m pip 安装 opencv-python
В команде из исходной документации вместо install ошибочно стоит «安装»; правильная команда — python3 -m pip install opencv-python.
2.2. Код на Python
import cv2
'''
apiPreference preferred Capture API backends to use.
Can be used to enforce a specific reader implementation
if multiple are available:
e.g. cv2.CAP_MSMF or cv2.CAP_DSHOW.
'''
# open video0
cap = cv2.VideoCapture(0, cv2.CAP_MSMF)
# set width and height
# cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
# cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
# set fps
# cap.set(cv2.CAP_PROP_FPS, 30)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
# Display the resulting frame
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
3. Доступ к нескольким веб-камерам UVC
3.1. Измените имя устройства и серийный номер
В отличие от Linux, в Windows при работе с несколькими камерами UVC их различают по имени устройства. Как изменить серийный номер камеры Arducam UVC, читайте в этом документе.
После изменения:


3.2. Код на Python
from pymf import get_MF_devices
device_list = get_MF_devices()
for i, device_name in enumerate(device_list):
print(f"opencv_index: {i}, device_name: {device_name}")
pymf через pip: пакет pymf в pip — другой, не тот, что скачивается отдельно.4. Устранение неполадок
4.1. Проверьте работу камеры
Прежде чем открывать камеру через OpenCV, проверьте, что она работает, в программе с графическим интерфейсом.
Способ 1 — проверьте камеру по инструкции.
Способ 2 — проверьте камеру с помощью файла .exe: где его скачать и как с ним работать, описано в этом разделе.
4.2. Не удаётся открыть камеру

Если у вас та же ошибка, сделайте следующее:
- Смените API (cv2.CAP_MSMF или cv2.CAP_DSHOW).
- Задайте более высокую частоту кадров.
4.3. Частота кадров не задаётся и слишком низкая

С бэкендом cv2.CAP_DSHOW частота кадров может не задаваться и оставаться очень низкой. В этом случае используйте бэкенд cv2.CAP_MSMF.
Linux
1. Видеоузел, поддерживаемые разрешения и частота кадров
Перед тем как открыть камеру UVC, определите нужный видеоузел.
ls /dev/video*

Обычно у камер с кодированием H.264 два или четыре видеоузла:
- если узлов два, video0 выдаёт YUV, MJPEG и т. д., а video1 — H.264;
- если узлов четыре, video0 выдаёт YUV, MJPEG и т. д., а video2 — H.264.
Посмотреть поддерживаемые форматы можно командой:
v4l2-ctl --list-formats -d 0 # 0 is video0
Ниже — пример для камеры UVC с четырьмя видеоузлами.


При необходимости проверьте разрешения и частоту кадров для каждого формата командой:
v4l2-ctl --list-formats-ext -d 0 # 0 is video0


2. Доступ к камере и управление через OpenCV
Откройте камеру через OpenCV на Python (OpenCV устанавливается через pip: pip install OpenCV-python).
import cv2
# open video0
cap = cv2.VideoCapture(0)
# The control range can be viewed through v4l2-ctl -L
cap.set(cv2.CAP_PROP_BRIGHTNESS, 64)
cap.set(cv2.CAP_PROP_CONTRAST, 0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
# Display the resulting frame
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
Пример вывода v4l2-ctl -L.

Ручная фокусировка камеры с автофокусом через OpenCV
Включить ручную фокусировку на камере с автофокусом можно двумя способами:
1. Значения задаются в коде
import cv2
import time
# open video0
cap = cv2.VideoCapture(0)
cap.grab()
cap.set(cv2.CAP_PROP_AUTOFOCUS, 1)
time.sleep(2)
cap.set(cv2.CAP_PROP_AUTOFOCUS, 0)
time.sleep(2)
cap.set(cv2.CAP_PROP_FOCUS, 123)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
# Display the resulting frame
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
2. Значения задаются через графический интерфейс
import cv2
import argparse
import configparser
from pathlib import Path
import time
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--vid", default="0", help="Video sourse, default 0")
parser.add_argument(
"-f", "--auto_focus", action="store_true", default=False, help="Turn on auto focus"
)
parser.add_argument(
"-c",
"--config",
default="focus.ini",
help="Focus config file, default focus.ini",
)
args = parser.parse_args()
try:
vid = int(args.vid)
except ValueError:
vid = args.vid
config_path = (Path(__file__).parent / Path(args.config)).resolve().absolute()
print("config file :", config_path)
config = configparser.ConfigParser()
config.read(config_path, encoding="utf-8")
cap = cv2.VideoCapture(vid)
cap.grab()
cap.set(cv2.CAP_PROP_AUTOFOCUS, 1)
if not args.auto_focus and config.has_section("Focus"):
auto_focus = (
config.getint("Focus", "auto_focus")
if config.has_option("Focus", "auto_focus")
else 1
)
focus = (
config.getint("Focus", "focus")
if config.has_option("Focus", "focus")
else int(cap.get(cv2.CAP_PROP_FOCUS))
)
else:
auto_focus = 1
focus = None
print("config auto_focus = %s" % auto_focus)
print("config focus = %s" % focus)
print("*" * 10)
if not auto_focus:
cap.set(cv2.CAP_PROP_AUTOFOCUS, 0)
time.sleep(2)
if focus:
cap.set(cv2.CAP_PROP_FOCUS, focus)
cv2.namedWindow("frame")
def set_auto_focus(x):
cap.set(cv2.CAP_PROP_AUTOFOCUS, x)
cv2.createTrackbar(
"0: OFF\r\n 1: ON\r\nauto_focus",
"frame",
int(cap.get(cv2.CAP_PROP_AUTOFOCUS)),
1,
set_auto_focus,
)
def set_focus(x):
cap.set(cv2.CAP_PROP_FOCUS, x)
cv2.createTrackbar("focus", "frame", int(cap.get(cv2.CAP_PROP_FOCUS)), 1023, set_focus)
while cap.isOpened():
# cap frame-by-frame
ret, frame = cap.read()
if not ret:
break
focus = int(cap.get(cv2.CAP_PROP_FOCUS))
cv2.setTrackbarPos("focus", "frame", focus)
af = int(cap.get(cv2.CAP_PROP_AUTOFOCUS))
cv2.setTrackbarPos("0: OFF\r\n 1: ON\r\nauto_focus", "frame", af)
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
# When everything done, release the cap
cap.release()
cv2.destroyAllWindows()
if not config.has_section("Focus"):
config.add_section("Focus")
print("set auto_focus = 0")
config.set("Focus", "auto_focus", "0")
print("set focus = %s" % focus)
config.set("Focus", "focus", str(focus))
config.write(open(config_path, "w"))
3. Доступ к нескольким камерам UVC
Иногда нужно подключить несколько камер UVC одновременно, но их трудно различить. У каждой камеры Arducam UVC есть собственный серийный номер, не повторяющийся у других камер; по нему её можно распознать и управлять ею отдельно. Ниже — два способа узнать серийный номер.
3.1. Через видеоузел
sudo udevadm info --query=all /dev/video0 | grep 'VENDOR_ID\|MODEL_ID\|SERIAL_SHORT'

3.2. Через Python
Установите зависимости:
sudo pip3 install pyudev
Код:
import pyudev
def getSerialNumber(device = 0):
context = pyudev.Context()
device_file = "/dev/video{}".format(device)
device = pyudev.Devices.from_device_file(context, device_file)
info = { item[0] : item[1] for item in device.items()}
return info["ID_SERIAL_SHORT"]

4. Трансляция видео через GStreamer
GStreamer — популярный открытый мультимедийный фреймворк. Его высокоуровневые API позволяют строить собственные приложения для трансляции, воспроизведения и монтажа видео с разными кодеками и функциями.
4.1. Формат видео
Камера выдаёт видео в трёх форматах.
MJPEG (формат вывода video0):
gst-launch-1.0 v4l2src device=/dev/video0 ! \
image/jpeg,width=1280,height=720,framerate=30/1 ! \
decodebin ! autovideosink
YUV (формат вывода video0):
gst-launch-1.0 -vv v4l2src device=/dev/video0 ! \
video/x-raw,format=YUY2,width=1280,height=720,framerate=10/1 ! \
videoconvert ! autovideosink
H.264 (формат вывода video2):
gst-launch-1.0 v4l2src device=/dev/video2 ! queue ! \
video/x-h264,width=1280,height=720,framerate=30/1 ! \
avdec_h264 ! autovideosink
4.2. Трансляция
В отличие от сжатых форматов MJPEG и H.264, YUV даёт большой объём данных и для трансляции не подходит.
MJPEG:
# server
gst-launch-1.0 v4l2src device=/dev/video0 ! \
image/jpeg,width=1280,height=720,framerate=30/1 ! \
tcpserversink host=0.0.0.0 port=5001
# client
# change xxx.xxx.xxx.xxx to the actual ip address
gst-launch-1.0 -v tcpclientsrc host=xxx.xxx.xxx.xxx port=5001 ! \
decodebin ! autovideosink
H.264:
# server
gst-launch-1.0 v4l2src device=/dev/video2 ! queue ! \
video/x-h264,width=1280,height=720,framerate=30/1 ! \
rtph264pay ! gdppay ! tcpserversink host=0.0.0.0 port=5001
# client
# change xxx.xxx.xxx.xxx to the actual ip address
gst-launch-1.0 -v tcpclientsrc host=xxx.xxx.xxx.xxx port=5001 ! \
gdpdepay ! rtph264depay ! avdec_h264 ! autovideosink
4.3. Работа с камерой
Запись видео:
gst-launch-1.0 v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720,framerate=30/1 ! jpegdec ! x264enc ! qtmux ! filesink location=test.mp4 -e
Сохранение снимка:
gst-launch-1.0 v4l2src device=/dev/video0 num-buffers=1 ! jpegenc ! filesink sync=false location=file.jpg
Просмотр:
gst-launch-1.0 v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720,framerate=30/1 ! jpegdec ! autovideosink