将遮罩应用于 opencv 中的网络摄像头流

Apply mask to Webcam streaming in opencv

我使用 OpenCV 对视频源应用了掩码,并希望在网站上显示直播,但以下代码在开始后停止流式传输。我一直在绞尽脑汁,但找不到解决方案。任何帮助将不胜感激。

views.py

def gen(frame):
    while True:
        # frame = camera.get_frame()
        yield (b'--frame\r\n'
                b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
@api_view(['GET'])
def seasoncolor(request):
    while True:
        return StreamingHttpResponse(gen(color_detection.color_detection(0)),
                    content_type='multipart/x-mixed-replace; boundary=frame')  

color_detection.py

import numpy as np
import cv2
import sys
'''
ML object detection algo(haarcascade)used to identify objects. 
the XML file consists of trained Haar Cascade models.
'''

def color_detection(season):
    face_cascade = cv2.CascadeClassifier(
        'accounts/personal_color/self_detection/haarcascade_frontalface_default.xml')
        # 'accounts/personal_color/self_detection/haarcascade_frontalface_default.xml'
    # initialize video from the webcam
    video = cv2.VideoCapture(1)
    # Spring/summer/fall/winter
    while True:
        # ret tells if the camera works properly. Frame is an actual frame from the video feed
        ret, frame = video.read()
        # make sure port is working and read the image
        if frame is not None and video.isOpened():
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            '''
            Detect the faces within the subregions of the image in scales
            scaleFactor indicates how much the image size is reduced at each image scale.
            minNeighbors: Higher value results in higher quality of the detected face.
            '''
            faces = face_cascade.detectMultiScale(
            gray, scaleFactor=1.1, minNeighbors=6)
            # Draw the rectangle around each face
            for (x, y, w, h) in faces:
                # Use the stcoordinates to find the center of the face and from that point draw a rectangle of radius w/2 or h/2.
                center_coordinates = x + w // 2, y + h // 2
                radius = w // 2  # or can be h / 2 or can be anything based on your requirements
                # background color(black)
                mask = np.zeros(frame.shape[:2], dtype="uint8")
                # Draw the desired region to crop out in white
                cv2.circle(mask, center_coordinates, radius, (255, 255, 255), -1)
                masked = cv2.bitwise_and(frame, frame, mask=mask)
                if int(season) ==0: # Spring
                    # Replace all (0,0,0)channel with Coral pink
                    masked[np.where((masked == [0, 0, 0]).all(axis=2))] = [121, 131, 248]
                elif int(season) ==1: # Summer
                    #Replace all (0,0,0)channel with Rose Red  
                    masked[np.where((masked==[0,0,0]).all(axis=2))]=[86,30,194]
                elif int(season) ==2: # Fall
                    #Replace all (0,0,0)channel with Red Brown /Cinnamon
                    masked[np.where((masked==[0,0,0]).all(axis=2))]=[30,105,210]
                else: # Winter
                    #Replace all (0,0,0)channel with Burgundy Red
                    masked[np.where((masked==[0,0,0]).all(axis=2))]=[31,2,141]
                # cv2.imshow('mask applied', masked)
                ret, jpeg = cv2.imencode('.jpg', masked)
                return jpeg.tobytes()
            if cv2.waitKey(30) & 0xff == 27:
                break

    video.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
     arg = sys.argv[1]
     color_detection(arg)

Self_color_diagnosis.js

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import CameraScreen from './CameraScreen'; 
import { StyleSheet, Text, View, Image } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import axios from 'axios';

function Self_color_diagnosis({navigation,route}) {
return (
    <View style={styles.title_container}>
    <Image style={styles.video} source={{
        uri: 'http://localhost:8000/seasoncolor/',}}/>
        </View>

);
}

const styles = StyleSheet.create({
    video: {
        width: 500,
        height: 500
    },
    title_container: {
        flex: 1,
        justifyContent: 'center'
      },
  });

export default Self_color_diagnosis;

以上代码生成下图。流式传输停止并且根本没有改变。

gen() 运行s 循环,所有时间都使用相同的 frame().

你必须 frame 进入这个循环。

def gen():
    while True:
        frame = color_detection.color_detection(0)
        if frame:
            yield b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n'
        #else:
        #    print('no frame')

但是 color_detection 应该 运行 没有 loop
而且你应该只创建一次 VideoCapture(1)
即使您没有检测到任何人脸,您也应该 return frame

path = os.path.join(cv2.data.haarcascades, 'haarcascade_frontalface_default.xml')
face_cascade = cv2.CascadeClassifier(path)
    
video = cv2.VideoCapture(1)

def color_detection(season):
    # ret tells if the camera works properly. Frame is an actual frame from the video feed
    ret, frame = video.read()
    # make sure port is working and read the image
    if frame is not None and video.isOpened():
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        '''
        Detect the faces within the subregions of the image in scales
        scaleFactor indicates how much the image size is reduced at each image scale.
        minNeighbors: Higher value results in higher quality of the detected face.
        '''
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=6)

        # Draw circle around each face
        for (x, y, w, h) in faces:
            # Use the stcoordinates to find the center of the face and from that point draw a rectangle of radius w/2 or h/2.
            center_coordinates = x + w // 2, y + h // 2
            radius = w // 2  # or can be h / 2 or can be anything based on your requirements
            # background color(black)
            mask = np.zeros(frame.shape[:2], dtype="uint8")
            # Draw the desired region to crop out in white
            cv2.circle(mask, center_coordinates, radius, (255, 255, 255), -1)
            masked = cv2.bitwise_and(frame, frame, mask=mask)
            if season == 0: # Spring
                # Replace all (0,0,0)channel with Coral pink
                masked[np.where((masked == [0, 0, 0]).all(axis=2))] = [121, 131, 248]
            elif season == 1: # Summer
                #Replace all (0,0,0)channel with Rose Red  
                masked[np.where((masked==[0,0,0]).all(axis=2))] = [86,30,194]
            elif season == 2: # Fall
                #Replace all (0,0,0)channel with Red Brown /Cinnamon
                masked[np.where((masked==[0,0,0]).all(axis=2))] = [30,105,210]
            else: # Winter
                #Replace all (0,0,0)channel with Burgundy Red
                masked[np.where((masked==[0,0,0]).all(axis=2))] = [31,2,141]
            ret, jpeg = cv2.imencode('.jpg', masked)
        else: # it is `for/else` construction, not `if/else`
            ret, jpeg = cv2.imencode('.jpg', frame)
    
        return jpeg.tobytes()

    #return None

顺便说一句:

我看到了其他问题。当它检测到多张脸时,它会为每张脸创建新的面具,并将每个面具分配给原始图像——所以每个面具都会跳过以前的面具——所以它应该只显示最后一张脸,并隐藏其他脸。您应该首先创建一个包含所有圆圈的蒙版,然后在图像上使用它。


编辑:

我不知道您使用的是什么 Web 框架,所以我使用 Flask 创建了最小的工作示例。

import os
from flask import Flask, Response
import cv2
import numpy as np

app = Flask(__name__)

print('\n'.join(sorted(os.listdir(cv2.data.haarcascades))))
    
path = os.path.join(cv2.data.haarcascades, 'haarcascade_frontalface_default.xml')
face_cascade = cv2.CascadeClassifier(path)
    
#video = cv2.VideoCapture(0)  # my webcam
video = cv2.VideoCapture(0)   # your webcam

def color_detection(season):    
    ret, frame = video.read()

    if frame is not None and video.isOpened():
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=6)

        # test two circles on image 640x480
        #faces = [[100, 100, 250, 250], [640-100-250, 480-100-250, 250, 250]]

        if len(faces) > 0:  # it can be `if faces:` because `faces` is `numpy array` which need different method to check if not empty
            # background color(black)
            mask = np.zeros(frame.shape[:2], dtype="uint8")
            
            # draw all circles on mask
            for (x, y, w, h) in faces:
                #print(x, y, w, h)
                
                # use the coordinates to find the center of the face and from that point draw a rectangle of radius w/2 or h/2.
                center_coordinates = x + w // 2, y + h // 2
                radius = max(w, h) // 2  # or can be h / 2 or can be anything based on your requirements
                
                # draw the desired region to crop out in white
                cv2.circle(mask, center_coordinates, radius, (255, 255, 255), -1)

            # use mask with all circles
            masked = cv2.bitwise_and(frame, frame, mask=mask)
            
            if season == 0: # Spring - Coral pink
                color = [121, 131, 248]
            elif season == 1: # Summer - Rose Red  
                color = [86,30,194]
            elif season == 2: # Fall - Red Brown /Cinnamon
                color = [30,105,210]
            else: # Winter - Burgundy Red
                color = [31,2,141]

            masked[np.where((masked == [0,0,0]).all(axis=2))] = color
        else:  # no faces
            masked = frame
                        
        ret, jpeg = cv2.imencode('.jpg', masked)
        return jpeg.tobytes()
    

def gen():
    while True:
        frame = color_detection(0)
        if frame:
            yield (b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
        #else:
        #    print('no frame')
                    
                    
@app.route('/')
def index():
    return '<image src="/seasoncolor">'
                    
@app.route('/seasoncolor')
def seasoncolor():
    return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
    
    
if __name__ == '__main__':
    #app.debug = True
    app.run()

顺便说一句:

确保:VideoCapture 只能与您 运行 网络服务器所在的本地摄像头一起使用。它不能与用户计算机上的远程摄像头一起使用。只有用户的浏览器可以访问其相机。要在用户计算机上使用远程摄像头,您必须使用 JavaScript 在用户浏览器中访问摄像头并将帧发送到服务器 - 就像我在 GitHub python-examples 中的示例:web camera in browser - canvas - take image and upload to server