LiveTalking icon indicating copy to clipboard operation
LiveTalking copied to clipboard

这个能流送带透明背景的视频吗,可以实时叠图换背景之类的

Open DESAKEY opened this issue 10 months ago • 7 comments

DESAKEY avatar Feb 20 '25 01:02 DESAKEY

用绿幕,在前端做叠加。 webrtc视频流不支持透明背景

lipku avatar Feb 20 '25 01:02 lipku

用绿幕,在前端做叠加。 webrtc视频流不支持透明背景

前端实时抠图效果一般 :-(

cacard avatar Feb 23 '25 00:02 cacard

感谢作者的分享 我用绿幕在前端做叠加成功分享给大家 1.修改client.js中的start方法修改代码为 pc = new RTCPeerConnection(config); pc.addEventListener('track', (evt) => { if (evt.track.kind == 'video') { const videoElement = document.getElementById('video'); videoElement.srcObject = evt.streams[0]; // 实时处理视频流 processVideoStream(videoElement); } else { document.getElementById('audio').srcObject = evt.streams[0]; } });

2然后在添加processVideoStream方法 代码如下:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.getElementById('media').appendChild(canvas);

videoElement.addEventListener('loadedmetadata', function () {
    canvas.width = videoElement.videoWidth;
    canvas.height = videoElement.videoHeight;

    const processFrame = () => {
        if (videoElement.paused || videoElement.ended) return;

        // 将当前帧绘制到 canvas
        ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;

// debugger

        // 遍历像素,进行抠图
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];     // 红色通道
            const g = data[i + 1]; // 绿色通道
            const b = data[i + 2]; // 蓝色通道

            // 判断是否为背景(绿色范围)
            if (g > 100 && r < 80 && b < 80) {
                // 将背景设置为透明
                data[i + 3] = 0; // alpha 通道设为 0(透明)
            }
        }

        // 更新处理后的图像数据
        ctx.putImageData(imageData, 0, 0);

// console.log(imageData.data); // 输出所有像素的 RGBA 数组

// // 将处理后的帧输出到新的视频流 // const stream = canvas.captureStream(); // outputVideo.srcObject = stream; // // // 继续处理下一帧 requestAnimationFrame(processFrame); };

    processFrame();
});

}

吃水不忘挖井人

agnils avatar Feb 24 '25 09:02 agnils

谢谢前辈提供的代码,但是我在执行过程还是碰到了一些问题,这是我修复之后的代码。 在html页面添加canvas元素

<canvas id="canvas"></canvas>

修改client.js文件

var pc = null;
var renderVideo = null;
var canvas = null;
var ctx = null;

//...

function init(){
    renderVideo = document.getElementById('video');
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
}

function start() {
    init();
    drawVideoFrame();
    // ...
}

// 移除绿色背景
function drawVideoFrame() {
    if (!renderVideo.paused && renderVideo.readyState >= renderVideo.HAVE_METADATA) {
        // 保持Canvas尺寸与视频一致
        if (canvas.width !== renderVideo.videoWidth || canvas.height !== renderVideo.videoHeight) {
            canvas.width = renderVideo.videoWidth;
            canvas.height = renderVideo.videoHeight;
        }
        // 清空画布并绘制视频帧
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(renderVideo, 0, 0, canvas.width, canvas.height);
        // 遍历像素,进行抠图
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];     // 红色通道
            const g = data[i + 1]; // 绿色通道
            const b = data[i + 2]; // 蓝色通道

            // 判断是否为背景(绿色范围)
            if (g > 180 && r < 210 && b < 210) {
                // 将背景设置为透明
                data[i + 3] = 0; // alpha 通道设为 0(透明)
            }
        }
        // 更新处理后的图像数据
        ctx.putImageData(imageData, 0, 0);
    }
    requestAnimationFrame(drawVideoFrame);
}

感谢作者的分享 我用绿幕在前端做叠加成功分享给大家 1.修改client.js中的start方法修改代码为 pc = new RTCPeerConnection(config); pc.addEventListener('track', (evt) => { if (evt.track.kind == 'video') { const videoElement = document.getElementById('video'); videoElement.srcObject = evt.streams[0]; // 实时处理视频流 processVideoStream(videoElement); } else { document.getElementById('audio').srcObject = evt.streams[0]; } });

2然后在添加processVideoStream方法 代码如下:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.getElementById('media').appendChild(canvas);

videoElement.addEventListener('loadedmetadata', function () {
    canvas.width = videoElement.videoWidth;
    canvas.height = videoElement.videoHeight;

    const processFrame = () => {
        if (videoElement.paused || videoElement.ended) return;

        // 将当前帧绘制到 canvas
        ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;

// debugger

        // 遍历像素,进行抠图
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];     // 红色通道
            const g = data[i + 1]; // 绿色通道
            const b = data[i + 2]; // 蓝色通道

            // 判断是否为背景(绿色范围)
            if (g > 100 && r < 80 && b < 80) {
                // 将背景设置为透明
                data[i + 3] = 0; // alpha 通道设为 0(透明)
            }
        }

        // 更新处理后的图像数据
        ctx.putImageData(imageData, 0, 0);

// console.log(imageData.data); // 输出所有像素的 RGBA 数组

// // 将处理后的帧输出到新的视频流 // const stream = canvas.captureStream(); // outputVideo.srcObject = stream; // // // 继续处理下一帧 requestAnimationFrame(processFrame); };

    processFrame();
});

}

吃水不忘挖井人

LangXiYi avatar Mar 14 '25 08:03 LangXiYi

建议使用以下方法判断是否绿色 const threshold = 50; function isGreen(r, g, b) { // 绿色的阈值可以调整,例如绿色通道至少比其他通道高一定比例 return g > r + threshold && g > b + threshold; }

Srammark avatar Apr 25 '25 12:04 Srammark

谢谢前辈提供的代码,但是我在执行过程还是碰到了一些问题,这是我修复之后的代码。 在html页面添加canvas元素

修改client.js文件

var pc = null; var renderVideo = null; var canvas = null; var ctx = null;

//...

function init(){ renderVideo = document.getElementById('video'); canvas = document.getElementById('canvas'); ctx = canvas.getContext('2d'); }

function start() { init(); drawVideoFrame(); // ... }

// 移除绿色背景 function drawVideoFrame() { if (!renderVideo.paused && renderVideo.readyState >= renderVideo.HAVE_METADATA) { // 保持Canvas尺寸与视频一致 if (canvas.width !== renderVideo.videoWidth || canvas.height !== renderVideo.videoHeight) { canvas.width = renderVideo.videoWidth; canvas.height = renderVideo.videoHeight; } // 清空画布并绘制视频帧 ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(renderVideo, 0, 0, canvas.width, canvas.height); // 遍历像素,进行抠图 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const r = data[i]; // 红色通道 const g = data[i + 1]; // 绿色通道 const b = data[i + 2]; // 蓝色通道

        // 判断是否为背景(绿色范围)
        if (g > 180 && r < 210 && b < 210) {
            // 将背景设置为透明
            data[i + 3] = 0; // alpha 通道设为 0(透明)
        }
    }
    // 更新处理后的图像数据
    ctx.putImageData(imageData, 0, 0);
}
requestAnimationFrame(drawVideoFrame);

}

感谢作者的分享 我用绿幕在前端做叠加成功分享给大家 1.修改client.js中的start方法修改代码为 pc = new RTCPeerConnection(config); pc.addEventListener('track', (evt) => { if (evt.track.kind == 'video') { const videoElement = document.getElementById('video'); videoElement.srcObject = evt.streams[0]; // 实时处理视频流 processVideoStream(videoElement); } else { document.getElementById('audio').srcObject = evt.streams[0]; } }); 2然后在添加processVideoStream方法 代码如下:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.getElementById('media').appendChild(canvas);

videoElement.addEventListener('loadedmetadata', function () {
    canvas.width = videoElement.videoWidth;
    canvas.height = videoElement.videoHeight;

    const processFrame = () => {
        if (videoElement.paused || videoElement.ended) return;

        // 将当前帧绘制到 canvas
        ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;

// debugger

        // 遍历像素,进行抠图
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];     // 红色通道
            const g = data[i + 1]; // 绿色通道
            const b = data[i + 2]; // 蓝色通道

            // 判断是否为背景(绿色范围)
            if (g > 100 && r < 80 && b < 80) {
                // 将背景设置为透明
                data[i + 3] = 0; // alpha 通道设为 0(透明)
            }
        }

        // 更新处理后的图像数据
        ctx.putImageData(imageData, 0, 0);

// console.log(imageData.data); // 输出所有像素的 RGBA 数组 // // 将处理后的帧输出到新的视频流 // const stream = canvas.captureStream(); // outputVideo.srcObject = stream; // // // 继续处理下一帧 requestAnimationFrame(processFrame); };

    processFrame();
});

} 吃水不忘挖井人 大佬,能提供修改后的js和html嘛?感谢感谢🙏

zhd5120153951 avatar Nov 05 '25 08:11 zhd5120153951

html和js抠图的代码你问问gpt都能生成;另外还可以使用 web gl shader 加速抠图,能提升几十倍的速度。

cacard avatar Nov 06 '25 08:11 cacard