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

news2024/12/27 0:50:33

需求:

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>

效果图如下所示

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL中Performance Schema库的详解(下)

昨天说了关于SQL语句相关的&#xff0c;今天来说说性能相关的&#xff0c;如果没有看过上篇请点传送门https://blog.csdn.net/2301_80479959/article/details/144693574?fromshareblogdetail&sharetypeblogdetail&sharerId144693574&sharereferPC&sharesource…

YOLO11全解析:从原理到实战,全流程体验下一代目标检测

前言 一、模型介绍 二、网络结构 1.主干网络&#xff08;Backbone&#xff09; 2.颈部网络&#xff08;Neck&#xff09; 3.头部网络&#xff08;Head&#xff09; 三、算法改进 1.增强的特征提取 2.优化的效率和速度 3.更高的准确性与更少的参数 4.环境适应性强 5.…

【Qt】了解和HelloWorld

目录 0.用户交互界面风格 Windows下GUI开发方案&#xff1f; 1.Qt简介 1.1 版本Qt5. 1.2搭建Qt开发环境 需要安装3个工具 安装过程 熟悉QtSDK重要工具 2.使用Qt Creator创建项目 2.1代码解释 2.2helloworld 1.图形化方式 2.代码方式 0.用户交互界面风格 1.TUI&…

原点安全再次入选信通院 2024 大数据“星河”案例

近日&#xff0c;中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;共同组织开展的 2024 大数据“星河&#xff08;Galaxy&#xff09;”案例征集活动结果正式公布。由工银瑞信基金管理有限公司、北京原点数安科技有限公司联…

【MySQL初阶】--- 数据类型

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; MySQL &#x1f3e0; 数据类型分类 MySQL是一套整体的对外数据存取方案,既然要存取数据,而数据有不同的类型,因此MySQL也存在不同的数据类型,有不同的用…

使用VsCode编译调试Neo4j源码

文章目录 使用VsCode编译调试Neo4j源码1 简介2 步骤1 下载源码2 依赖3 构建Neo4j4 运行5 安装VsCode扩展6 **调试** 使用VsCode编译调试Neo4j源码 1 简介 Neo4j作为领先的图数据库&#xff0c;在存储、查询上都非常值得分析学习。通过调试、日志等方法跟踪代码工作流有助于理…

从零开始构建美颜SDK:直播美颜插件的开发实践指南

很多人好奇的一个问题&#xff0c;直播APP中主播们的美颜功能是如何实现的&#xff0c;今天&#xff0c;我们将以构建一款美颜SDK为主线&#xff0c;从技术架构、功能设计到开发实践&#xff0c;为读者提供一个全面的指导。 一、美颜SDK的核心技术 要构建一款优秀的美颜SDK&a…

计算机网络习题( 第3章 物理层 第4章 数据链路层 )

第3章 物理层 一、单选题 1、下列选项中&#xff0c;不属于物理层接口规范定义范畴的是&#xff08; &#xff09;。 A、 接口形状 B、 引脚功能 C、 传输媒体 D、 信号电平 正确答案&#xff1a; C 2、在物理层接口特性中&#xff0c;用于描述完成每种功能的事件发…

云手机群控能用来做什么?

随着云手机的发展&#xff0c;云手机群控技术逐渐从小众的游戏多开工具&#xff0c;发展为涵盖多个领域的智能操作平台。不论是手游搬砖、短视频运营&#xff0c;还是账号养成等场景&#xff0c;云手机群控都展现出了强大的应用潜力。本文将为大家详细解析云手机群控的应用场景…

深度学习实验十七 优化算法比较

目录 一、优化算法的实验设定 1.1 2D可视化实验&#xff08;被优化函数为&#xff09; 1.2 简单拟合实验 二、学习率调整 2.1 AdaGrad算法 2.2 RMSprop算法 三、梯度修正估计 3.1 动量法 3.2 Adam算法 四、被优化函数变为的2D可视化 五、不同优化器的3D可视化对比 …

汽车IVI中控开发入门及进阶(43):NanoVG

NanoVG:基于OpenGL的轻量级抗锯齿2D矢量绘图库 NanoVG是一个跨平台、基于OpenGL的矢量图形渲染库。它非常轻量级,用C语言实现,代码不到5000行,非常精简地实现了一套HTML5 Canvas API,做为一个实用而有趣的工具集,用来构建可伸缩的用户界面和可视化效果。NanoVG-Library为…

【生信圆桌x教程系列】如何安装 seurat V4版本R包

生物信息分析,上云更省心; 欢迎访问 www.tebteb.cc 了解 【生信云】 一.介绍 Seurat 是一个广泛使用的 R 包&#xff0c;专门用于单细胞基因表达数据的分析与可视化。它主要被生物信息学和生物统计学领域的研究者用来处理、分析和理解单细胞 RNA 测序&#xff08;scRNA-seq&am…

阿里云技术公开课直播预告:基于阿里云 Elasticsearch 构建 AI 搜索和可观测 Chatbot

在当今数据驱动的商业环境中&#xff0c;企业面临着前所未有的挑战与机遇。如何高效搜索、分析和观测数据&#xff0c;已成为企业成功的关键。Elasticsearch 企业版作为 Elastic Stack 的商业发行版&#xff0c;提供了一整套高效的搜索、分析和观测解决方案。 为此&#xff0c…

android 登录界面编写

1、登录页面实现内容 1.实现使用两个EditText输入框输入用户名和密码。 2.使用CheckBox控件记住密码功能。 3.登录时候&#xff0c;验证用户名和密码是否为空。 4.当前CheckBox控件记住密码勾上时&#xff0c;使用SharedPreferences存储用户名和密码。 5.登录时候使用Prog…

多目标应用(一):多目标麋鹿优化算法(MOEHO)求解10个工程应用,提供完整MATLAB代码

一、麋鹿优化算法 麋鹿优化算法&#xff08;Elephant Herding Optimization&#xff0c;EHO&#xff09;是2024年提出的一种启发式优化算法&#xff0c;该算法的灵感来源于麋鹿群的繁殖过程&#xff0c;包括发情期和产犊期。在发情期&#xff0c;麋鹿群根据公麋鹿之间的争斗分…

设计模式——装饰模式

文章目录 1.定义2. 结构组成3. 组合模式结构4. 示例代码5. 模式优势6. 应用场景 1.定义 装饰模式就像是给你的对象穿上不同的 “时尚服装”&#xff0c;在程序运行时&#xff0c;你可以随意地给对象搭配各种 “服装” 来增加新的功能&#xff0c;而且完全不用对对象本身的 “身…

python+reportlab创建PDF文件

目录 字体导入 画布写入 创建画布对象 写入文本内容 写入图片内容 新增页 画线 表格 保存 模板写入 创建模板对象 段落及样式 表格及样式 画框 图片 页眉页脚 添加图形 构建pdf文件 reportlab库支持创建包含文本、图像、图形和表格的复杂PDF文档。 安装&…

<数据集>芝麻作物和杂草识别数据集<目标检测>

数据集下载链接 &#xff1c;数据集&#xff1e;芝麻作物和杂草识别数据集&#xff1c;目标检测&#xff1e;https://download.csdn.net/download/qq_53332949/90181548数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1300张 标注数量(xml文件个数)&#xff1a;130…

Python爬虫:速卖通aliexpress商品详情获取指南

在数字化时代&#xff0c;数据已成为企业竞争的关键资源。对于电商行业而言&#xff0c;获取竞争对手的商品信息是洞察市场动态、优化自身产品策略的重要手段。速卖通&#xff08;AliExpress&#xff09;作为全球知名的跨境电商平台&#xff0c;其商品信息的获取自然成为了许多…

【绿色碳中和】全国各省各地级市绿色金融数据(1990-2022年)

数据介绍&#xff1a;绿色金融指数采用熵值法进行测算&#xff0c;综合评价体系如下&#xff1a; 绿色金融指标体系 二级指标 三级指标 指标说明 绿色信贷 高能耗行业利息支出占比 六大高能耗行业利息支出/工业总利息 A股上市公司环保企业新增银行贷款占比 …