uniapp使用live-pusher实现模拟人脸识别效果

需求:

1、前端实现模拟用户人脸识别,识别成功后抓取视频流或认证的一张静态图给服务端。

2、服务端调用第三方活体认证接口,验证前端传递的人脸是否存在,把认证结果反馈给前端。

3、前端根据服务端返回的状态,显示在页面上用于提示用户。

难点:

1、前端APP如果要实现人脸活体校验验证,需要对接大厂的SDK实现。

2、一开始我采用用使用在App内嵌套H5来单独部署一套人脸验证,把结果通过webview与APP进行数据交互,但是H5试了使用好用高效的effet.js 库人脸识别 目前仅支持H5(但是发现最后在手机上识别人像整个人被压缩似得,而且是反向镜头,跟作者已反馈,等待作者持续更新)。

3、抛弃了使用App嵌套H5方法,因此最终选择了使用原生live-pusher直播流来模拟实现人脸识别效果。本打算给大家写成组件方便大家直接调用来着,但是发现组件内获取实例仅支持在onReady页面生命周期使用。

实现思路

1、首先需要获取手机是否有录音以及相机权限,没有的话引导用户前去设置页主动设置。

2、其次创建live-pusher实例,根据业务需求实现自己的模拟人脸识别思路(目前我们这是在用户手动点击5s后进行自动抓拍的)。

3、前端拿到抓拍最后一帧图片调用接口给服务端传递,服务端调用第三方进行人脸活体检测,一般是需要付费的哈。

4、最后前端把服务端返回的识别状态展示在页面上,方便后续用户操作。

代码步骤(当前是vue3项目示例,最低sdk 21版本并且勾选livepusher)

1、获取当前手机是否允许开启相机和录音权限。(建议直接使用官方大佬写的js sdk)App权限判断和提示

引入js sdk插件(vue3版本需要转换为export function,vue2版本直接按照官方大佬的直接使用即可)

import {
		requestAndroidPermission,
		gotoAppPermissionSetting
	} from '@/js_sdk/wa-permission/permission.js'

2、创建live-pusher实例,根据业务需求写业务逻辑(注意:一定要用nvue页面哈,cover-image覆盖一个矢量图在直播流画面上)。

ilve-pusher详细参数说明具体看live-pusher官方文档

<template>
	<view class="container">
		<live-pusher id='livePusher' ref="livePusher" class="livePusher" url="" mode="FHD" :muted="true"
			:enable-camera="true" :auto-focus="true" :beauty="2" whiteness="2" aspect="9:16" local-mirror="disable"
			@statechange="statechange" @error="error" @netstatus="netstatus" :style="[{
					width:'400rpx',
		            height: '400rpx',
		            marginLeft: '175rpx',
					marginTop:'20rpx',
		        }]"></live-pusher>
		<cover-image style="
		            width: 400rpx;
		            height: 400rpx;
		            transform: scale(1.01);
					position: absolute;
					left: 175rpx;
					top: 190.5rpx;
		        " src="@/static/circleBg.png" />
	</view>
</template>
<script>
export default{
    onReady() {//需要在onReady页面生命周期函数来写
			this.livePusher = uni.createLivePusherContext("livePusher", this);
		},
}
</script>

3、5s后抓拍最后一帧图片给服务端,人脸识别成功即登录系统,识别失败跳转认证失败页面(这里跟服务端对接采用的formdata格式上传文件流,你也可以采取转成base64

临时路径转base64

			// 定时器 几秒后自动抓拍
			handleSetTime() {
				this.timeFlag = setInterval(async () => {
					if (this.timeOut > 0) {
						this.timeOut--
						this.titleTips = this.countDownTimerStartTips
						this.buttonTip = `${this.countDownTimerStartBtnTips} ${this.timeOut}秒`
					}
					if (this.timeOut == 1) {
						this.livePusher.snapshot({
							success: (res) => {
								this.snapshotInfo = res.message
							}
						})
					}
					// 进行快照人脸认证
					if (this.timeOut == 0) {
						clearInterval(this.timeFlag);
						this.titleTips = this.countDownTimerZeroTips
						this.buttonTip = this.countDownTimerZeroBtnTips
						uni.showLoading({
							title: this.countDownTimerZeroBtnTips
						})
						uni.uploadFile({
							url: 'http://192.168.60.2:8080/bsCheckImage/checkImg',
							filePath: this.snapshotInfo.tempImagePath,
							name: "file",
							success: (uploadFileRes) => {
								const jxStrData = JSON.parse(uploadFileRes.data)
								console.log(jxStrData)
								const resResultCode = jxStrData.code
								const resResultData = jxStrData.data
								if (resResultCode !== '00000') {
									uni.navigateTo({
										url: '/pages/liveb-result/liveb-result?failResultObj=' +
											this.passData(jxStrData)
									})
									this.handleStop()
									return
								}
								if (resResultCode == '00000' && resResultData.score >= 0.8) {
									uni.showToast({
										title: this.faceSucessTips
									})
									this.buttonTip = this.faceSucessTips
									this.handleStop()
									return
								}
								if (resResultCode == '00000' && resResultData.score < 0.8) {
									const paramsData = {
										success: false,
										code: 'A9901',
										message: '人脸校验失败,请将人脸正对取景框内重新认证',
										failCode: 'A9901',
										faceScore: resResultData.score
									}
									uni.navigateTo({
										url: '/pages/liveb-result/liveb-result?failResultObj=' +
											this.passData(paramsData)
									})
									this.handleStop()
									return
								}
							},
							fail: (error) => {
								uni.hideLoading()
								uni.navigateTo({
									url: '/pages/liveb-result/liveb-result',
									animationType: 'zoom-out',
									animationDuration: 1000
								})
								this.handleStop()
							},
							complete: () => {
								uni.hideLoading()
								clearInterval(this.timeFlag) // 清除定时器,防止再次执行
							}
						});
					}
				}, 1000)
			},

4、人脸认证失败服务端返回状态,前端跳转认证失败页面,返回时给上个页面传递监听参数。(liveb-result.vue页面)

<template>
	<view class="container">
		<view class="result-area">
			<view class="result-icon">
				<image class="result-icon-pic" src="../../static/fece_result.png"></image>
			</view>
			<view class="result-tips">{{failInfos.message}}</view>
			<view class="result-button" @click="handleRetryFace">重新认证</view>
		</view>
	</view>
</template>

<script setup>
	import {
		ref
	} from 'vue'
	import {
		onLoad,
		onBackPress
	} from '@dcloudio/uni-app'
	const faceStatus = ref('')
	const failInfos = ref({})
	const failResultMsg = (() => {
		const data = {
			code: '3698',
			msg: '人脸认证失败'
		}
		uni.$emit('failResult', data);
	})
	onLoad((options) => {
		if (options.failResultObj) {
			const resultObj = JSON.parse(decodeURIComponent(options.failResultObj));
			failInfos.value = resultObj
		}
	})
	const handleRetryFace = (() => {
		console.log('handleRetryFace')
		failResultMsg()
		uni.navigateBack()
	})
	onBackPress((e) => {
		failResultMsg()
	})
</script>

<style lang="scss" scoped>
	.container {
		width: 750rpx;

		.result-area {
			position: absolute;
			top: 44%;
			left: 50%;
			transform: translate(-50%, -50%);

			.result-icon {
				display: flex;
				align-items: center;
				justify-content: center;

				.result-icon-pic {
					width: 140rpx;
					height: 140rpx;
				}
			}

			.result-tips {
				font-weight: 600;
				text-align: center;
				font-size: 32rpx;
				color: #515151;
				margin-top: 20rpx;
				margin-bottom: 60rpx;
			}

			.result-button {
				padding: 20rpx 100rpx;
				background-color: rgba(12, 75, 158, 1);
				border-radius: 60rpx;
				color: #eeeeee;
			}
		}
	}
</style>

人脸识别页面所有代码(liveb.nvue)

<template>
	<view class="container">
		<view class="header">
			<view class="header-title">
				<text class="header-title-tips">{{titleTips}}</text>
				<view class="header-title-carmera">
					<image class="header-title-img" src="../../static/change_camera.png" @click="handleChangeCrame">
					</image>
				</view>
			</view>
		</view>
		<live-pusher id='livePusher' ref="livePusher" class="livePusher" url="" mode="FHD" :muted="true"
			:enable-camera="true" :auto-focus="true" :beauty="2" whiteness="2" aspect="9:16" local-mirror="disable"
			@statechange="statechange" @error="error" @netstatus="netstatus" :style="[{
					width:'400rpx',
		            height: '400rpx',
		            marginLeft: '175rpx',
					marginTop:'20rpx',
		        }]"></live-pusher>
		<cover-image style="
		            width: 400rpx;
		            height: 400rpx;
		            transform: scale(1.01);
					position: absolute;
					left: 175rpx;
					top: 190.5rpx;
		        " src="@/static/circleBg.png" />
		<view class="footer">
			<view class="footer-tips">
				<text class="footer-tips-first">
					{{footerTipsFirst}}
				</text>
				<text class="footer-tips-second">
					{{footerTipsSecond}}
				</text>
			</view>
			<view class="footer-required">
				<view class="footer-required-row">
					<view class="row-area" v-for="(item,index) in tipList" :key="index">
						<image class="row-area-img" :src="item.icon">
						</image>
						<text class="row-area-tip">
							{{item.name}}
						</text>
					</view>
				</view>
			</view>
		</view>
		<!-- 手动抓拍 -->
		<view class="start-button" :style="{marginTop:footerBtnStyle.marginTop}">
			<view class="button-hover"
				:style="{width:footerBtnStyle.width,padding:footerBtnStyle.padding,borderRadius:footerBtnStyle.borderRadius,backgroundColor:footerBtnStyle.btnBackground}"
				@click="startFace">
				<text class="button-tip" :style="{
					fontSize:footerBtnStyle.fontSize,color:footerBtnStyle.textColor
				}">{{buttonTip}}</text>
			</view>
		</view>
	</view>
</template>

<script>
	import {
		requestAndroidPermission,
		gotoAppPermissionSetting
	} from '@/js_sdk/wa-permission/permission.js'
	export default {
		name: 'sevenq-faceLiver',
		props: {
			//是否默认开启抓拍
			isDeaultStartLive: {
				type: Boolean,
				default: false
			},
			//默认开启的话需要设置延迟时间(毫秒级)
			defaultStartDelayTime: {
				type: Number,
				default: 600
			},
			//是否需要监听结果页传递的事件
			needListenResultPage: {
				type: Boolean,
				default: true
			},
			//是否开启可以翻转摄像头
			isCanChangeCarame: {
				type: Boolean,
				default: true
			},
			//抓拍倒计时 (如果默认开启需要+1)
			snapCountdownTimer: {
				type: Number,
				default: 6
			},
			//如果不允许翻转摄像头 提示词
			notAllowChangeCarameMsg: {
				type: String,
				default: "刷脸认证仅支持前置摄像头"
			},
			//顶部提示词
			topTitleTips: {
				type: String,
				default: "请把人脸放在圆圈内拍摄脸部,开始人脸识别"
			},

			//提示词 1 
			footerTipsFirst: {
				type: String,
				default: "确认为您本人照片"
			},
			//提示词 2
			footerTipsSecond: {
				type: String,
				default: "保持正脸在取景框中系统将在5s后自动抓拍"
			},
			//提示展示列表
			tipList: {
				type: Object,
				default: [{
						icon: "../../static/img3.png",
						name: '正对手机'
					},
					{
						icon: "../../static/img2.png",
						name: '光线充足'
					},
					{
						icon: "../../static/img1.png",
						name: '脸无遮挡'
					},
				]
			},
			//抓拍倒计时开始时提示词
			countDownTimerStartTips: {
				type: String,
				default: "请保存人脸在实景框中,正在进行抓拍"
			},
			//抓拍倒计时为0时提示词
			countDownTimerZeroTips: {
				type: String,
				default: "正在人脸认证中,请稍等..."
			},
			//按钮默认文本
			buttonTips: {
				type: String,
				default: "开始人脸识别"
			},
			//抓拍倒计时开始时按钮显示提示词
			countDownTimerStartBtnTips: {
				type: String,
				default: "正在抓拍中"
			},
			//抓拍倒计时为0时按钮提示词
			countDownTimerZeroBtnTips: {
				type: String,
				default: "人脸认证中...."
			},
			//认证成功按钮提示词
			faceSucessTips: {
				type: String,
				default: "认证成功"
			},
			//权限提示开启提示词
			premissonTips: {
				type: String,
				default: "当前应用需要使用相机权限进行拍照,但相机权限暂未开启。是否前往应用设置打开相机权限?"
			},
			//底部按钮样式
			footerBtnStyle: {
				type: Object,
				default: {
					marginTop: '120rpx',
					width: '480rpx',
					padding: '24rpx',
					btnBackground: 'rgba(12, 75, 158, 1)',
					borderRadius: '300rpx',
					textColor: '#dfdfdf',
					fontSize: '32rpx'
				}
			}
		},
		data() {
			return {
				titleTips: this.topTitleTips,
				buttonTip: this.buttonTips,
				livePusher: '', // livePusher实例
				snapshotInfo: '', // 快照信息
				showCountDown: false, // 拍摄倒计时
				timeOut: this.snapCountdownTimer, // 签到倒计时
				timeFlag: null, // 定时器
				isPass: null, // 是否通过人脸认证
				phoneSysInfos: {}, //当前手机系统信息
			}
		},
		onReady() {
			this.livePusher = uni.createLivePusherContext("livePusher", this);
		},
		onShow() {
			//监听结果页面传递的失败事件
			if (this.needListenResultPage) {
				uni.$on('failResult', (resultData) => {
					if (resultData.code == '3698') {
						clearInterval(this.timeFlag)
						this.resertAll()
						this.livePusher.startPreview()
					}
				});
			}
		},
		async mounted() {
			const that_ = this
			if (!that_.showCountDown) {
				setTimeout(function() {
					that_.getCarmeraPremisson()
				}, this.defaultStartDelayTime)
			}
			that_.getPhoneSys()
		},
		onUnload() {
			if (this.needListenResultPage) {
				uni.$off('failResult');
			}
			clearInterval(this.timeFlag)
			uni.hideLoading();
		},
		onHide() {
			console.log('页面隐藏')
		},
		methods: {
			//校验是否获取相机权限
			async getCarmeraPremisson() {
				const currentSystem = uni.getSystemInfoSync().platform
				if (currentSystem == 'android') {
					const result = await requestAndroidPermission("android.permission.CAMERA")
					if (result == 1) {
						if (this.isDeaultStartLive) { //如果打开页面就进行抓拍
							this.showCountDown = true
						}
						this.startPreview()
					} else {

						uni.showModal({
							title: '提示',
							content: this.premissonTips,
							confirmText: '去设置',
							cancelText: '取消',
							success: function(res) {
								if (res.confirm) {
									gotoAppPermissionSetting()
								} else if (res.cancel) {
									uni.showToast({
										icon: 'error',
										title: '暂无相机权限'
									})
								}
							}
						});
					}
				}
			},
			//重置初始化值 需要在认证失败时候再次调用
			resertAll() {
				this.titleTips = this.topTitleTips
				this.buttonTip = this.buttonTips
				this.snapshotInfo = '' // 快照信息
				this.showCountDown = false // 拍摄倒计时
				this.timeOut = this.snapCountdownTimer // 签到倒计时
				this.timeFlag = null // 定时器
				this.isPass = null // 是否通过人脸认证
			},
			//手动翻转摄像头
			handleChangeCrame() {
				if (!this.isCanChangeCarame) {
					uni.showToast({
						icon: 'none',
						title: this.notAllowChangeCarameMsg
					})
					return
				}
				this.livePusher.switchCamera({
					success: (a) => {
						uni.showToast({
							icon: 'none',
							title: '摄像头翻转成功'
						})
					}
				});
			},
			//手动开始人脸识别
			startFace() {
				const that_ = this
				if (!that_.showCountDown) {
					that_.showCountDown = true
					if (that_.showCountDown) {
						const {
							platform,
							osVersion
						} = that_.phoneSysInfos
						if (platform == 'android' && osVersion < 10) { //判断兼容安卓10以下效果
							that_.startPreview()
							return
						}
						that_.handleSetTime()
					}
				}
			},
			// 开始预览直播流
			startPreview() {
				const _that = this
				this.livePusher.startPreview({
					success: (res) => {
						if (_that.showCountDown) {
							_that.handleSetTime()
						}
					}
				})
			},
			// 定时器 几秒后自动抓拍
			handleSetTime() {
				this.timeFlag = setInterval(async () => {
					if (this.timeOut > 0) {
						this.timeOut--
						this.titleTips = this.countDownTimerStartTips
						this.buttonTip = `${this.countDownTimerStartBtnTips} ${this.timeOut}秒`
					}
					if (this.timeOut == 1) {
						this.livePusher.snapshot({
							success: (res) => {
								this.snapshotInfo = res.message
							}
						})
					}
					// 进行快照人脸认证
					if (this.timeOut == 0) {
						clearInterval(this.timeFlag);
						this.titleTips = this.countDownTimerZeroTips
						this.buttonTip = this.countDownTimerZeroBtnTips
						uni.showLoading({
							title: this.countDownTimerZeroBtnTips
						})
						// this.$emit(handleStartFaceApi, {
						// 	code: '4364',
						// 	msg: '开始人脸与服务端进行人脸',
						// 	currentTempImagePath: this.snapshotInfo.tempImagePath
						// })
						uni.uploadFile({
							url: 'http://192.168.60.2:8080/bsCheckImage/checkImg',
							filePath: this.snapshotInfo.tempImagePath,
							name: "file",
							success: (uploadFileRes) => {
								const jxStrData = JSON.parse(uploadFileRes.data)
								console.log(jxStrData)
								const resResultCode = jxStrData.code
								const resResultData = jxStrData.data
								if (resResultCode !== '00000') {
									uni.navigateTo({
										url: '/pages/liveb-result/liveb-result?failResultObj=' +
											this.passData(jxStrData)
									})
									this.handleStop()
									return
								}
								if (resResultCode == '00000' && resResultData.score >= 0.8) {
									uni.showToast({
										title: this.faceSucessTips
									})
									this.buttonTip = this.faceSucessTips
									this.handleStop()
									return
								}
								if (resResultCode == '00000' && resResultData.score < 0.8) {
									const paramsData = {
										success: false,
										code: 'A9901',
										message: '人脸校验失败,请将人脸正对取景框内重新认证',
										failCode: 'A9901',
										faceScore: resResultData.score
									}
									uni.navigateTo({
										url: '/pages/liveb-result/liveb-result?failResultObj=' +
											this.passData(paramsData)
									})
									this.handleStop()
									return
								}
							},
							fail: (error) => {
								uni.hideLoading()
								uni.navigateTo({
									url: '/pages/liveb-result/liveb-result',
									animationType: 'zoom-out',
									animationDuration: 1000
								})
								this.handleStop()
							},
							complete: () => {
								uni.hideLoading()
								clearInterval(this.timeFlag) // 清除定时器,防止再次执行
							}
						});
					}
				}, 1000)
			},
			//向下个页面传递参数
			passData(obj) {
				let passDataStr = JSON.stringify(obj)
				let newPassDataStr = passDataStr.replace(/%/g, '%25');
				return encodeURIComponent(newPassDataStr);
			},
			//抛出停止推流 在调用成功与失败都得调用
			handleStop() {
				this.livePusher.stop()
			},
			//监听直播流状态变化
			statechange(val) {
				console.log(val, '监听直播流变化')
			},
			//监听直播流警告
			error(err) {
				console.log(err, '监听直播流警告')
			},
			//监听网络状态
			netstatus(status) {
				console.log(status, '监听直播流网络状态')
			},
			//获取手机型号
			getPhoneSys() {
				const system = uni.getDeviceInfo()
				this.phoneSysInfos = system
			}
		}

	}
</script>

<style lang="scss" scoped>
	.container {
		width: 750rpx;
	}
</style>

效果图如下所示

注意:代码仅可自己使用,不可进行二次转载哈,有问题在请私信我哦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/943403.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE5仿漫威争锋灵蝶冲刺技能

这两天玩了一下漫威争锋Marvel Rivals&#xff0c;发现是UE5做的&#xff0c;对里面一些角色技能挺感兴趣的&#xff0c;想简单复刻一下技能功能&#xff0c;顺便复习一下学过的知识 首先把摄像机设置调整一下 CameraBoom里搜索lag 把摄像机延迟关掉 &#xff0c;这样摄像机就…

去除 el-input 输入框的边框(element-ui@2.15.13)

dgqdgqdeMac-mini spid-admin % yarn list --pattern element-ui yarn list v1.22.22 └─ element-ui2.15.13 ✨ Done in 0.23s.dgqdgqdeMac-mini spid-admin % yarn list vue yarn list v1.22.22 warning Filtering by arguments is deprecated. Please use the pattern opt…

Suno Api V4模型无水印开发「综合实战开发自己的音乐网站」 —— 「Suno Api系列」第14篇

历史文章 Suno AI API接入 - 将AI音乐接入到自己的产品中&#xff0c;支持120并发任务 Suno Api V4模型无水印开发「灵感模式」 —— 「Suno Api系列」第1篇 Suno Api V4模型无水印开发「自定义模式」 —— 「Suno Api系列」第2篇 Suno Api V4模型无水印开发「AI生成歌词」…

企业如何搭建安全的跨网文件安全交换管理系统

在数字化转型的浪潮中&#xff0c;企业对数据的安全性和流动性提出了前所未有的高要求。特别是在网络隔离的情况下&#xff0c;如何实现跨网的安全、高效的文件交换成为了众多企业迫切需要解决的问题。 这不仅是技术上的挑战&#xff0c;还涉及到企业内部管理流程的优化和安全策…

Torch.gather

1.官方文档 2.使用要点 输入index的shape等于输出value的shape输入index的索引值仅替换该index中对应dim的index值最终输出为替换index后在原tensor中的值 最终输出的shape和index的shape相同 根据dim的值 选择将index[i,j,k]这个结果替换input[i,j,k]里面对应的i or j or…

报警推送消息升级的名厨亮灶开源了

简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算法加…

《解锁 Python 数据挖掘的奥秘》

《解锁 Python 数据挖掘的奥秘》 一、Python 数据挖掘基础&#xff08;一&#xff09;Python 基础与数据挖掘环境搭建&#xff08;二&#xff09;数据挖掘基本流程概述 二、Python 数据挖掘核心技术&#xff08;一&#xff09;数据收集与预处理技术&#xff08;二&#xff09;常…

如何通过 360 驱动大师检查自己电脑上的显卡信息

在深入探讨如何查看显卡信息之前&#xff0c;首先需要了解显卡的基本概念。显卡&#xff08;Graphics Processing Unit, GPU&#xff09;&#xff0c;是计算机中负责处理图形输出到显示器的重要硬件。根据其集成度和性能&#xff0c;显卡通常被分为两类&#xff1a; 集成显卡&…

深度学习目标检测算法之RetinaNet算法

文章目录 前言RetinaNet 算法原理1.RetinaNet 简介2.backbone 部分3.FPN特征金字塔4.分类和预测5.Focal Loss 结束语 &#x1f482; 个人主页:风间琉璃&#x1f91f; 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注…

[源码解析] 模型并行分布式训练Megatron (2) --- 整体架构

link [源码解析] 模型并行分布式训练Megatron (2) --- 整体架构 目录 [源码解析] 模型并行分布式训练Megatron (2) --- 整体架构 0x00 摘要0x01 启动 1.1 分布式启动1.2 构造基础 1.2.1 获取模型1.2.2 获取数据集1.2.3 步进函数 1.2.3.1 广播数据0x02 Pretrain0x03 初始化 3.1 …

点击标题滚动到指定模块

vue鼠标点击标题滚动到指定模块&#xff0c;如果滚动页面到指定模块的话标题同样改变颜色 <script> export default {name: ceshi,data() {return {activeSection: 0, // 默认激活第一个标题sections: [{ title: Section 1, content: Content for section 1 },{ title: S…

Kubernetes 镜像拉取策略全解析:如何根据需求选择最佳配置?

在Kubernetes集群里&#xff0c;拉取容器镜像是一个非常关键的步骤。这些镜像包含了应用程序及其所有需要的依赖项&#xff0c;Kubernetes通过拉取这些镜像来启动Pod中的容器。为了提升集群的稳定性、速度和安全性&#xff0c;Kubernetes提供了几种不同的镜像拉取策略。这篇文章…

【碳库】双碳目标下农田温室气体排放估算与模拟(从碳库模拟、机器学习方法、生命周期评价法(LCA)、经验模型和过程模型多个维度)

生态与农业是甲烷&#xff08;CH4&#xff09;、氧化亚氮&#xff08;N2O&#xff09;和二氧化碳&#xff08;CO2&#xff09;等温室气体的主要排放源&#xff0c;占全产业排放的13.5%。农田温室气体又以施肥产生的N2O和稻田生产产生的CH4为主&#xff0c;如何对农田温室气体进…

[计算机网络]OSPF协议

开放最短路径优先OSPF 1&#xff09;OSPF的工作方式 1>和谁交换消息 使用洪泛法&#xff0c;向本自治系统的所有路由器发送消息。 2>交换什么消息 发送的消息就是与本路由器相邻的所有路由器的链路状态&#xff0c;但这只是路由器所知道的部分信息。 链路状态就是说…

mysql进阶

存储引擎 MySQL体系结构&#xff1a; 存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表而不是基于库的&#xff0c;所以存储引擎也可以被称为表引擎。 默认存储引擎是InnoDB。 相关操作&#xff1a; -- 查询建表语句 show create table ac…

Unity2021.3.16f1可以正常打开,但是Unity2017.3.0f3却常常打开闪退或者Unity2017编辑器运行起来就闪退掉

遇到问题&#xff1a; 从今年开始&#xff0c;不知道咋回事&#xff0c;电脑上的Unity2017像是变了个人似得&#xff0c;突然特别爱闪退掉&#xff0c;有时候还次次闪退&#xff0c;真是让人无语&#xff0c;一直以来我都怀疑是不是电脑上安装了什么别的软件了&#xff0c;导致…

linux系统上SQLPLUS的重“大”发现

SQL plus版本&#xff1a; [oraclepg-xc2 ~]$ sqlplus -v SQL*Plus: Release 19.0.0.0.0 - Production Version 19.3.0.0.0 操作系统&#xff1a;CentOS Linux 7 (Core) 数据库&#xff1a;Oracle 19c Version 19.3.0.0.0 同样的SQL脚本在windos CMD sqlplus 执行没问题。…

YOLO11改进-注意力-引入自调制特征聚合模块SMFA

本篇文章将介绍一个新的改进机制——SMFA&#xff08;自调制特征聚合模块&#xff09;&#xff0c;并阐述如何将其应用于YOLOv11中&#xff0c;显著提升模型性能。随着深度学习在计算机视觉中的不断进展&#xff0c;目标检测任务也在快速发展。YOLO系列模型&#xff08;You Onl…

js-000000000000

1、js书写的位置 - 内部 <body> <!-- 习惯把 js 放到 /body 的后面 --> <script> console.log(这是内部 js 的书写位置) alert(内部js) </script> </body> <body><!-- 习惯把 js 放到 /body 的后面 --><script>console.log(这…

Android笔记(四十):ViewPager2嵌套RecyclerView滑动冲突进一步解决

背景 ViewPager2内嵌套横向滑动的RecyclerView&#xff0c;会有滑动冲突的情况&#xff0c;引入官方提供的NestedScrollableHost类可以解决冲突问题&#xff0c;但是有一些瑕疵&#xff0c;滑动横向RecyclerView到顶部&#xff0c;按住它不放手继续往左拖再往右拖&#xff0c;这…