В ml5.js работа с видеопотоком осуществляется через объект
p5.Video, который интегрируется с библиотекой p5.js. Для
подключения нескольких камер необходимо создать отдельные объекты
createCapture() для каждого устройства. По умолчанию
createCapture() использует первую доступную камеру, но
можно указать конкретное устройство через объект с параметром
video: { deviceId: "ID_камеры" }.
Пример создания двух видеопотоков:
let video1, video2;
function setup() {
createCanvas(640, 480);
navigator.mediaDevices.enumerateDevices().then(devices => {
const videoDevices = devices.filter(device => device.kind === 'videoinput');
video1 = createCapture({ video: { deviceId: videoDevices[0].deviceId } });
video1.size(320, 240);
video1.hide();
video2 = createCapture({ video: { deviceId: videoDevices[1].deviceId } });
video2.size(320, 240);
video2.hide();
});
}
Ключевой момент: использование
navigator.mediaDevices.enumerateDevices() позволяет
динамически получать список доступных камер и управлять их
подключением.
После инициализации видеопотоков их можно подключать к различным
моделям ml5.js, таким как poseNet,
objectDetector или faceApi. Каждое видео может
быть связано с отдельной моделью или с одним экземпляром модели, которая
обрабатывает несколько потоков последовательно.
Пример интеграции с PoseNet:
let poseNet1, poseNet2, poses1 = [], poses2 = [];
function setup() {
createCanvas(640, 480);
// video1 и video2 создаются как показано выше
poseNet1 = ml5.poseNet(video1, modelReady1);
poseNet1.on('pose', results => poses1 = results);
poseNet2 = ml5.poseNet(video2, modelReady2);
poseNet2.on('pose', results => poses2 = results);
}
function modelReady1() {
console.log('PoseNet для первой камеры загружен');
}
function modelReady2() {
console.log('PoseNet для второй камеры загружен');
}
Важно: каждая модель работает асинхронно, поэтому
события on('pose', ...) позволяют обрабатывать результаты
по мере их поступления без блокировки основного потока.
Для визуализации нескольких камер необходимо правильно размещать их
на холсте с использованием функции image() p5.js. Можно
комбинировать оба потока на одном холсте или создавать отдельные канвасы
для каждого потока.
Пример совместного отображения двух видеопотоков:
function draw() {
background(220);
image(video1, 0, 0, 320, 240);
drawPoses(poses1);
image(video2, 320, 0, 320, 240);
drawPoses(poses2);
}
function drawPoses(poses) {
for (let i = 0; i < poses.length; i++) {
let pose = poses[i].pose;
for (let j = 0; j < pose.keypoints.length; j++) {
let keypoint = pose.keypoints[j];
fill(0, 255, 0);
noStroke();
ellipse(keypoint.position.x, keypoint.position.y, 10, 10);
}
}
}
Особенность: координаты ключевых точек PoseNet соответствуют локальной системе координат для каждого видео. При совмещении потоков необходимо корректировать координаты, если видео отображается не в оригинальном размере.
Для реального времени важно минимизировать задержку при обработке нескольких видеопотоков. Для этого рекомендуется:
draw().video.hide(), чтобы
рендеринг происходил только через image(), что ускоряет
отрисовку.Пример оптимизации:
video1.size(160, 120);
video2.size(160, 120);
function draw() {
background(220);
if (poses1.length > 0) drawPosesScaled(video1, poses1, 0, 0, 320, 240);
if (poses2.length > 0) drawPosesScaled(video2, poses2, 320, 0, 320, 240);
}
function drawPosesScaled(video, poses, xOffset, yOffset, w, h) {
image(video, xOffset, yOffset, w, h);
let scaleX = w / video.width;
let scaleY = h / video.height;
for (let i = 0; i < poses.length; i++) {
let pose = poses[i].pose;
for (let j = 0; j < pose.keypoints.length; j++) {
let keypoint = pose.keypoints[j];
fill(255, 0, 0);
ellipse(keypoint.position.x * scaleX + xOffset, keypoint.position.y * scaleY + yOffset, 8, 8);
}
}
}
Примечание: масштабирование координат ключевых точек необходимо при изменении размера видео для корректного отображения на холсте.
ml5.js позволяет обучать простые модели прямо в браузере с использованием видеопотока. Наличие нескольких камер предоставляет возможность собрать более разнообразные данные для обучения:
Пример использования нескольких видеопотоков для классификации:
let featureExtractor, classifier;
function setup() {
createCanvas(640, 480);
featureExtractor = ml5.featureExtractor('MobileNet');
classifier = featureExtractor.classification(video1);
// Добавление данных с второй камеры
classifier.addImage(video2, 'label2');
}
Важный аспект: при работе с несколькими камерами важно поддерживать синхронизацию данных, чтобы метки соответствовали правильным кадрам.
В сценариях с несколькими камерами возможно использование нескольких моделей одновременно:
Для высокой производительности рекомендуется:
frameRate().async/await или callback-функции для
последовательной обработки, чтобы избежать блокировки основного
потока.