如何将p5.js画布背景设置为摄像头画面(基于ml5.js PoseNet)
问题:将摄像头画面设为p5.js画布背景(PoseNet应用)
我正在使用ml5.js的PoseNet开发应用,该应用可将摄像头检测到的人物绘制为2D头像到p5.js画布中。目前摄像头视频与p5.js草图使用独立的canvas元素,我希望将p5.js画布的背景设置为摄像头画面。
我是否应该使用background();函数?我尝试将VIDEO和Video作为参数传入background();但并未生效,能否有人提供帮助?
以下是我的草图代码(假设已在HTML中引入p5.js和ml5.js):
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script> <script src="https://unpkg.com/ml5@0.4.3/dist/ml5.min.js"></script>
let nosex, nosey, learx, leary, rearx, reary, leyex, leyey, reyex, reyey, lhipx, lhipy, rhipx, rhipy, lsholx, lsholy, rsholx, rsholy, lelbx, lelby, relbx, relby, lwrix, lwriy, rwrix, rwriy, lankx, lanky, rankx, ranky, lknex, lkney, rknex, rkney, angel; function setup() { video = createCapture(VIDEO); video.size(550, 500); canvas = createCanvas(550, 550); canvas.position(560, 150); poseNet = ml5.poseNet(video, modelLoaded); poseNet.on('pose', gotPoses); angleMode(DEGREES); } function draw() { background("#ffffff"); fill('#ebca7f'); stroke('#ebca7f'); quad(nosex - ((learx + rearx) / 2) + rearx, nosey - 20, nosex + ((learx - rearx) / 2), nosey - 20, ((lsholx - rsholx) * 0.60) + rsholx, lsholy - 30, ((lsholx - rsholx) * 0.40) + rsholx, rsholy - 30); strokeWeight(20); fill(color); stroke('#000000'); quad(lhipx, lhipy, rhipx, rhipy, rsholx, rsholy - 30, lsholx, lsholy - 30); line(lsholx, lsholy, lelbx, lelby); line(lelbx, lelby, lwrix, lwriy); line(rsholx, rsholy, relbx, relby); line(relbx, relby, rwrix, rwriy); stroke('#000000'); line(lhipx, lhipy, lknex, lkney); line(lknex, lkney, lankx, lanky); line(rhipx, rhipy, rknex, rkney); line(rknex, rkney, rankx, ranky); angel = leary - reary; translate(nosex, nosey); rotate(angel / 2.5); rectMode(CENTER); strokeWeight(0); fill('#ffda82'); stroke('#ffda82'); ellipse(0, 0, rearx - learx * 1.1, rearx - learx * 1.1); translate(-nosex, -nosey); fill('#000000'); stroke('#000000'); rect(leyex, leyey, (rearx - learx) / 10, (rearx - learx) / 10); fill('#000000'); stroke('#000000'); rect(reyex, reyey, (rearx - learx) / 10, (rearx - learx) / 10); fill('#c7a75b'); stroke('#c7a75b'); rect(nosex, nosey, (rearx - learx) / 8, (rearx - learx) / 8); fill('#f0c3a5'); stroke('#f0c3a5'); rect(nosex, nosey + (rearx - learx * 1.1) * -0.194, (rearx - learx) / 1.5, (rearx - learx) / 10); fill('#734603'); stroke('#734603'); rect(nosex, nosey - (rearx - learx * 1.1) * -0.44444, (rearx - learx), (rearx - learx) * -0.2); } function modelLoaded() { console.log('PoseNet is Initialized'); } function gotPoses(results) { if (results.length > 0) { leary = results[0].pose.leftEar.y; learx = results[0].pose.leftEar.x; reary = results[0].pose.rightEar.y; rearx = results[0].pose.rightEar.x; leyey = results[0].pose.leftEye.y; leyex = results[0].pose.leftEye.x; reyey = results[0].pose.rightEye.y; reyex = results[0].pose.rightEye.x; nosey = results[0].pose.nose.y; nosex = results[0].pose.nose.x; lhipx = results[0].pose.leftHip.x; lhipy = results[0].pose.leftHip.y; rhipx = results[0].pose.rightHip.x; rhipy = results[0].pose.rightHip.y; lsholx = results[0].pose.leftShoulder.x; lsholy = results[0].pose.leftShoulder.y; rsholx = results[0].pose.rightShoulder.x; rsholy = results[0].pose.rightShoulder.y; lelbx = results[0].pose.leftElbow.x; lelby = results[0].pose.leftElbow.y; relbx = results[0].pose.rightElbow.x; relby = results[0].pose.rightElbow.y; lwrix = results[0].pose.leftWrist.x; lwriy = results[0].pose.leftWrist.y; rwrix = results[0].pose.rightWrist.x; rwriy = results[0].pose.rightWrist.y; lknex = results[0].pose.leftKnee.x; lkney = results[0].pose.leftKnee.y; rknex = results[0].pose.rightKnee.x; rkney = results[0].pose.rightKnee.y; lankx = results[0].pose.leftAnkle.x; lanky = results[0].pose.leftAnkle.y; rankx = results[0].pose.rightAnkle.x; ranky = results[0].pose.rightAnkle.y; } } function generateRainbowHex() { time = new Date().getTime(); frequency = 0.001; amplitude = 127; red = Math.sin(frequency * time + 0) * amplitude + 128; green = Math.sin(frequency * time + 2 * Math.PI / 3) * amplitude + 128; blue = Math.sin(frequency * time + 4 * Math.PI / 3) * amplitude + 128; hexCode = `#${Math.round(red).toString(16).padStart(2,'0')}${Math.round(green).toString(16).padStart(2,'0')}${Math.round(blue).toString(16).padStart(2,'0')}`; return hexCode; } setInterval(() => { color = generateRainbowHex(); }, 1);
解决方案
background()函数不能直接接收视频对象作为参数,正确的做法是用image()函数将摄像头画面绘制到整个画布上,替代原来的纯色背景。同时需要隐藏默认生成的摄像头元素,避免重复显示。
修改步骤:
- 隐藏默认摄像头元素:在
setup()里添加video.hide();,让createCapture创建的video元素不再显示在页面上。 - 替换背景绘制逻辑:把
draw()里的background("#ffffff");替换为image(video, 0, 0, width, height);,将摄像头画面铺满整个画布。 - 统一画布与视频尺寸:你的画布是550x550,视频是550x500,会导致画面拉伸,建议将画布尺寸改为和视频一致
createCanvas(550, 500);,避免变形。
修改后的完整代码:
let nosex, nosey, learx, leary, rearx, reary, leyex, leyey, reyex, reyey, lhipx, lhipy, rhipx, rhipy, lsholx, lsholy, rsholx, rsholy, lelbx, lelby, relbx, relby, lwrix, lwriy, rwrix, rwriy, lankx, lanky, rankx, ranky, lknex, lkney, rknex, rkney, angel; function setup() { video = createCapture(VIDEO); video.size(550, 500); video.hide(); // 隐藏默认摄像头元素 canvas = createCanvas(550, 500); // 统一画布与视频尺寸,避免拉伸 canvas.position(560, 150); poseNet = ml5.poseNet(video, modelLoaded); poseNet.on('pose', gotPoses); angleMode(DEGREES); } function draw() { // 用摄像头画面作为背景,铺满整个画布 image(video, 0, 0, width, height); fill('#ebca7f'); stroke('#ebca7f'); quad(nosex - ((learx + rearx) / 2) + rearx, nosey - 20, nosex + ((learx - rearx) / 2), nosey - 20, ((lsholx - rsholx) * 0.60) + rsholx, lsholy - 30, ((lsholx - rsholx) * 0.40) + rsholx, rsholy - 30); strokeWeight(20); fill(color); stroke('#000000'); quad(lhipx, lhipy, rhipx, rhipy, rsholx, rsholy - 30, lsholx, lsholy - 30); line(lsholx, lsholy, lelbx, lelby); line(lelbx, lelby, lwrix, lwriy); line(rsholx, rsholy, relbx, relby); line(relbx, relby, rwrix, rwriy); stroke('#000000'); line(lhipx, lhipy, lknex, lkney); line(lknex, lkney, lankx, lanky); line(rhipx, rhipy, rknex, rkney); line(rknex, rkney, rankx, ranky); angel = leary - reary; translate(nosex, nosey); rotate(angel / 2.5); rectMode(CENTER); strokeWeight(0); fill('#ffda82'); stroke('#ffda82'); ellipse(0, 0, rearx - learx * 1.1, rearx - learx * 1.1); translate(-nosex, -nosey); fill('#000000'); stroke('#000000'); rect(leyex, leyey, (rearx - learx) / 10, (rearx - learx) / 10); fill('#000000'); stroke('#000000'); rect(reyex, reyey, (rearx - learx) / 10, (rearx - learx) / 10); fill('#c7a75b'); stroke('#c7a75b'); rect(nosex, nosey, (rearx - learx) / 8, (rearx - learx) / 8); fill('#f0c3a5'); stroke('#f0c3a5'); rect(nosex, nosey + (rearx - learx * 1.1) * -0.194, (rearx - learx) / 1.5, (rearx - learx) / 10); fill('#734603'); stroke('#734603'); rect(nosex, nosey - (rearx - learx * 1.1) * -0.44444, (rearx - learx), (rearx - learx) * -0.2); } function modelLoaded() { console.log('PoseNet is Initialized'); } function gotPoses(results) { if (results.length > 0) { leary = results[0].pose.leftEar.y; learx = results[0].pose.leftEar.x; reary = results[0].pose.rightEar.y; rearx = results[0].pose.rightEar.x; leyey = results[0].pose.leftEye.y; leyex = results[0].pose.leftEye.x; reyey = results[0].pose.rightEye.y; reyex = results[0].pose.rightEye.x; nosey = results[0].pose.nose.y; nosex = results[0].pose.nose.x; lhipx = results[0].pose.leftHip.x; lhipy = results[0].pose.leftHip.y; rhipx = results[0].pose.rightHip.x; rhipy = results[0].pose.rightHip.y; lsholx = results[0].pose.leftShoulder.x; lsholy = results[0].pose.leftShoulder.y; rsholx = results[0].pose.rightShoulder.x; rsholy = results[0].pose.rightShoulder.y; lelbx = results[0].pose.leftElbow.x; lelby = results[0].pose.leftElbow.y; relbx = results[0].pose.rightElbow.x; relby = results[0].pose.rightElbow.y; lwrix = results[0].pose.leftWrist.x; lwriy = results[0].pose.leftWrist.y; rwrix = results[0].pose.rightWrist.x; rwriy = results[0].pose.rightWrist.y; lknex = results[0].pose.leftKnee.x; lkney = results[0].pose.leftKnee.y; rknex = results[0].pose.rightKnee.x; rkney = results[0].pose.rightKnee.y; lankx = results[0].pose.leftAnkle.x; lanky = results[0].pose.leftAnkle.y; rankx = results[0].pose.rightAnkle.x; ranky = results[0].pose.rightAnkle.y; } } function generateRainbowHex() { time = new Date().getTime(); frequency = 0.001; amplitude = 127; red = Math.sin(frequency * time + 0) * amplitude + 128; green = Math.sin(frequency * time + 2 * Math.PI / 3) * amplitude + 128; blue = Math.sin(frequency * time + 4 * Math.PI / 3) * amplitude + 128; hexCode = `#${Math.round(red).toString(16).padStart(2,'0')}${Math.round(green).toString(16).padStart(2,'0')}${Math.round(blue).toString(16).padStart(2,'0')}`; return hexCode; } setInterval(() => { color = generateRainbowHex(); }, 1);
内容的提问来源于stack exchange,提问作者AwesomeBillyBob
相关产品推荐
相关产品推荐

