人脸识别+采集分开处理

This commit is contained in:
2026-02-05 13:32:45 +08:00
parent 67f5d07f25
commit 2ee4d2d320
4 changed files with 1146 additions and 64 deletions
@@ -39,6 +39,9 @@ import com.sw.platecabinet.MyApp
import com.sw.platecabinet.R
import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.ext.clickWithDebounce
import com.sw.platecabinet.ext.gone
import com.sw.platecabinet.ext.visible
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.model.response.UserFaceModel
import com.sw.platecabinet.utils.BitmapSaver
@@ -148,7 +151,21 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
soundMap[COLLECT_SUCCESS] = R.raw.collect_success
soundMap[RECOGNIZE_SUCCESS] = R.raw.recognize_success
soundMap[NO_PLATE_REMIND] = R.raw.no_plate_remind
SoundPoolUtil.getInstance().loadR(this, soundMap);
SoundPoolUtil.getInstance().loadR(this, soundMap)
binding.btnCollect.clickWithDebounce {
//先重置,后采集
resetRecognizeState()
//采集数据按钮
retryFailCount = 0
isCollectFace = true
collectFace()
}
binding.btnRetry.clickWithDebounce {
//重新识别按钮
resetRecognizeState()
}
}
@@ -187,6 +204,17 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
private val regDebouncer = Debouncer(3000)
private var registerHandler: RegisterCallbackHandler? = null
private fun isFitData(userFaceInfo: UserFaceInfo?): Boolean {
if (userFaceInfo == null) return false
val headBmp = userFaceInfo.headBmp
if (headBmp == null) return false
if (headBmp.width <= 200 || headBmp.height <= 200) {
loadLogInfo("collectFace,isFitData:拿到人脸提取特征数据照片过小-----------")
return false
}
return true
}
private fun initArcViewModel() {
recognizeViewModel.setLiveType(livenessType)
recognizeViewModel.prepareRegister()
@@ -195,14 +223,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
if (!isCollectFace) {
return@setCallback
}
if (this.userFaceInfo == null && userFaceInfo != null) {
if (this.userFaceInfo == null && isFitData(userFaceInfo)) {
recognizeViewModel.updateRegisterStatus(RecognizeViewModel.REGISTER_STATUS_DONE)
this.userFaceInfo = userFaceInfo
getFaceDataBlock { faceData, headBmp ->
isCollecting.set(false)
isCollectFace = false
sendFaceData(faceData, headBmp)
}
getFaceDataBlock()
}
}
}
@@ -247,11 +271,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? ->
loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
if (faceUseSwitch) {
loadFaceRecognizeResult2(result)
return@Observer
}
loadFaceRecognizeResult(result)
loadFaceRecognizeResult3(result)
})
recognizeViewModel.drawRectInfoText.observe(this, Observer { info ->
@@ -288,6 +308,81 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
private val openPlateDebouncer = Debouncer(5000)
private val isCollecting = AtomicBoolean(false)
private fun loadFaceRecognizeResult3(result: CompareResult?) {
val similar = result?.similar ?: return
binding.tvSimilarValue.text = "$similar${retryFailCount}"
loadLogInfo(
"collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, " +
"fail=$faceFailThreshold, similar=$similar, retry=$retryFailCount"
)
if (similar >= faceSuccessThreshold) {
val userId = result.faceEntity?.userName
if (userId.isNullOrBlank()) {
loadLogInfo("collectFace,recognizeUserId 识别成功但 userId 为空,忽略")
return
}
loadLogInfo(
"collectFace,recognizeUserId 识别成功 userId=$userId, time=${DateTimeUtils.getDateTimeString()}"
)
// 成功后必须重置状态
retryFailCount = 0
isCollecting.set(false)
isCollectFace = false
currentUserId = userId
// 防抖,防止重复开闸
openPlateDebouncer.debounce {
openPlate(userId)
}
return
}
if (isCollectFace) {
loadLogInfo("collectFace,recognizeUserId 正在采集中,忽略失败结果 similar=$similar")
return
}
// if (similar <= faceFailThreshold || retryFailCount >= maxFailCount) {
if (retryFailCount >= maxFailCount) {
loadLogInfo(
"collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
"retry=$retryFailCount, time=${DateTimeUtils.getDateTimeString()}"
)
if (!isCollecting.compareAndSet(false, true)) {
loadLogInfo("collectFace 已在采集中,忽略重复触发")
return
}
recognizeFail()
return
}
retryFailCount++
loadLogInfo("collectFace,recognizeUserId 中间态重试,retryFailCount=$retryFailCount")
}
private fun recognizeFail() {
countDownTimer?.cancel()
binding.tvToInit.text = ""
binding.tvSimilarValue.text = ""
binding.llCollectButton.visible()
binding.btnCollect.run {
visible()
if (tag == "1") {
binding.btnRetry.gone()
text = "重新采集"
tag = "1"
} else {
binding.btnRetry.visible()
"新用户采集"
tag = "1"
}
}
}
/**
* 根据相似度similar>设定阈值0.8作为识别成功判断,
* 如果similar<0.3作为识别失败判断,
@@ -352,38 +447,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
loadLogInfo("collectFace,recognizeUserId 中间态重试,retryFailCount=$retryFailCount")
}
// private fun loadFaceRecognizeResult2(result: CompareResult?) {
//
// if(isCollectFace){
// return
// }
//
// val similar = result?.similar ?: 0f
// binding.tvSimilarValue.text = "$similar${retryFailCount}次"
// loadLogInfo("collectFace,recognizeUserId 人脸识别配置阈值为:${faceSuccessThreshold},失败阈值为:${faceFailThreshold},相似度为:${similar},失败次数为:${retryFailCount}")
// if (similar > faceSuccessThreshold) {
// loadLogInfo("collectFace,recognizeUserId 相似度为:${similar},识别成功时间:${DateTimeUtils.getDateTimeString()}")
// //大 于识别阈值,作为识别成功判断
// currentUserId = result?.faceEntity?.userName
// Debouncer(5000).debounce { openPlate(currentUserId) }
// return
// }
// loadLogInfo("collectFace,recognizeUserId 人脸识别相似度为:${similar},失败次数为:${retryFailCount}")
// if (similar < faceFailThreshold || retryFailCount >= maxFailCount) {
// //相似度低于识别错误阈值进行采集、识别失败次数大于等于最大失败次数,作为识别失败判断
// if (isCollectFace.not()) {
// //非采集中
// loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,相似度为:${similar},开始采集时间:${DateTimeUtils.getDateTimeString()}")
// isCollectFace = true
// collectFace()
// return
// }
// }
// //满足similar >= faceFailThreshold && similar <= successThreshold时进行重试
// retryFailCount++
// loadLogInfo("collectFace,recognizeUserId 人脸识别失败次数为:${retryFailCount}")
// }
/**
* 根据相似度similar>设定阈值0.8作为识别是否成功来加载人脸识别结果
*/
@@ -449,6 +512,9 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
Debouncer(5000).debounce {
//打开柜子
PlateUtils.open(this, item.equipmentBoxCode, false)
countDownTimer?.cancel()
goInitActivity()
}
currentPlateInfo = item
MyApp.plateList = items
@@ -600,6 +666,9 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
currentPlateInfo = null
currentUserId = null
userFaceInfo = null
binding.btnCollect.tag = ""
binding.tvSimilarValue.text = ""
retryFailCount = 0
}
recognizeViewModel.clearLeftFace(facePreviewInfoList)
}
@@ -680,13 +749,13 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}
}
/**
* 临时用户id
*/
private var tempUserId: String = ""
override fun onResume() {
super.onResume()
registerHandler = RegisterCallbackHandler()
try {
if (isFirstOpen) {
goInitActivity()
@@ -695,26 +764,32 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}, 1000)
return
}
retryFailCount = 0
binding.tvSimilarValue.text = ""
isCollecting.set(false)
isCollectFace = false
plateEnable = true
collectCount = 0
tempUserId = "${System.currentTimeMillis()}"
resumeCamera()
userViewModel.resetUserInfo()
countDownTimer?.let {
it.cancel()
it.start()
}
// initFaceScheduler()
// checkFaceState()
resetRecognizeState()
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 重置识别状态
*/
private fun resetRecognizeState() {
binding.llCollectButton.gone()
retryFailCount = 0
binding.tvSimilarValue.text = ""
isCollecting.set(false)
isCollectFace = false
plateEnable = true
collectCount = 0
tempUserId = "${System.currentTimeMillis()}"
resumeCamera()
registerHandler = RegisterCallbackHandler()
countDownTimer?.let {
it.cancel()
it.start()
}
}
private var isFirstOpen = true
protected override fun onPause() {
@@ -742,7 +817,11 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}
override fun onFinish() {
goInitActivity()
if (facePreviewInfoList.isNullOrEmpty()) {
goInitActivity()
} else {
recognizeFail()
}
}
}
}
@@ -813,16 +892,22 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}
}
private fun getFaceDataBlock(block: (String, Bitmap) -> Unit) {
if (userFaceInfo == null || userFaceInfo?.faceFeature?.featureData == null) {
private fun getFaceDataBlock() {
if (userFaceInfo?.faceFeature?.featureData == null) {
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据为null-----------")
return
}
val faceData: String? = Base64.encode(userFaceInfo?.faceFeature?.featureData)
if (faceData.isNullOrBlank()) {
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据Base64编码为空")
return
}
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据时间:${DateTimeUtils.getDateTimeString()}-----------")
block(faceData, userFaceInfo!!.headBmp)
val headBmp = userFaceInfo!!.headBmp
//block(faceData, headBmp)
isCollecting.set(false)
isCollectFace = false
sendFaceData(faceData, headBmp)
}
/**
@@ -0,0 +1,946 @@
//package com.sw.platecabinet.activity
//
//import android.Manifest
//import android.content.Intent
//import android.graphics.Bitmap
//import android.graphics.Point
//import android.hardware.Camera
//import android.os.Build
//import android.os.CountDownTimer
//import android.util.DisplayMetrics
//import android.view.View
//import android.view.ViewGroup
//import android.view.ViewTreeObserver
//import androidx.annotation.RequiresApi
//import androidx.lifecycle.Observer
//import com.arcsoft.face.ErrorInfo
//import com.google.gson.Gson
//import com.sw.inbound.utils.DateTimeUtils
//import com.sw.plate.utils.Base64
//import com.sw.plate.utils.ToastUtils
//import com.sw.plate.utils.arcface.ConfigUtil
//import com.sw.plate.utils.arcface.ErrorCodeUtil
//import com.sw.plate.utils.arcface.FaceRectTransformer
//import com.sw.plate.utils.arcface.FaceRectView
//import com.sw.plate.utils.arcface.FaceRectView.DrawInfo
//import com.sw.plate.utils.arcface.PreviewConfig
//import com.sw.plate.utils.arcface.camera.CameraListener
//import com.sw.plate.utils.arcface.camera.DualCameraHelper
//import com.sw.plate.utils.arcface.face.constants.LivenessType
//import com.sw.plate.utils.arcface.face.model.CompareResult
//import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
//import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
//import com.sw.plate.utils.arcface.facedb.FaceDatabase
//import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
//import com.sw.plate.utils.arcface.model.UserFaceInfo
//import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
//import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel.REGISTER_STATUS_READY
//import com.sw.platecabinet.MyApp
//import com.sw.platecabinet.R
//import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
//import com.sw.platecabinet.databinding.ItemTitleTimeBinding
//import com.sw.platecabinet.model.response.EquipmentUserInfo
//import com.sw.platecabinet.model.response.UserFaceModel
//import com.sw.platecabinet.utils.BitmapSaver
//import com.sw.platecabinet.utils.Debouncer
//import com.sw.platecabinet.utils.LogFileUtil
//import com.sw.platecabinet.utils.PermissionHelper
//import com.sw.platecabinet.utils.PlateUtils
//import com.sw.platecabinet.utils.RegisterCallbackHandler
//import com.sw.platecabinet.utils.SoundPoolUtil
//import com.sw.platecabinet.utils.SpTool
//import org.greenrobot.eventbus.EventBus
//import org.greenrobot.eventbus.Subscribe
//import org.greenrobot.eventbus.ThreadMode
//import timber.log.Timber
//import java.io.File
//import java.util.concurrent.atomic.AtomicBoolean
//
///**
// * 人脸识别
// */
//class LoginByFaceActivityBak : BaseActivity<ActivityLoginFaceBinding>(),
// ViewTreeObserver.OnGlobalLayoutListener {
// // private val recognizeViewModel by viewModels<RecognizeViewModel>()
// private var countDownTimer: CountDownTimer? = null
//
// private val CAMERA_PERMISSION_REQUEST_CODE = 100
// private val REQUIRED_PERMISSIONS: Array<String> = arrayOf(
// Manifest.permission.CAMERA,
// Manifest.permission.WRITE_EXTERNAL_STORAGE,
// Manifest.permission.READ_EXTERNAL_STORAGE
// )
//
// override fun inflateViewBinding(): ActivityLoginFaceBinding {
// return ActivityLoginFaceBinding.inflate(layoutInflater)
// }
//
// override fun inflateTitleBinding(): ItemTitleTimeBinding {
// return binding.includeHeader
// }
//
// companion object {
// private const val TAG = "LoginByFaceActivity"
// private var instance: LoginByFaceActivityBak? = null
//
// private const val COLLECT_SUCCESS = "collect_success"
// private const val RECOGNIZE_SUCCESS = "recognize_success"
// private const val NO_PLATE_REMIND = "no_plate_remind";
//
// fun goInitActivity() {
// instance?.goInitActivity()
// }
// }
//
//// private var logList: MutableList<String> = mutableListOf()
//// private val logAdapter by lazy { LogAdapter(logList) }
//
// override fun initialize() {
// instance = this
// userViewModel.activeEngine()
//// addSocketListener()
// initCountTime()
// initArcViewModel()
// initArcView()
// openRectInfoDraw = true
// recognizeViewModel.setDrawRectInfoTextValue(true)
// //viewModel.generateToken()
//
//// binding.llToPwd.setOnClickListener {
//// val intent = Intent(this, LoginByPwdActivity::class.java)
//// startActivity(intent)
//// }
//
//// TaskManager.startTask()
//
// EventBus.getDefault().register(this)
//
//// binding.btnCollectFace.setOnClickListener { collectFace() }
//
//
//// binding.rvLogInfo.let {
//// it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)
//// it.adapter = logAdapter
//// }
////
//// binding.btnLogState.let {
//// it.tag = false
//// it.setImageResource(R.drawable.ic_log_show)
//// it.setOnClickListener { v ->
//// binding.rvLogInfo.run {
//// val state = it.tag.toString().toBooleanStrictOrNull() ?: false
//// if (state) visible() else gone()
//// }
//// val state = it.tag.toString().toBooleanStrictOrNull() ?: false
//// it.tag = state
//// it.setImageResource(
//// if (state) R.drawable.ic_log_hide else R.drawable.ic_log_show
//// )
//// }
//// }
//
// //开启人脸增量数据定时任务
// startFaceTask {
// loadLogInfo("collectFace增量接口数据用户id:${Gson().toJson(it)}")
// }
//
// val soundMap = hashMapOf<String, Int>()
// soundMap[COLLECT_SUCCESS] = R.raw.collect_success
// soundMap[RECOGNIZE_SUCCESS] = R.raw.recognize_success
// soundMap[NO_PLATE_REMIND] = R.raw.no_plate_remind
// SoundPoolUtil.getInstance().loadR(this, soundMap);
// }
//
//
// private fun checkCameraPermission() {
// Timber.i("checkCameraPermission")
// val permissionHelper =
// PermissionHelper.with(this, REQUIRED_PERMISSIONS, CAMERA_PERMISSION_REQUEST_CODE)
// .onGranted {
// loadLogInfo("checkCameraPermission 权限已允许")
// // 权限已授予
// recognizeViewModel.init()
// initRgbCamera()
// resumeCamera()
// }
// .build()
// registerPermissionHelper(permissionHelper)
// permissionHelper.checkAndRequest()
// }
//
// @Subscribe(threadMode = ThreadMode.MAIN)
// fun getFaceEntity(insertEntity: FaceEntity) {
// Timber.tag("performSync").e("-time=%s", insertEntity.registerTime)
// recognizeViewModel.addFace(insertEntity)
// Timber.tag("performSync over").e("-time=%s", insertEntity.registerTime)
// }
//
// override fun onLeftDoubleClick() {
// finish()
// }
//
// private var isRecognition = false
// private var rgbCameraHelper: DualCameraHelper? = null
// private var rgbFaceRectTransformer: FaceRectTransformer? = null
// private val livenessType = LivenessType.RGB
// private var openRectInfoDraw = false
// private val regDebouncer = Debouncer(3000)
// private var registerHandler: RegisterCallbackHandler? = null
//
// private fun initArcViewModel() {
// recognizeViewModel.setLiveType(livenessType)
// recognizeViewModel.prepareRegister()
// recognizeViewModel.setOnRegisterFinishedCallback { facePreviewInfo, userFaceInfo ->
// registerHandler?.setCallback {
// if (!isCollectFace) {
// return@setCallback
// }
// if (this.userFaceInfo == null && userFaceInfo != null) {
// recognizeViewModel.updateRegisterStatus(RecognizeViewModel.REGISTER_STATUS_DONE)
// this.userFaceInfo = userFaceInfo
// getFaceDataBlock { faceData, headBmp ->
// isCollecting.set(false)
// isCollectFace = false
// sendFaceData(faceData, headBmp)
// }
// }
// }
// }
// recognizeViewModel.ftInitCode.observe(this, Observer { ftInitCode: Int? ->
// if (ftInitCode != ErrorInfo.MOK) {
// val error: String? = getString(
// R.string.specific_engine_init_failed, "ftEngine",
// ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode!!)
// )
// Timber.e("ftInitCode observe = $error")
// ToastUtils.showToast(error)
// }
// })
// recognizeViewModel.frInitCode.observe(this, Observer { frInitCode: Int? ->
// if (frInitCode != ErrorInfo.MOK) {
// val error: String? = getString(
// R.string.specific_engine_init_failed, "frEngine",
// frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode!!)
// )
// Timber.e("frInitCode observe = $error")
// ToastUtils.showToast(error)
// }
// })
// recognizeViewModel.flInitCode.observe(this, Observer { flInitCode: Int? ->
// if (flInitCode != ErrorInfo.MOK) {
// val error: String? = getString(
// R.string.specific_engine_init_failed, "flEngine",
// flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode!!)
// )
// Timber.e("flInitCode observe = $error")
// ToastUtils.showToast(error)
// }
// })
//
// recognizeViewModel.recognizeConfiguration
// .observe(this, Observer { recognizeConfiguration: RecognizeConfiguration? ->
// Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
// })
// recognizeViewModel.recognizeNotice.observe(this, Observer { notice: String? ->
// Timber.i("recognizeNotice observe notice = $notice")
// })
//
// recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? ->
// loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
// if (faceUseSwitch) {
// loadFaceRecognizeResult2(result)
// return@Observer
// }
// loadFaceRecognizeResult(result)
// })
//
// recognizeViewModel.drawRectInfoText.observe(this, Observer { info ->
// Timber.i("drawRectInfoText observe info = $info")
// })
// }
//
// /**
// * 人脸识别开关,true-loadFaceRecognizeResult2false-loadFaceRecognizeResult
// */
// private var faceUseSwitch = true
//
// /**
// * 识别失败重试次数
// */
// private var retryFailCount = 0
//
// /**
// * 识别失败最大次数
// */
// private val maxFailCount = 5
//
// /**
// * 人脸失败阈值
// */
// private val faceFailThreshold = 0.3
//
// /**
// * 配置阈值,默认值为0.8
// */
// private val faceSuccessThreshold by lazy { ConfigUtil.getRecognizeThreshold(this) }
//
//
// private val openPlateDebouncer = Debouncer(5000)
// private val isCollecting = AtomicBoolean(false)
//
// /**
// * 根据相似度similar>设定阈值0.8作为识别成功判断,
// * 如果similar<0.3作为识别失败判断,
// * 如果similar>=0.3&&similar<=0.8时,暂时不作为失败情况,再识别5次如果similar<0.8则作为识别失败处理
// */
// private fun loadFaceRecognizeResult2(result: CompareResult?) {
// // 无有效结果直接忽略
// val similar = result?.similar ?: return
//
// binding.tvSimilarValue.text = "$similar${retryFailCount}次"
// loadLogInfo(
// "collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, " +
// "fail=$faceFailThreshold, similar=$similar, retry=$retryFailCount"
// )
//
// if (similar >= faceSuccessThreshold) {
// val userId = result.faceEntity?.userName
// if (userId.isNullOrBlank()) {
// loadLogInfo("collectFace,recognizeUserId 识别成功但 userId 为空,忽略")
// return
// }
//
// loadLogInfo(
// "collectFace,recognizeUserId 识别成功 userId=$userId, time=${DateTimeUtils.getDateTimeString()}"
// )
//
// // 成功后必须重置状态
// retryFailCount = 0
// isCollecting.set(false)
// isCollectFace = false
// currentUserId = userId
//
// // 防抖,防止重复开闸
// openPlateDebouncer.debounce {
// openPlate(userId)
// }
// return
// }
//
// if (isCollectFace) {
// loadLogInfo("collectFace,recognizeUserId 正在采集中,忽略失败结果 similar=$similar")
// return
// }
//
// if (similar <= faceFailThreshold || retryFailCount >= maxFailCount) {
// loadLogInfo(
// "collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
// "retry=$retryFailCount, time=${DateTimeUtils.getDateTimeString()}"
// )
// if (!isCollecting.compareAndSet(false, true)) {
// loadLogInfo("collectFace 已在采集中,忽略重复触发")
// return
// }
//
// retryFailCount = 0
// isCollectFace = true
// collectFace()//采集人脸
// return
// }
//
// retryFailCount++
// loadLogInfo("collectFace,recognizeUserId 中间态重试,retryFailCount=$retryFailCount")
// }
//
//// private fun loadFaceRecognizeResult2(result: CompareResult?) {
////
//// if(isCollectFace){
//// return
//// }
////
//// val similar = result?.similar ?: 0f
//// binding.tvSimilarValue.text = "$similar${retryFailCount}次"
//// loadLogInfo("collectFace,recognizeUserId 人脸识别配置阈值为:${faceSuccessThreshold},失败阈值为:${faceFailThreshold},相似度为:${similar},失败次数为:${retryFailCount}")
//// if (similar > faceSuccessThreshold) {
//// loadLogInfo("collectFace,recognizeUserId 相似度为:${similar},识别成功时间:${DateTimeUtils.getDateTimeString()}")
//// //大 于识别阈值,作为识别成功判断
//// currentUserId = result?.faceEntity?.userName
//// Debouncer(5000).debounce { openPlate(currentUserId) }
//// return
//// }
//// loadLogInfo("collectFace,recognizeUserId 人脸识别相似度为:${similar},失败次数为:${retryFailCount}")
//// if (similar < faceFailThreshold || retryFailCount >= maxFailCount) {
//// //相似度低于识别错误阈值进行采集、识别失败次数大于等于最大失败次数,作为识别失败判断
//// if (isCollectFace.not()) {
//// //非采集中
//// loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,相似度为:${similar},开始采集时间:${DateTimeUtils.getDateTimeString()}")
//// isCollectFace = true
//// collectFace()
//// return
//// }
//// }
//// //满足similar >= faceFailThreshold && similar <= successThreshold时进行重试
//// retryFailCount++
//// loadLogInfo("collectFace,recognizeUserId 人脸识别失败次数为:${retryFailCount}")
//// }
//
// /**
// * 根据相似度similar>设定阈值0.8作为识别是否成功来加载人脸识别结果
// */
// private fun loadFaceRecognizeResult(result: CompareResult?) {
// val similar = result?.similar ?: 0f
// binding.tvSimilarValue.text = "$similar"
// val similarPass = result?.isSimilarPass ?: false
// if (similarPass.not()) {
// loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,开始采集")
// isCollectFace = true
// collectFace()
// return
// }
// currentUserId = result?.faceEntity?.userName
// if (tempUserId != currentUserId) {
// SoundPoolUtil.getInstance().play("success", 0)
// }
//// plateEnable = true
// Debouncer(5000).debounce { openPlate(currentUserId) }
// }
//
// /**
// * 查询餐盘数据
// */
// private fun getPlateData(block: (List<EquipmentUserInfo>) -> Unit) {
// MyApp.plateList?.let {
// block(it)
// }
// }
//
// private var currentPlateInfo: EquipmentUserInfo? = null
// private var plateEnable = false
//
// /**
// * 打开餐盘柜
// */
// private fun openPlate(userId: String?) {
// if (plateEnable.not()) return
// loadLogInfo("collectFace,openPlate: userId=$userId")
// getPlateData { items ->
// loadLogInfo("collectFace,openPlate: items:${Gson().toJson(items)}")
// // 未查询到当前用户,查询第一个未绑定的餐盘
// val item = items.firstOrNull { it.plateNumber.isNullOrEmpty().not() }
// loadLogInfo("collectFace,openPlate: item2:${item}")
// if (item == null) {
// //暂无餐盘
// loadLogInfo("collectFace,openPlate: 暂无餐盘,请联系管理人员,isCollectFace=$isCollectFace")
// //ToastUtils.showToast("暂无餐盘,请联系管理人员")
// SoundPoolUtil.getInstance().play(NO_PLATE_REMIND, 0)
// goInitActivity()
// return@getPlateData
// }
//// if (tempUserId != currentUserId) {
// SoundPoolUtil.getInstance().play(RECOGNIZE_SUCCESS, 0)
//// } else {
//// SoundPoolUtil.getInstance().play(COLLECT_SUCCESS, 0)
//// }
// loadLogInfo("collectFace,openPlate: item3:${item}")
// item.faceId = userId
// item.updateTime = DateTimeUtils.getDateTimeString()
// item.plateNumber = null
// plateEnable = false
// Debouncer(5000).debounce {
// //打开柜子
// PlateUtils.open(this, item.equipmentBoxCode, false)
// }
// currentPlateInfo = item
// MyApp.plateList = items
// SpTool.savePlateData(items)
// }
// }
//
//// private val debouncer by lazy { Debouncer(3000) }
//
// private fun initArcView() {
// //在布局结束后才做初始化操作
// binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
// recognizeViewModel.getCompareResultList().getValue()
// }
//
// override fun onDestroy() {
// loadLogInfo("onDestroy")
// instance = null
// if (rgbCameraHelper != null) {
// rgbCameraHelper!!.release()
// rgbCameraHelper = null
// }
//
// recognizeViewModel.destroy()
//
// EventBus.getDefault().unregister(this)
// super.onDestroy()
// }
//
//
// /**
// * 调整View的宽高,使预览显示正常且采集框固定为380x380
// *
// * @param rgbPreview RGB预览View
// * @param previewView 显示预览数据的view
// * @param faceRectView 画框的view (380x380)
// * @param previewSize 预览大小
// * @param displayOrientation 相机旋转角度
// * @param scale 缩放比例
// * @return 调整后的LayoutParams
// */
// private fun adjustPreviewViewSize(
// rgbPreview: View,
// previewView: View,
// faceRectView: FaceRectView,
// previewSize: Camera.Size,
// displayOrientation: Int,
// scale: Float
// ): ViewGroup.LayoutParams {
// val layoutParams = previewView.layoutParams
// val measuredWidth = previewView.measuredWidth
// val measuredHeight = previewView.measuredHeight
// var ratio = (previewSize.height.toFloat()) / previewSize.width.toFloat()
// if (ratio > 1) {
// ratio = 1 / ratio
// }
// if (displayOrientation % 180 == 0) {
// layoutParams.width = measuredWidth
// layoutParams.height = (measuredWidth * ratio).toInt()
// } else {
// layoutParams.height = measuredHeight
// layoutParams.width = (measuredHeight * ratio).toInt()
// }
// if (scale < 1f) {
// val rgbParam = rgbPreview.getLayoutParams()
// layoutParams.width = (rgbParam.width * scale).toInt()
// layoutParams.height = (rgbParam.height * scale).toInt()
// } else {
// layoutParams.width = (layoutParams.width * scale).toInt()
// layoutParams.height = (layoutParams.height * scale).toInt()
// }
//
// val metrics = DisplayMetrics()
// windowManager.defaultDisplay.getMetrics(metrics)
//
// if (layoutParams.width >= metrics.widthPixels) {
// val viewRatio = layoutParams.width / (metrics.widthPixels.toFloat())
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
// }
// if (layoutParams.height >= metrics.heightPixels) {
// val viewRatio = layoutParams.height / (metrics.heightPixels.toFloat())
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
// }
//
// previewView.setLayoutParams(layoutParams)
// faceRectView.setLayoutParams(layoutParams)
// return layoutParams
// }
//
// private fun initRgbCamera() {
// val cameraListener: CameraListener = object : CameraListener {
// override fun onCameraOpened(
// camera: Camera,
// cameraId: Int,
// displayOrientation: Int,
// isMirror: Boolean
// ) {
// loadLogInfo("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
// runOnUiThread({
// val previewSizeRgb = camera.getParameters().getPreviewSize()
// val layoutParams = adjustPreviewViewSize(
// binding.dualCameraTexturePreviewRgb,
// binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
// previewSizeRgb, displayOrientation, 0.6f
// )
// loadLogInfo("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
// loadLogInfo("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
// loadLogInfo(
// "initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
// ConfigUtil.isDrawRgbRectHorizontalMirror(
// context
// )
// }, isDrawRgbRectVerticalMirror = ${
// ConfigUtil.isDrawRgbRectVerticalMirror(
// context
// )
// }"
// )
// rgbFaceRectTransformer = FaceRectTransformer(
// previewSizeRgb.width,
// previewSizeRgb.height,
// layoutParams.width,
// layoutParams.height,
// 90,
// cameraId,
// isMirror,
// true,
// true
// )
//
// recognizeViewModel.onRgbCameraOpened(camera)
// recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
// })
// }
//
// @RequiresApi(api = Build.VERSION_CODES.Q)
// override fun onPreview(nv21: ByteArray?, camera: Camera?) {
// if (!isRecognition) {
// return
// }
// binding.dualCameraFaceRectView.clearFaceInfo()
// facePreviewInfoList = recognizeViewModel.onPreviewFrame(nv21, true)
// if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
// drawPreviewInfo(facePreviewInfoList)
// }
// if (facePreviewInfoList.isNullOrEmpty()) {
// currentPlateInfo = null
// currentUserId = null
// userFaceInfo = null
// }
// recognizeViewModel.clearLeftFace(facePreviewInfoList)
// }
//
// override fun onCameraClosed() {
// Timber.i("initRgbCamera onCameraClosed: ")
// }
//
// override fun onCameraError(e: java.lang.Exception) {
// Timber.i("initRgbCamera onCameraError: %s", e.message)
// e.printStackTrace()
// }
//
// override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
// Timber.i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
// if (rgbFaceRectTransformer != null) {
// rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
// }
// Timber.i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
// }
// }
//
// val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
// rgbCameraHelper = DualCameraHelper.Builder()
// .previewViewSize(
// Point(
// binding.dualCameraTexturePreviewRgb.measuredWidth,
// binding.dualCameraTexturePreviewRgb.measuredHeight
// )
// )
// .rotation(windowManager.defaultDisplay.rotation)
// .additionalRotation(0) // 角度
// .previewSize(recognizeViewModel.loadPreviewSize())
// .specificCameraId(previewConfig.rgbCameraId)
// .isMirror(true)
// .previewOn(binding.dualCameraTexturePreviewRgb)
// .cameraListener(cameraListener)
// .build()
// rgbCameraHelper!!.init()
// rgbCameraHelper!!.start()
// }
//
// /**
// * 绘制RGB、IR画面的实时人脸信息
// *
// * @param facePreviewInfoList RGB画面的实时人脸信息
// */
// private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo>?) {
// if (rgbFaceRectTransformer != null) {
// val rgbDrawInfoList: MutableList<DrawInfo>? = recognizeViewModel.getDrawInfo(
// facePreviewInfoList, LivenessType.RGB, openRectInfoDraw
// )
// // 识别成功
// binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
// }
// }
//
// fun openRectInfoDraw(view: View?) {
// openRectInfoDraw = !openRectInfoDraw
// recognizeViewModel.setDrawRectInfoTextValue(openRectInfoDraw)
// }
//
//
// override fun onGlobalLayout() {
// loadLogInfo("onGlobalLayout")
// binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
// // 请求权限
// checkCameraPermission()
// }
//
// private fun resumeCamera() {
// isRecognition = true
// if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
// loadLogInfo("collectFace,resumeCamera: ")
// rgbCameraHelper!!.start()
// } else {
// recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
// }
// }
//
// /**
// * 临时用户id
// */
// private var tempUserId: String = ""
// override fun onResume() {
// super.onResume()
// registerHandler = RegisterCallbackHandler()
// try {
// if (isFirstOpen) {
// goInitActivity()
// binding.root.postDelayed({
// isFirstOpen = false
// }, 1000)
// return
// }
// retryFailCount = 0
// binding.tvSimilarValue.text = ""
// isCollecting.set(false)
// isCollectFace = false
// plateEnable = true
// collectCount = 0
// tempUserId = "${System.currentTimeMillis()}"
// resumeCamera()
// userViewModel.resetUserInfo()
// countDownTimer?.let {
// it.cancel()
// it.start()
// }
//// initFaceScheduler()
//// checkFaceState()
// } catch (e: Exception) {
// e.printStackTrace()
// }
// }
//
// private var isFirstOpen = true
//
// protected override fun onPause() {
// pauseCamera()
// super.onPause()
// countDownTimer?.cancel()
// }
//
// private fun pauseCamera() {
// isRecognition = false
// recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
// }
//
// override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
// super.handleLoginSuccess(equipmentUserInfo, isAdmin)
//// goInitActivity()
// }
//
// private val totalTimeInMillis = 15 * 1000L
// fun initCountTime() {
// countDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) {
// override fun onTick(millisUntilFinished: Long) {
// loadLogInfo("initCountTime onTick = ${(millisUntilFinished / 1000).toInt()}")
// binding.tvToInit.text = "${(millisUntilFinished / 1000).toInt() + 1}"
// }
//
// override fun onFinish() {
// goInitActivity()
// }
// }
// }
//
// fun goInitActivity() {
// val intent = Intent(context, InitActivity::class.java)
// startActivity(intent)
// }
//
// /**
// * 采集人脸
// */
// private fun collectFace() {
// loadLogInfo("collectFace: -----------1,开始采集人脸,开始时间:${DateTimeUtils.getDateTimeString()}----------")
// if (isFirstOpen) return
// loadLogInfo("collectFace: ----------2,isFirstOpen=false-----------")
// loadLogInfo("collectFace: ----------4,检测到人脸信息-----------")
// currentUserId = null
// userFaceInfo = null
// recognizeViewModel.updateRegisterStatus(REGISTER_STATUS_READY)
// loadLogInfo("collectFace: ----------5,人脸采集中-----------")
// }
//
// private val uploadFaceDebouncer = Debouncer(10000)
//
// /**
// * 改为上传人脸数据,不需要发生人脸数据到收银端
// */
// private fun sendFaceData(faceData: String, headBmp: Bitmap) {
// loadLogInfo("collectFace,sendFaceData: ----------11,发送人脸数据-----------")
//
// val imageFile = BitmapSaver.saveToAppFilesDir(
// headBmp, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
// )
// loadLogInfo("collectFace,sendFaceData:${imageFile?.absolutePath}")
// if (imageFile == null) {
// ToastUtils.showToast("获取人脸照片失败")
// return
// }
// uploadFaceDebouncer.debounce {
// addUserFace(faceData, imageFile)
// }
//// // 给某个客户端发送
//// val jsonObject = JSONObject().also {
//// it.put("type", "faceFeature")
//// it.put("content", faceData)
//// }
//// tcpClient?.send(jsonObject)
// }
//
// private fun addUserFace(faceData: String, imageFile: File) {
// settingViewModel.addUserFace(faceData, imageFile) { status, faceModel ->
// loadLogInfo("collectFace,saveFaceInfo: ----------12,人脸数据发送完成时间:${DateTimeUtils.getDateTimeString()},结果:$status-----------")
// if (status.not()) {
// ToastUtils.showToast("新增数据失败")
// loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据失败,faceData=${faceData}")
// return@addUserFace
// }
// if (faceModel == null) {
// ToastUtils.showToast("新增数据成功,但未拿到接口数据")
// loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据成功但接口返回为nullfaceData=${faceData}")
// return@addUserFace
// }
// Thread {
// saveFaceInfo(faceModel)
// uploadFaceDebouncer.reset()
// }.start()
// }
// }
//
// private fun getFaceDataBlock(block: (String, Bitmap) -> Unit) {
// if (userFaceInfo == null || userFaceInfo?.faceFeature?.featureData == null) {
// return
// }
// val faceData: String? = Base64.encode(userFaceInfo?.faceFeature?.featureData)
// if (faceData.isNullOrBlank()) {
// return
// }
// loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据时间:${DateTimeUtils.getDateTimeString()}-----------")
// block(faceData, userFaceInfo!!.headBmp)
// }
//
// /**
// * 加载日志
// * @param msg 日志内容
// */
// private fun loadLogInfo(msg: String) {
// Timber.tag(TAG).d(msg)
// LogFileUtil.saveLog(msg)
// }
//
// private var facePreviewInfoList: MutableList<FacePreviewInfo>? = null
// private var userFaceInfo: UserFaceInfo? = null
//
// private var currentUserId: String? = null
//
// private fun saveFaceInfo(faceModel: UserFaceModel) {
// try {
// loadLogInfo("collectFace,saveFaceInfo: ----------8,保存人脸取特征数据-----------")
// val faceDao = FaceDatabase.getInstance(this).faceDao()
// val faceEntity = FaceEntity(
// faceModel.userId,
// null,
// Base64.decode(faceModel.faceFeatureStr)
// ).also {
// //1-会员、2-临时用户
// it.userType = if (faceModel.isMember) "1" else "2"
// }
// faceDao.insert(faceEntity)
// loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
// recognizeViewModel.refreshFaceList()
// loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
// runOnUiThread { recognizeViewModel.onPreviewFrame(ByteArray(1382400), true) }
// loadLogInfo("---------------------------------")
// collectCount = 0
// } catch (e: Exception) {
// e.printStackTrace()
// loadLogInfo("collectFace,saveFaceInfo: ----------13,人脸采集异常:${e.message}-----------")
// collectCount++
// loadLogInfo("collectFace,saveFaceInfo: ----------14,${collectCount}次人脸采集异常:${e.message}-----------")
// ToastUtils.showToast("人脸采集异常:${e.message}")
// }
// }
//
// private var collectCount = 0
//
//// private var tcpClient: TcpClient? = null
////
//// private fun addSocketListener() {
////// val ipAddress = NetworkUtils.getIPAddress(true)
////// loadLogInfo( "addSocketListener,ipAddress: $ipAddress");
//// tcpClient = TcpClient(
//// "192.168.1.95",
//// 5000,
//// GlobalData.deviceId, // clientId
//// 5000, // connectTimeoutMs
//// 10 * 1000, // heartbeatIntervalMs
//// 30 * 1000 // heartbeatTimeoutMs
//// )
////
//// tcpClient?.setListener(object : TcpClientListenerImpl() {
//// override fun onSendSuccess(json: JSONObject?) {
//// super.onSendSuccess(json)
//// loadLogInfo("collectFace,TcpClientListener发送成功:$json")
////// toast("发送成功")
//// }
////
//// override fun onSendFailed(json: JSONObject?, e: java.lang.Exception?) {
//// super.onSendFailed(json, e)
//// ToastUtils.showToast("发送失败${e?.message}")
//// loadLogInfo("collectFace,TcpClientListener人脸数据发送失败:${e?.message}")
//// }
////
//// override fun onMessage(json: JSONObject?) {
//// super.onMessage(json)
//// loadLogInfo("collectFace,TcpClientListener收到消息:$json")
//// val type = json?.getInt("type")
//// // 根据服务端消息类型处理删除新增临时用户人脸数据---------------------
//// when (type) {
//// LanServer.TYPE_ADD_FACE_DATA -> {
//// //采集设备id自己生成,不接收服务端的,在本地只判断是否存在同一人脸数据
//// loadLogInfo("collectFace,TcpClientListener收到消息,新增用户:采集设备id自己生成,不接收服务端的,在本地只判断是否存在同一人脸数据")
//// }
////
//// LanServer.TYPE_CLEAR_FACE_DATA -> {
//// clearFaceData()
//// }
////
//// else -> {}
//// }
//// }
//// })
//// tcpClient?.start()
////
//// try {
//// LanServer.getInstance().let {
//// it.setListener(object : LanServerListenerImpl() {
//// override fun onMessageReceived(clientId: String?, message: JSONObject?) {
//// super.onMessageReceived(clientId, message)
//// ToastUtils.showToast("收到消息clientId=$clientId")
//// }
//// })
//// it.start()
//// }
//// } catch (e: Exception) {
//// e.printStackTrace()
//// }
//// }
//
// private fun clearFaceData() {
// Thread {
// val faceDao = FaceDatabase.getInstance(this).faceDao()
// faceDao.deleteTempUserFaceData()
// recognizeViewModel.refreshFaceList()
// loadLogInfo("collectFace,TcpClientListener收到消息,已删除并刷新人脸数据")
// }.start()
// }
//
// private var isCollectFace = false
//
//}
@@ -69,4 +69,15 @@ fun View.clickWithCoroutines(
lastClickTime = currentTime
}
}
}
fun View.clickWithDebounce(delay: Long = 500, action: () -> Unit) {
var job: Job? = null
setOnClickListener {
job?.cancel()
job = CoroutineScope(Dispatchers.Main).launch {
delay(delay)
action()
}
}
}
@@ -105,6 +105,46 @@
android:textSize="36sp"
tools:text="30" />
<LinearLayout
android:id="@+id/llCollectButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_gravity="bottom"
android:layout_marginBottom="100dp"
android:visibility="gone">
<TextView
android:id="@+id/btnRetry"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:text="再次重试"
android:textColor="#FFCC99"
android:textSize="24sp"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="10dp"
android:paddingHorizontal="45dp"
android:gravity="center"
android:background="@drawable/btn_outline"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/btnCollect"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:text="新用户采集"
android:textColor="#FFCC99"
android:textSize="24sp"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="10dp"
android:paddingHorizontal="45dp"
android:gravity="center"
android:background="@drawable/btn_outline"
tools:ignore="HardcodedText" />
</LinearLayout>
<!-- <LinearLayout-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->