Введение
В конечном итоге я пытаюсь визуализировать взаимодействие человека с самим пространством и объектами в космосе. В качестве первого прототипа я буду использовать пиксели выходного видеосигнала для захвата и сохранения движения в пространстве.
Мой процесс задокументирован в три этапа:
- Отслеживание движения
Сравнение предыдущего кадра с входящим по значениям RGB и определение перемещенных* пикселей (пикселей с большой разницей по сравнению с предыдущим кадром) выше определенный порог. - Подсчет и сохранение перемещенных пикселей
После определения того, что пиксель переместился, он обновляется в поле (с использованием массива), где каждый пиксель содержит значение, равное его количеству переехал. - Сопоставление перемещенныхпикселей с максимальным значением или временем
Наконец важно сопоставить значение цвета от нуля до самого высокого значения RGB, 255, чтобы иметь точное среднее значение того, где было наибольшее движение.
*Очевидно, что этот прототип не передает все движения. Например, когда движение в реальной жизни имеет тот же свет или цвет, что и камера, оно не воспринимается как «движение». Сопровождается обилием ошибок измерения.
Трекер движения
На основе скетча обнаружения движения от bestesaylar по адресу:
«Веб-редактор p5.js
Веб-редактор для p5.js, библиотеки JavaScript с целью сделать программирование доступным для художников, дизайнеров…editor.p5js .или"
Сначала давайте настроим холст HTML, импортировав библиотеку p5js и связав наш sketch.js с index.html.
index.html:
<!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.sound.min.js"></script> <link rel="stylesheet" type="text/css" href="style.css" /> <meta charset="utf-8" /> </head> <body> <script src="src/sketch.js"></script> </body> </html>
sketch.js:
var video; var scaler = 10; var preFrame; function setup() { createCanvas(640, 480); pixelDensity(1); video = createCapture(VIDEO); video.size(width / scaler, height / scaler); video.hide(); preFrame = createImage(video.width, video.height); frameRate(10); } function draw() { video.loadPixels(); preFrame.loadPixels(); fill(255, 50); rect(0, 0, innerWidth, innerWidth); if (frameCount >= 10) { for (let y = 0; y < video.height; y++) { for (let x = 0; x < video.width; x++) { let index = (x + y * video.width) * 4; let pr = preFrame.pixels[index + 0]; let pg = preFrame.pixels[index + 1]; let pb = preFrame.pixels[index + 2]; let pbright = (pr + pg + pb) / 3; let r = video.pixels[index + 0]; let g = video.pixels[index + 1]; let b = video.pixels[index + 2]; let bright = (r + g + b) / 3; var diff = distSq(r, g, b, pr, pg, pb); if (diff * diff < 10000) { // fill(r, g, b); } else { fill(255, 0, 0, 50); drawHeatmap(x, y); } noStroke(); } } } preFrame.copy( video, 0, 0, video.width, video.height, 0, 0, video.width, video.height ); } const distSq = (x1, y1, z1, x2, y2, z2) => { let d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1); return d; }; const drawHeatmap = (x, y) => { rect(x * scaler, y * scaler, scaler, scaler); };
Результат с использованием плагина Live Server в коде Visual Studio (открытие HTML-файла в браузере также сделает эту работу):
Скриншоты:
Подсчет и сохранение перемещенных пикселей
var video; var scaler = 10; var preFrame; let motionCaptureArray = []; function setup() { createCanvas(640, 480); pixelDensity(1); video = createCapture(VIDEO); video.size(width / scaler, height / scaler); video.hide(); preFrame = createImage(video.width, video.height); frameRate(10); } function draw() { video.loadPixels(); preFrame.loadPixels(); if (motionCaptureArray.length === 0) { motionCaptureArray = preFrame.pixels; } // fill(255, 50); // rect(0, 0, innerWidth, innerWidth); if (frameCount >= 10) { for (let y = 0; y < video.height; y++) { for (let x = 0; x < video.width; x++) { let index = (x + y * video.width) * 4; let pr = preFrame.pixels[index + 0]; let pg = preFrame.pixels[index + 1]; let pb = preFrame.pixels[index + 2]; let pbright = (pr + pg + pb) / 3; let r = video.pixels[index + 0]; let g = video.pixels[index + 1]; let b = video.pixels[index + 2]; let bright = (r + g + b) / 3; var diff = distSq(r, g, b, pr, pg, pb); if (diff * diff < 10000) { // fill(r, g, b); } else { drawHeatmap(x, y); } noStroke(); } } } preFrame.copy( video, 0, 0, video.width, video.height, 0, 0, video.width, video.height ); } const distSq = (x1, y1, z1, x2, y2, z2) => { let d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1); return d; }; const drawHeatmap = (x, y) => { let index = (x + y * video.width) * 4; motionCaptureArray[index] = motionCaptureArray[index] + 1; // console.log(motionCaptureArray[index]); //textSize(10); text(motionCaptureArray[index], x * scaler, y * scaler); fill( motionCaptureArray[index], motionCaptureArray[index + 1], motionCaptureArray[index + 2] ); rect(x * scaler, y * scaler, scaler, scaler); };
Скриншоты:
Сопоставление перемещенныхпикселей с максимальным значением или временем
var video; const devices = []; var scaler = 7; var preFrame; let motionCaptureArray = []; let mapNumber = 1; function setup() { video = createCapture(VIDEO); createCanvas(840, 680); pixelDensity(1); video.size(width / scaler, height / scaler); video.hide(); preFrame = createImage(video.width, video.height); frameRate(10); } function draw() { image(video, 840, 0, width, height); video.loadPixels(); preFrame.loadPixels(); if (motionCaptureArray.length === 0) { motionCaptureArray = Array.prototype.slice.call(preFrame.pixels); } //mapNumber = frameCount; if (frameCount % 10 == 0) { mapNumber = Math.max(...motionCaptureArray); } if (frameCount >= 10) { for (let y = 0; y < video.height; y++) { for (let x = 0; x < video.width; x++) { let index = (x + y * video.width) * 4; let pr = preFrame.pixels[index + 0]; let pg = preFrame.pixels[index + 1]; let pb = preFrame.pixels[index + 2]; let r = video.pixels[index + 0]; let g = video.pixels[index + 1]; let b = video.pixels[index + 2]; var diff = distSq(r, g, b, pr, pg, pb); if (diff * diff < 50000) { //fill(r, g, b); } else { motionCaptureArray[index] = motionCaptureArray[index] + 1; } noStroke(); colorMode(HSB); fill( map(motionCaptureArray[index], 0, mapNumber, 0, 360), map(motionCaptureArray[index], 0, mapNumber, 0, 100), map(motionCaptureArray[index], 0, mapNumber, 0, 100) ); // text(motionCaptureArray[index], x * scaler, y * scaler); rect(x * scaler, y * scaler, scaler, scaler); colorMode(RGB); fill((r + g + b) / 3, 90); rect(x * scaler, y * scaler, scaler, scaler); } } } preFrame.copy( video, 0, 0, video.width, video.height, 0, 0, video.width, video.height ); } const distSq = (x1, y1, z1, x2, y2, z2) => { let d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1); return d; };
Скриншоты:
Заключение и следующие шаги
Это работает с постоянным источником света и неподвижной камерой.
Следующий шаг — использовать настоящий тепловой датчик или сделать то же самое с датчиками расстояния.
Следующим проектом также может быть генератор тепловых карт из импортированного видеофайла в качестве отдельного сервиса.
Репозиторий на гитхабе: