提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
菜谱编程,照着菜谱做,你也能实现我相同的效果。
条条大路通罗马,我的方法并不一定是最好的,只是个人一些技术总结,仅供参考
一、准备食材
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"></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把直播流视频分割成一张张图片,然后进行识别
五.最终效果
总结
这是对于自己关于直播练习的小项目,其中走了很多弯路。写一篇关于自己对于整个过程的总结。