【编程菜谱系列一】手把手教你用废旧手机改造为人脸识别监控

1 篇文章 0 订阅
1 篇文章 0 订阅

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

菜谱编程,照着菜谱做,你也能实现我相同的效果。
条条大路通罗马,我的方法并不一定是最好的,只是个人一些技术总结,仅供参考


一、准备食材

1、livego下载【负责配置本地直播环境】
2、uniapp下载【负责手机端直播推流使用】
3、Anaconda下载【负责python Opencv包管理】
4、OBS Studio下载【负责推流直播的测试工作】【可选】
5、VLC media player下载【负责测试直播环境的拉流是否成功】【可选】

二、配置直播环境

1.下载:livego编译包 https://github.com/gwuhaolin/livego/releases

根据你的工作环境选择相应的,这里默认使用win环境

在这里插入图片描述
win版用法:
下载完成之后:
在这里插入图片描述
1、打开:livego.exe 程序

你会看到程序正在运行

time="2022-06-15T12:56:11+08:00" level=warning msg="open livego.yaml: The system cannot find the file specified."
time="2022-06-15T12:56:11+08:00" level=info msg="Using default config"
time="2022-06-15T12:56:11+08:00" level=info msg="\n     _     _            ____       \n    | |   (_)_   _____ / ___| ___  \n    | |   | \\ \\ / / _ \\ |  _ / _ \\ \n    | |___| |\\ V /  __/ |_| | (_) |\n    |_____|_| \\_/ \\___|\\____|\\___/ \n        version: master\n\t"
time="2022-06-15T12:56:11+08:00" level=info msg="HLS listen On :7002"
time="2022-06-15T12:56:11+08:00" level=info msg="HTTP-API listen On :8090"
time="2022-06-15T12:56:11+08:00" level=info msg="HLS server enable...."
time="2022-06-15T12:56:11+08:00" level=info msg="HTTP-FLV listen On :7001"
time="2022-06-15T12:56:11+08:00" level=info msg="RTMP Listen On :1935"

表示运行成功,参考说明文档
在这里插入图片描述2、打开浏览器,输入url (“http://localhost:8090/control/get?room=movie”)
你会看到
{"status":200,"data":"rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk"}
表示环境已经运行成功了

1.测试直播环境是否有效

下载
OBS Studio下载【负责推流直播的测试工作】【可选】
VLC media player下载【负责测试直播环境的拉流是否成功】【可选】
随便一部.mp4视频

1、打开 OBS软件
在这里插入图片描述
2、打开设置页面输入推流url与key值(key值,也就是一开始打开浏览器的的值)
{"status":200,"data":"rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk"}
推流url:如果没有特殊需求可以直接输入:rtmp://localhost:1935/live/ 默认推流url地址
输入对应的值记得保存
在这里插入图片描述
3、选择mp4文件开始推流

在这里插入图片描述
在这里插入图片描述
表示推流已经成功
测试直播是否有效
打开VLC media player下载【负责测试直播环境的拉流是否成功】【可选】
输入拉流地址
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
到这里表示直播推拉流基本功能实现了

三.配置Anaconda环境(懂得可以直接跳过)

下载Anaconda下载【负责python Opencv包管理】
注意* 下载Anaconda不是必须的,只是因为纯粹为了好管理python的包,Opencv才是这次做菜关键的目的

1.安装Anaconda完成之后,打开Anaconda Navigator (conda).exe

在这里插入图片描述
2.PyCharm 关联 Anaconda环境
打开 PyCharm
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

测试opencv是否成功(示例):

import cv2 as cv
#读取图片
img = cv.imread('1.jpeg')
#暂示图片
cv.imshow('ceshi',img)
#关闭窗口
cv.waitKey(0)
cv.destroyAllWindows()

可以看到图片代表opencv有效
在这里插入图片描述


四.uniapp部分——写一个可以直播的app装手机上

uni-app写一个可以推流的app装在手机上下面是文档说明:
https://uniapp.dcloud.io/api/media/live-player-context.html
在这里插入图片描述

uniapp部分:先创建一个首页,然后跳转到创建直播,然后再跳转到直播间进行推流。
而负责拉流的部分,则是使用下面部分的python代码,把拉流地址填上运行即可。

uniapp:手机app首页部分


<template>
	<view class="content">
		<button type="default" @click="createlive()">创建我的直播间</button>
	</view>
</template>

<script>
	export default {
		data() {
			return {	
			}
		},
		onLoad() {
		},
		methods: {
			createlive(){
				uni.redirectTo({
					url:'../../components/createlive/createlive'
				})
			}
		}
	}
</script>
<style lang="scss">
</style>

创建直播间

<template>
	<view>
		<live-pusher
		id='livePusher'
		ref="livePusher" 
		class="livePusher" 
		:url="url"
		:mode="mode" 
		:enable-camera="enableCamera" 
		:auto-focus="true" 
		:device-position="position"
		:beauty="beauty" 
		:whiteness="whiteness"
		aspect="9:16" 
		@statechange="statechange" 
		@netstatus="netstatus" 
		@error = "error"
		:style="'height: '+windowHeight+'px;'"
		style="width: 750rpx;"></live-pusher>
		
		
		<view v-if="showBars" >
			
			<view style="position: fixed;left: 0;right: 0;height: 500rpx;" :style="'top:'+statusBarHeight+'px;'">
				<view class="flex align-center justify-center" style="width: 90rpx;height: 90rpx;" @click="back">
					<text class="iconfont text-white">X</text>
				</view>
				
				<view class="position-absolute rounded p-2 flex align-center" style="left: 90rpx;right: 100rpx;height: 160rpx;background-color: rgba(0,0,0,0.2);">
					<view style="height: 120rpx;width: 120rpx;" class="position-relative rounded" @click="chooseCover">
						<image :src="form.cover || '/static/gift/3.png'" style="height: 120rpx;width: 120rpx;"></image>
						<text class="text-white position-absolute font" style="left: 0;right: 0;bottom: 0;">更换封面</text>
					</view>
					<view class="flex-1 ml-2">
						<input type="text" v-model="form.title" placeholder="请输入直播标题" class="mb-2"/>
						<!-- <text class="text-white font">#请选择分类</text> -->
					</view>
				</view>
				
				<view class="position-absolute right-0 flex flex-column " style="width: 100rpx;" >
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="switchCamera">
						<text class="iconfont text-white mb-1">切换  镜头</text>
						<!-- <text class="text-white font">翻转</text> -->
					</view>
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="openPopup('mode')">
						<text class="iconfont text-white mb-1">设置  画质</text>
						<!-- <text class="text-white font">画质</text> -->
					</view>
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="openPopup('beauty')">
						<text class="iconfont text-white mb-1">设置  美颜</text>
						<!-- <text class="text-white font">美颜</text> -->
					</view>
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="openPopup('whiteness')">
						<text class="iconfont text-white mb-1">设置  美白</text>
						<!-- <text class="text-white font">美白</text> -->
					</view>
				</view>
				
			</view>
			
			
			<view class="position-fixed bg-main flex align-center justify-center rounded-circle" style="left: 100rpx;right: 100rpx;bottom: 100rpx;height: 120rpx;" @click="openLiveRoom">
				<text class="text-white font-md">开始视频直播</text>
			</view>
			
			
			<uni-popup type="bottom" ref="popup">
				<view class="bg-white">
					<view class="flex align-center justify-center border-bottom" style="height: 90rpx;">
						<text class="font-md">{{popupTitle}}</text>
					</view>
					<!-- 画质选择 -->
					<view v-if="popupType === 'mode'">
						<view class="flex align-center justify-center py-2" v-for="(item,index) in modeList" :key="index" :class="mode === item.type ? 'bg-main' : ''" @click="chooseMode(item)">
							<text class="font-md" :class="mode === item.type ? 'text-white' : ''">{{item.desc}}</text>
						</view>
					</view>
					<!-- 美颜 -->
					<view v-else-if="popupType === 'beauty'">
						<slider :min="0" :max="9" :step="1" :value="beauty" :block-size="18" show-value @change="handleSliderChange"/>
					</view>
					<!-- 美白 -->
					<view v-else>
						<slider :min="0" :max="9" :step="1" :value="whiteness" :block-size="18" show-value @change="handleSliderChange"/>
					</view>
					<view class="f-divider"></view>
					<view  class="flex align-center justify-center"
					style="height: 90rpx;" hover-class="bg-light"
					@click="closePopup">
						<text class="font-md">取消</text>
					</view>
				</view>
			</uni-popup>
		</view>
		
	</view>
</template>

<script>
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
		components: {
			uniPopup
		},
		data() {
			return {
				url:"",
				mode:"SD",
				enableCamera:true,
				position:"back",
				beauty:0,
				whiteness:0,
				windowHeight:0,
				context:null,
				statusBarHeight:0,
				modeList:[{
					type:"SD",
					desc:"标清"
				},{
					type:"HD",
					desc:"高清"
				},{
					type:"FHD",
					desc:"超清"
				}],
				popupType:"mode",
				showBars:true,
				form:{
					title:"",
					cover:"https://profile-avatar.csdnimg.cn/c213bed6000f404fa2dd41a6ed271fe3_qq_40750573.jpg!2"
				}
			}
		},
		onLoad() {
			let res = uni.getSystemInfoSync()
			this.windowHeight = res.windowHeight
			this.statusBarHeight = res.statusBarHeight
		},
		computed: {
			popupTitle() {
				let o = {
					mode:"画质",
					beauty:"美颜",
					whiteness:"美白",
				}
				return o[this.popupType]
			}
		},
		onReady() {
			this.context = uni.createLivePusherContext('livePusher', this)
			this.startPreview()
		},
		onBackPress() {
			this.showBars = false
		},
		methods: {
			chooseCover(){
				uni.chooseImage({
					count:1,
					success: (res) => {
						$H.upload('/upload',{
							filePath:res.tempFilePaths[0]
						},(p)=>{
							console.log(p);
						}).then(res=>{
							this.form.cover = $C.imageUrl + res.url
						})
					}
				})
			},
			back(){
				uni.reLaunch({
				    url: '/pages/index/index'
				});
			},
			// 画质选择
			chooseMode(item){
				this.mode = item.type
				uni.showToast({
					title: '画质切换为' + item.desc,
					icon: 'none'
				});
				this.$refs.popup.close()
			},
			openPopup(type){
				this.popupType = type
				this.$refs.popup.open()
			},
			closePopup(){
				this.$refs.popup.close()
			},
			// 切换摄像头
			switchCamera(){
				this.context.switchCamera({
					success:(e)=>{
						this.position = this.position === 'back' ? 'front' : 'back'
					}
				})
			},
			// 开启预览
			startPreview(){
				this.context.startPreview({
					success:(e)=>{
						console.log(e);
					}
				})
			},
			// 直播状态变化
			statechange(e){
				console.log(e);
			},
			// 直播网络变化
			netstatus(e){
				console.log(e);
			},
			error(e){
				console.log(e);
			},
			handleSliderChange(e){
				this[this.popupType] = e.detail.value
			},
			openLiveRoom(){
					let options = {
						mode:this.mode,
						position:this.position,
						beauty:this.beauty,
						whiteness:this.whiteness
					}  
					uni.redirectTo({
						url: '../liveing/liveing'
					});
			}
		}
	}
</script>

<style>

</style>

开始直播
这是推流地址:rtmp://localhost:1935/live/rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk(仅供参考)

<template>
	<view class="page">
		
		<live-pusher
		id='livePusher'
		ref="livePusher" 
		class="livePusher" 
		:url="mysrc"
		:mode="mode" 
		:muted="false"
		:enable-camera="enableCamera" 
		:auto-focus="true" 
		:device-position="position"
		:beauty="beauty" 
		:whiteness="whiteness"
		aspect="9:16" 
		:orientation="fangxaing"
		:local-mirror='yulan'
		:enable-mic='maikefeng'
		:min-bitrate='malv'
		:audio-quality='hezi'
		:audio-volume-type='yinliang'
		:enable-agc='true'
		:enable-ans='true'
		audio-reverb-type=6	
		@statechange="statechange" 
		@netstatus="netstatus" 
		@error = "error"
		:style="'height: '+windowHeight+'px;'"
		style="width: 750rpx;"></live-pusher>
	
		<!-- 头部 -->
		<view style="position: fixed;left: 0;right: 0;" :style="'top:'+statusBarHeight+'px'">
			<!-- 个人信息|观看详细信息 -->
			<view style="height: 80rpx;" class="px-2 flex justify-between align-center">
				<view style="width: 325rpx;background-color: rgba(0,0,0,0.4);" class="flex rounded-circle">
					<view class="p">
						<image :src="detail.user.avatar || '/static/tabbar/min.png'" style="width: 70rpx;height: 70rpx;" class="rounded-circle"></image>
					</view>
					<view class="flex-1 flex flex-column justify-center">
						<text class="text-white font">{{ detail.user.nickname || detail.user.username }}</text>
						<text class="text-white font-sm">{{ detail.look_count }}</text>
					</view>
					<view class="p">
						<view class="rounded-circle flex align-center justify-center bg-danger" style="width: 70rpx;height: 70rpx;">
							<text class="text-white">+</text>
						</view>
					</view>
				</view>
				
				<view style="width: 325rpx;background-color: rgba(0,0,0,0.4);" class="flex rounded-circle">
					<scroll-view scroll-x="true" class="flex-1 flex">
						<view class="p" v-for="(item,index) in list" :key="index">
							<image :src="item.avatar || '/static/tabbar/min.png'" style="width: 70rpx;height: 70rpx;" class="rounded-circle"></image>
						</view>
					</scroll-view>
					<view class="p">
						<view class="rounded-circle flex align-center justify-center bg-danger" style="width: 70rpx;height: 70rpx;">
							<text class="text-white font-sm">{{ list.length }}</text>
						</view>
					</view>
				</view>
			</view>
			<!-- 金币 -->
			<view style="height: 80rpx;" class="px-2 my-2"  >
				<view style="width: 325rpx;background-color: rgba(0,0,0,0.4);" class="flex rounded-circle align-center">
					<view class="p">
						<text class="text-warning">金币</text>
					</view>
					<view class="flex-1 flex flex-column justify-center">
						<text class="text-white font">{{ detail.coin }}</text>
					</view>
				</view>
			</view>
			<!-- 收到礼物 -->
		<!-- 	<f-gift ref="gift"></f-gift> -->
		</view>
		
		<!-- 弹幕 -->
		<view style="position: fixed;bottom: 120rpx;left: 0;right: 0;">
			<scroll-view scroll-y="true" style="width: 520rpx;height: 300rpx;" scroll-with-animation class="pl-3" :scroll-into-view="scrollInToView">
				<view :id="'danmu'+item.id" class="flex justify-start align-start rounded p-2 mb-2" style="background-color: rgba(255,255,255,0.125);" v-for="(item,index) in listdan" :key="index">
					<text class="font-md text-danger">{{item.name}}</text>
					<text class="font-md text-white">{{item.content}}</text>
				</view>
			</scroll-view>
		</view>
		
		<!-- 底部 -->
		<view style="position: fixed;left: 0;bottom: 0;right: 0;height: 120rpx;" class="flex align-center justify-between">
			
			<view class="flex-1 flex flex-column align-center justify-center" v-for="(item,index) in btns" :key="index" @click="handleBottomEvent(item)">
				<text class="iconfont text-white mb-1">{{item.icon}}</text>
				<text class="text-white font">{{item.name}}</text>
			</view>
		</view>
		
		<uni-popup type="bottom" ref="popup">
			<view class="bg-white">
				<view class="flex align-center justify-center border-bottom" style="height: 90rpx;">
					<text class="font-md">{{popupTitle}}</text>
				</view>
				<!-- 画质选择 -->
				<view v-if="popupType === 'mode'">
					<view class="flex align-center justify-center py-2" v-for="(item,index) in modeList" :key="index" :class="mode === item.type ? 'bg-main' : ''" @click="chooseMode(item)">
						<text class="font-md" :class="mode === item.type ? 'text-white' : ''">{{item.desc}}</text>
					</view>
				</view>
				<!-- 美颜 -->
				<view v-else-if="popupType === 'beauty'">
					<slider :min="0" :max="9" :step="1" :value="beauty" :block-size="18" show-value @change="handleSliderChange"/>
				</view>
				<!-- 美白 -->
				<view v-else-if="popupType === 'whiteness'">
					<slider :min="0" :max="9" :step="1" :value="whiteness" :block-size="18" show-value @change="handleSliderChange"/>
				</view>
				<!-- 更多 -->
				<view v-else class="flex flex-wrap">
					<view class="flex flex-column align-center justify-center" style="width: 150rpx;height: 150rpx;" @click="pauseOrPlay">
						<text class="iconfont mb-1">&#xe611;</text>
						<text class="font">{{ isPause ? '继续直播' : '暂停直播' }}</text>
					</view>
					<view class="flex flex-column align-center justify-center" style="width: 150rpx;height: 150rpx;" @click="back">
						<text class="iconfont mb-1">直播</text>
						<text class="font">退出</text>
					</view>
				</view>
				
				<view class="f-divider"></view>
				<view  class="flex align-center justify-center"
				style="height: 90rpx;" hover-class="bg-light"
				@click="closePopup">
					<text class="font-md">取消</text>
				</view>
			</view>
		</uni-popup>
		
	</view>
</template>

<script>
	import fGift from '@/components/live/f-gift.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	import { mapState } from 'vuex';
	export default {
		components: {
			fGift,
			uniPopup
		},
		data() {
			return {
				mysrc:"rtmp://192.168.3.2:1935/live/rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk",//推流地址,也就是OBS设置的那个部分
				statusBarHeight:0,
				content:"",
				gifts:[],
				giftActiveId:0,
				fangxaing:'vertical',//屏幕头方向
				yulan:'auto',
				malv:'200',//最小码率。
				hezi:'low',//声音赫兹,音质
				maikefeng:'true',//是否开启麦克风
				yinliang:'media',//音量类型
				mode:"SD",
				enableCamera:true,
				position:"back",
				beauty:0,
				whiteness:0,
				windowHeight:0,
				context:null,
				scrollInToView:"",
				listdan: [],
				modeList:[{
					type:"SD",
					desc:"标清"
				},{
					type:"HD",
					desc:"高清"
				},{
					type:"FHD",
					desc:"超清"
				}],
				popupType:"mode",
				
				btns:[{
					name:"翻转",
					icon:"",
					event:"switchCamera"
				},{
					name:"画质",
					icon:"",
					event:"openPopup",
					params:"mode"
				},{
					name:"美颜",
					icon:"",
					event:"openPopup",
					params:"beauty"
				},{
					name:"美白",
					icon:"",
					event:"openPopup",
					params:"whiteness"
				},{
					name:"更多",
					icon:"",
					event:"openPopup",
					params:"more"
				}],
				detail:{
					"created_time": "",
					"id": 0,
					"title": "",
					"cover": "",
					"user_id": 0,
					"look_count": 0,
					"coin": 0,
					"key": "",
					"status": 0,
					"userId": 0,
					"user": {
						"id": 0,
						"username": "XXXXXX的直播间",
						"avatar": "https://profile-avatar.csdnimg.cn/c213bed6000f404fa2dd41a6ed271fe3_qq_40750573.jpg!2"
					}
				},
				sign:"",
				list:[{'avatar':'http://img2.woyaogexing.com/2022/03/31/4730b59516814a9c9b375492fcde1a69!400x400.jpeg'},{'avatar':'https://profile-avatar.csdnimg.cn/c213bed6000f404fa2dd41a6ed271fe3_qq_40750573.jpg!2'},{'avatar':'http://img2.woyaogexing.com/2022/03/31/bd0cc5184f8c4a4f84bfdb69982c633e!400x400.jpeg'},{'avatar':'http://img2.woyaogexing.com/2022/03/31/f98b46592727466083b857321fd37d90!400x400.jpeg'}],
				
				// 是否开始推流
				isStart:false,
				isPause:false,
				isget:false
			}
		},
		onReady() {
			this.context = uni.createLivePusherContext('livePusher', this)
			this.startPreview()
			// 开始推流
			 this.start()
		},
		onLoad(e) {
			let res = uni.getSystemInfoSync()
			this.statusBarHeight = res.statusBarHeight
			this.windowHeight = res.windowHeight
			
			uni.connectSocket({
			  url: 'ws://192.168.3.28:8282'
			});
			uni.onSocketError(function (res) {
			  console.log('WebSocket连接打开失败,请检查!');
			});
			uni.onSocketMessage(function (res) {
			  console.log('收到服务器内容:' + res.data);
			});
			uni.onSocketOpen(function (res) { 
			// 	//发送id绑定workerman给予缓存
				let mgg = {type:'zhibo_bing','ymuid':1,'uid':0,'iszhubo':1};				 
				uni.sendSocketMessage({
					data:JSON.stringify(mgg),
				});
			});
			this.workerman();
		},
		mounted() {
			setInterval(()=>{
				uni.sendSocketMessage({
					data:JSON.stringify({type:'refreshServer'})
				});
			},3000)
		},
		destroyed() {
		},
		computed: {
			popupTitle() {
				let o = {
					mode:"画质",
					beauty:"美颜",
					whiteness:"美白",
					more:"更多"
				}
				return o[this.popupType]
			},
		},
		onBackPress() {
			if(!this.isget){
				this.back()
				return true
			}
		},
		methods: {
			// 简单粗暴:监听是否掉线
			onSocket(){
				uni.onSocketClose(function (res) {
					 console.log('WebSocket 已关闭');
					uni.connectSocket({
					  url: this.WokerUrl,
					});
					uni.onSocketOpen(function (res) { 
					  console.log("链接成功"); 
					});
				});
			},
			//接收消息
			workerman(){
				let that = this ;
				let id = 1 ;
				uni.onSocketMessage(function (res) {
					 var data = JSON.parse(res.data);
					if(data.type == 'zhubo'){
						var gta = {
							id:id,
							name:"系统提示",
							content:data.msg
						};
						that.senddata(gta);
					}else if(data.type == 'guanzong'){
						var gta = {
							id:id,
							name:data.name,
							content:data.msg
						};
						that.senddata(gta);
					}
					 id++;
				});
			},
			// 发送弹幕
			 senddata(data) {
				this.listdan.push(data)
				// 置于底部
				this.toBottom()
			},
			toBottom() {
				setTimeout(()=>{
					let len = this.listdan.length
					if(len > 0 && this.listdan[len - 1]){
						this.scrollInToView = 'danmu' + this.listdan[len - 1].id
					}
				},300)
			},
			
			pauseOrPlay(){
				if(!this.isPause){
					return uni.showModal({
						content: '是否要暂停推流?',
						success: (res)=>{
							if (res.cancel) {
								return
							}
							this.pause()
						}
					});
				}
				// 继续推流
				this.resume()
			},
			// 退出直播
			back(){
				uni.showModal({
					content: '是否要退出直播间?',
					success: (res)=> {
						if (res.cancel) {
							return
						}
						this.stop()
						this.isget = true
						uni.reLaunch({
						    url: '/pages/index/index'
						});
						uni.showToast({
							title: '退出直播间成功',
							icon: 'none'
						});
					}
				});
			},
			// 开始推流
			start(){
				this.context.start({
					success:(e)=>{
						console.log("开始推流" + JSON.stringify(e));
						this.isStart = true
					}
				})
			},
			// 暂停推流
			pause(){
				this.context.pause({
					success:()=>{
						this.isPause = true
					}
				})
			},
			// 继续推流
			resume(){
				this.context.resume({
					success:()=>{
						this.isPause = false
					}
				})
			},
			stop(){
				this.context.stop({
					success:()=>{
						this.isStart = false
					}
				})
			},
			handleLiveEvent(e){
				let d = e.data
				switch (e.type){
					case 'online':
					if(d.action === 'join'){
						this.list = d.data
					}
						break;
					case 'comment':
					this.$refs.danmu.send({
						id:d.id,
						name:d.user.name,
						content:d.content
					})
						break;
					case 'gift':
					this.detail.coin += d.gift_coin * d.num
					this.$refs.gift.send(d)
						break;
					default:
						break;
				}
			},
			// 加入或离开直播间
			joinOrLeaveLive(type){
				if(this.socket && this.token){
					this.socket.emit( type + 'Live',{
						live_id:this.detail.id,
						token:this.token
					})
				}
			},
			handleBottomEvent(item){
				this[item.event](item.params)
			},
			// 画质选择
			chooseMode(item){
				this.mode = item.type
				uni.showToast({
					title: '画质切换为' + item.desc,
					icon: 'none'
				});
				this.$refs.popup.close()
			},
			openPopup(type){
				this.popupType = type
				this.$refs.popup.open()
			},
			closePopup(){
				this.$refs.popup.close()
			},
			// 切换摄像头
			switchCamera(){
				this.context.switchCamera({
					success:(e)=>{
						this.position = this.position === 'back' ? 'front' : 'back'
					}
				})
			},
			// 开启预览
			startPreview(){
				this.context.startPreview({
					success:(e)=>{
					}
				})
			},
			// 直播状态变化
			statechange(e){
			},
			// 直播网络变化
			netstatus(e){
			},
			error(e){
			},
			handleSliderChange(e){
				this[this.popupType] = e.detail.value
			},
		}
	}
</script>

<style>
.page{
	flex: 1;
}
.btn{
	height: 80rpx;
	border-radius: 100rpx;
	background-color: rgba(255,255,255,0.12);
	align-items: center;
	justify-content: center;
}
.btn-icon{
	width: 80rpx;
	margin-right: 20rpx;
}
</style>

五.python代码部分

import cv2 as cv
# 读取照片,转换成灰度图
def face_detect_img(img):
    gray_img = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
    # 将级联算法加载到一个变量中
    #每个人的路径不一样请根据自己的路径填写-----人脸识别
    haar_face_cascade = cv.CascadeClassifier('E:/conda/envs/pycharm/Library/etc/haarcascades/haarcascade_frontalface_alt.xml')
    faces = haar_face_cascade.detectMultiScale(gray_img, scaleFactor=1.1,minNeighbors=3)
    # 在图像中画上矩形框和圆框
    for (x, y, w, h) in faces:
        print(x,y,w,h)
        cv.rectangle(img, (x, y), (x + w, y + h), (156, 114, 32), 2)

    cv.imshow("detect",img)
# 加载图片
#加载直播流
cap = cv.VideoCapture('rtmp://localhost:1935/live/movie')#如果你不想用手机也可以直接使用OBS也是可以的
# cap = cv.VideoCapture('1.mp4')
while True:
    flag,frame=cap.read()
    if not flag:
        break
    face_detect_img(frame)
    if ord('q') == cv.waitKey(10):
        break
cv.destroyAllWindows() # 释放内存
cap.release()

cv.CascadeClassifier('请填写自己的路径')
E:/conda/envs/pycharm/Library/etc/haarcascades/haarcascade_frontalface_alt.xml
仅供参考,我们主要是获取:haarcascade_frontalface_alt.xml 用户人脸识别使用
这是网上找的资料:
Opencv自带训练好的人脸检测模型,存储在sources/data/haarcascades文件夹和sources/data/lbpcascades文件夹下。其中几个.xml文件如下:
人脸检测器(默认):haarcascade_frontalface_default.xml
人脸检测器(快速Harr):haarcascade_frontalface_alt2.xml
人脸检测器(侧视):haarcascade_profileface.xml
眼部检测器(左眼):haarcascade_lefteye_2splits.xml
眼部检测器(右眼):haarcascade_righteye_2splits.xml
嘴部检测器:haarcascade_mcs_mouth.xml
鼻子检测器:haarcascade_mcs_nose.xml
身体检测器:haarcascade_fullbody.xml
人脸检测器(快速LBP):lbpcascade_frontalface.xml

这里先展示使用OBS推流的效果

在这里插入图片描述
opencv把直播流视频分割成一张张图片,然后进行识别

五.最终效果

总结

这是对于自己关于直播练习的小项目,其中走了很多弯路。写一篇关于自己对于整个过程的总结。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值