+
80
-

js如何实现摄像头实时绿布抠像?

请问js如何实现摄像头实时绿布抠像?

网友回复

+
1
-
绿布直接通过js计算rgb值来扣除并更换背景,项目代码可以查看 点击打开链接
+
0
-

可以试试tensorflow.js结合body-pix.js来识别人身体进行背景扣除,不需要绿屏就能扣除,非常强大,完整代码如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

<link type="text/css" rel="stylesheet" href="//repo.bfw.wiki/bfwrepo/css/bootstrap.4.3.1.min.css">
  <style>
      body {
    background: #20232a;
    color: #ffffff;
}


/* ============== Loading ============== */
@keyframes App-logo-spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

.loading {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 300px;
    height: 300px;
    transform: translate(-50%, -50%);
    text-align: center;
}

.loading img{
    width: 300px;
    height: 300px;
    animation: App-logo-spin infinite 20s linear;
}
/* ============== End Loading ============== */


.center-content {
    text-align: center;
}
  </style>
  </head>
  <body>
    <div id="root"></div>

<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/babel.min.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/react.production.16.13.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/react-dom.production.16.13.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/tf.2.0.js"></script>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/body-pix@2.0.js"></script>
    <script type="text/babel" >
        function Loading ({ children }) {
    return (
        <div className="loading">
            <img src="//repo.bfw.wiki/bfwrepo/icon/5f7abfbff367a.gif"/>
            <div>{ children }</div>
        </div>
    );
}
    </script>
    <script type="text/babel" >
        class App extends React.Component {
    constructor(props) {
        super(props);
        this.model = null;
        this.ctx = null;
        this.state = {
            modelReady: false,
            modelOption: {
                architecture: 'MobileNetV1',
                outputStride: 16,
                multiplier: 0.75,
                quantBytes: 2,
            },
            foregroundColor: {r: 0, g: 0, b: 0, a: 255},
            backgroundColor: {r: 0, g: 0, b: 0, a: 0},
            height: 400,
            width: 400,
            backgroundMode: 'video',
            backgroundSrc: '//repo.bfw.wiki/bfwrepo/video/5d8741d7df44b.mp4'
        };
        this.videoRef = React.createRef();
        this.canvasRef = React.createRef();
        this.backgroundRef = React.createRef();
        this.offCanvas = null;
    }

    /**
     * setState util
     * in case you need to await until state is set
     * @param {Object} newState - New state to update
     */
    asyncSetState = (newState) => {
        return new Promise(resolve => this.setState(newState, resolve));
    }

    /**
     * Call on every render frame:
     * - Estimate person mask
     * - Draw background and the person over
     */
    renderVideo = async () => {
        const { 
            model, 
            ctx, 
            videoRef, 
            canvasRef, 
            backgroundRef, 
            offCtx,
            offCanvas
        } = this;
        const background = backgroundRef.current;
        const video = videoRef.current;
        const canvas = canvasRef.current;
        const { foregroundColor, backgroundColor, height, width } = this.state;
        

        const segmentation = await model.segmentPerson(video, {
            flipHorizontal: false,
            internalResolution: 'medium',
            segmentationThreshold: 0.7
        });
        const personMasked = bodyPix.toMask(segmentation, foregroundColor, backgroundColor);
        // Draw background first if any
        if (background) {
            ctx.drawImage(background, 0, 0, width, height);
        }
        const oldGCO = offCtx.globalCompositeOperation
        // Prepare the mask, blend with webcam video
        offCtx.clearRect(0, 0, width, height);
        offCtx.putImageData(personMasked, 0, 0);
        offCtx.globalCompositeOperation = 'source-in';
        offCtx.drawImage(video, 0, 0);
        // Restore GCO
        offCtx.globalCompositeOperation = oldGCO;

        // Copy video with mask on top of background
        ctx.drawImage(offCanvas, 0, 0);

        // Next frame
        requestAnimationFrame(this.renderVideo);
    }

    /**
     * Load model and prepare webcam
     * @param {Object} options - Model options https://github.com/tensorflow/tfjs-models/tree/master/body-pix#config-params-in-bodypixload
     */
    loadModel = async (options) => {
        await this.asyncSetState({ modelReady: false });
        const model = await bodyPix.load();
        await this.asyncSetState({ modelReady: true });
        return model
    }

    /**
     * Request webcam permission
     */
    setupCamera = async () => {
        if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
            throw new Error(
                'Browser API navigator.mediaDevices.getUserMedia not available');
        }

        const video = this.videoRef.current;
        const stream = await navigator.mediaDevices.getUserMedia({
            'audio': false,
            'video': { facingMode: 'user' },
        });
        video.srcObject = stream;

        return new Promise((resolve) => {
            video.onloadedmetadata = () => {
                resolve(video);
            };
        });
    }

    /**
     * Load bodyPix model, setup webcam input and canvas output
     */
    setupApp = async () => {
        const { modelOption } = this.state;
        // 1. Load model
        this.model = await this.loadModel(modelOption);

        // 2. Setup camera
        const video = await this.setupCamera();
        const videoWidth = video.videoWidth;
        const videoHeight = video.videoHeight;
        video.width = videoWidth;
        video.height = videoHeight;
        video.play();

        // 3. Setup output canvas
        const canvas = this.canvasRef.current;
        canvas.width = videoWidth;
        canvas.height = videoHeight;
        const ctx = canvas.getContext('2d');
        this.ctx = ctx;

        // Set offCanvas size
        this.offCanvas = new OffscreenCanvas(videoWidth, videoHeight);
        this.offCtx = this.offCanvas.getContext('2d');

        this.setState({
            height: videoHeight,
            width: videoWidth
        })
        this.renderVideo();
    }

    /**
     * Handle file selector changes
     */
    handleFileChange = (evt) => {
        const { backgroundRef } = this;
        const { target: input } = evt;
        if (input.files && input.files[0]) {
            const file = input.files[0];
            const url = URL.createObjectURL(file);
            const { backgroundUrl: oldUrl } = this.state;
            const isVideo = file.type.indexOf('video') !== -1;

            // Revoke old obj url to free memory
            if (oldUrl) {
                URL.revokeObjectURL(oldUrl);
            }

            if (isVideo) {
                this.setState({
                    backgroundMode: 'video',
                    backgroundSrc: url
                }, () => {
                    backgroundRef.current.play();
                });
            } else {
                this.setState({
                    backgroundMode: 'image',
                    backgroundSrc: url
                });
            }
        }
    }

    async componentDidMount() {
        const { backgroundRef } = this;
        const { backgroundMode } = this.state;
        await this.setupApp();

        if (backgroundMode === 'video' && backgroundRef.current) {
            backgroundRef.current.play();
        }
    }

    render() {
        const { videoRef, canvasRef, backgroundRef, handleFileChange } = this;
        const { 
            modelReady,
            height, 
            width, 
            backgroundMode, 
            backgroundSrc 
        } = this.state;

        return (
            <div>
                { !modelReady ? 
                    <Loading>Please wait for model</Loading> 
                : 
                    <div className="container-fluid">
                        <div className="row">
                            <div className="webcam-source col-sm">
                                <video 
                                    playsinline 
                                    ref={ videoRef } 
                                    height={ height }
                                    width={ width }>
                                </video>
                            </div>
                            <div className="background col-sm">
                                { backgroundMode === 'image' ? 
                                    <img src={backgroundSrc} alt="background"
                                        height={ height }
                                        width={ width }
                                        ref={ backgroundRef }/> 
                                : 
                                    <video 
                                        playsinline
                                        autoplay
                                        loop
                                        muted
                                        src={backgroundSrc}
                                        height={ height }
                                        width={ width }
                                        ref={ backgroundRef }>    
                                    </video>
                                }
                                <input type="file"BfwOnChange={ handleFileChange } />
                            </div>
                        </div>
                        <div className="row">
                            <div className="output col-sm center-content">
                                <canvas 
                                    ref={ canvasRef }
                                    height={ height }
                                    width={ width }>
                                </canvas>
                            </div>
                        </div>
                    </div>
                }
            </div>
        )
    }
}
    </script>

    <script type="text/babel">
        ReactDOM.render(
            <App />,
            document.getElementById('root')
        );
    </script>
  </body>
</html>

+
0
-

还可以通过Seriously.js来实现,项目地址:点击打开链接

我知道答案,我要回答