Преглед на файлове

:seedling: 整合人脸SDK

everywindchase преди 10 месеца
родител
ревизия
546dd51383

+ 3 - 0
app/src/main/AndroidManifest.xml

@@ -36,6 +36,9 @@
     <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
 
 
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
+       />
+
 
     <application
         android:name=".apps.Application"

+ 5 - 0
app/src/main/java/com/ethan/psbc/constants/ConstantApp.java

@@ -11,4 +11,9 @@ public class ConstantApp {
     public static final String EMPTY_JSON_STRING = "{}";
     public static final String EMPTY_STRING      = "";
     public static boolean isUserLoginOut =false;
+
+
+
+    public static  final   String  faceLicense="MDQwNTE3NWQyNTkzNGM4MmQ3ZDk2MmQyM2UzODBhMmI1YjFjNjdjd2F1dGhvcml6Zd3n5+Xk5+fk/OXm5+fg5ef15efn5+Xk4Mfl4OXm5uvliObm6+Xm5uuR5ebm6+Xm5qLr5ebm6+Xmrubg5ufr5+v95+vn4OTk59Dk5ubr5+vn1uvn5+fm5Of+5g==";
+
 }

+ 250 - 2
app/src/main/java/com/ethan/psbc/ui/dialogs/DialogValidateFace.kt

@@ -1,9 +1,30 @@
 package com.ethan.psbc.ui.dialogs
 
+import android.Manifest
 import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.AssetManager
+import android.os.Environment
+import android.util.Log
+import android.view.View
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.camera.core.*
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.view.PreviewView
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.LifecycleOwner
+import cn.face.sdk.FaceInterface
+import cn.face.sdk.FaceRecog
 import com.ethan.psbc.R
+import com.ethan.psbc.constants.ConstantApp
 import com.ethan.psbc.databinding.DialogValidateFaceBinding
 import com.lxj.xpopup.impl.FullScreenPopupView
+import java.io.*
+import java.nio.ByteBuffer
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
 
 /**
  * <p>人脸验证界面</p>
@@ -20,23 +41,250 @@ class DialogValidateFace( mContext: Context) : FullScreenPopupView(mContext){
     }
 
 
+    init {
+        initSdk(mContext)
+    }
+
+
+
+    private lateinit    var faceConfigFile:File
+
+    var faceHandleRet:Int=-1
+
+
+
+    fun initSdk(mContext:Context){
+
+
+        try {
+            Log.d("demo", "访问外部文件权限" + Environment.getExternalStorageState())
+
+
+            val dirPath= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+
+
+
+            //createModelFileAll(dirPath, mContext.assets)
+
+            faceConfigFile = File(dirPath, "CWR_Config_1_1.xml")
+
+
+            Log.d("demo", "初始化人脸识别SDK")
+            FaceRecog.getInstance();
+            faceHandleRet = FaceRecog.cwCreateRecogHandle(faceConfigFile.absolutePath, ConstantApp.faceLicense, 0)
+            Log.d("demo", "获取SDK句柄:$faceHandleRet")
+            if (faceHandleRet >= FaceInterface.cw_errcode_t.CW_UNKNOWN_ERR) {
+                faceHandleRet = -1
+            } else {
+                faceHandleRet = 0
+            }
+
+
+        }catch (e:Exception){
+            Log.d("demo", "人脸SDK加载失败:"+e.message)
+            faceHandleRet = -1
+        }
+
+    }
+
+
+
+
+
+
+    fun createModelFileAll(modelDirPath: String, assetManager: AssetManager) {
+        val parentDir = "CWModels"
+        var fileNameList: List<String?>? = null
+        var files: Array<String?>? = null
+
+        var fileAbsPath = ""
+        fileAbsPath = if (modelDirPath.endsWith("/")) {
+            modelDirPath
+        } else {
+            "$modelDirPath/"
+        }
+        mkDir(modelDirPath)
+        try {
+            files = assetManager.list(parentDir)
+            fileNameList =files?.toCollection(ArrayList())
+        } catch (e: IOException) {
+            Log.e("demo", e.message!!)
+        }
+
+        for (fileName in fileNameList!!) {
+            Log.e("demo", fileName!!)
+            val modelFile = File(fileAbsPath + fileName)
+            if (!modelFile.exists()) {
+                copyRawFileToSdcard(parentDir + File.separator + fileName, assetManager, fileAbsPath + fileName)
+            }
+        }
+    }
+
+
+    fun mkDir(dirPath: String) {
+        val dirArray = dirPath.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+        var pathTemp = ""
+        for (i in 1 until dirArray.size) {
+            pathTemp = pathTemp + "/" + dirArray[i]
+            val newF = File(dirArray[0] + pathTemp)
+            if (!newF.exists()) {
+                newF.mkdir()
+            }
+        }
+    }
+
+
+    fun copyRawFileToSdcard(rawFileName: String?, assetManager: AssetManager, outPutFileAbs: String?) {
+        var inputStream: InputStream? = null
+        try {
+            inputStream = assetManager.open(rawFileName!!)
+        } catch (e: IOException) {
+            Log.e("tag", e.message!!)
+        }
+
+        if (inputStream != null) inputStreamToFile(inputStream, outPutFileAbs)
+    }
+
+
+    fun inputStreamToFile(inputStream: InputStream, absPath: String?) {
+        val outputStream = ByteArrayOutputStream()
+
+
+        val bufferSize = 1024
+        val buffer = ByteArray(bufferSize)
+        var length: Int
+
+        try {
+            while (inputStream.read(buffer).also { length = it } != -1) {
+                outputStream.write(buffer, 0, length)
+            }
+        } finally {
+            inputStream.close()
+            outputStream.close()
+        }
+
 
+        try {
+            val imgBytes = outputStream.toByteArray()
+            val fos = FileOutputStream(absPath, false)
+            fos.write(imgBytes)
+            fos.close()
+        } catch (e: IOException) {
+            e.printStackTrace()
+        }
+    }
 
 
 
     override  fun onCreate() {
         super.onCreate()
         mBinding = DialogValidateFaceBinding.bind(popupContentView)
+        mBinding.cameraLayout.visibility= View.VISIBLE
         mBinding.mPreviewView.bringToFront()
-        mBinding.cameraScan.mPreview=mBinding.mPreviewView
-
 
 
 
+        runCamera(  this  ,mBinding.mPreviewView,faceHandleRet)
 
     }
 
 
+    private lateinit var cameraExecutor: ExecutorService
+    private var cameraProvider: ProcessCameraProvider? = null
+    private var preview: Preview? = null
+    private var imageCamera: ImageCapture? = null
+    private lateinit var camera: Camera //相机对象
+    private lateinit   var imageAnalysis: ImageAnalysis
+
+
+    fun runCamera(lifecycleOwner:LifecycleOwner, previewView: PreviewView?, handle:Int){
+
+        if( PackageManager.PERMISSION_GRANTED!= ContextCompat.checkSelfPermission(context    , Manifest.permission.CAMERA)){
+            ActivityCompat.requestPermissions(context    as AppCompatActivity, arrayOf(Manifest.permission.CAMERA),1)
+            Toast.makeText(context    as AppCompatActivity, "申请相机权限", Toast.LENGTH_SHORT).show()
+        }else{
+            Toast.makeText(context    as AppCompatActivity, "已经拥有相机权限", Toast.LENGTH_SHORT).show()
+        }
+
+
+        val  cameraSelector: CameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
+
+
+
+
+
+                cameraExecutor = Executors.newSingleThreadExecutor()
+                val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
+            cameraProviderFuture.addListener({
+                cameraProvider = cameraProviderFuture.get()//获取相机信息
+
+                //预览配置
+                preview = Preview.Builder()
+                    .build()
+
+
+
+
+
+
+
+                imageCamera = ImageCapture.Builder()
+                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
+                    .build()
+
+
+                imageAnalysis = ImageAnalysis.Builder()
+                    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
+                    .build()
+
+
+                imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
+                    val planes: Array<ImageProxy.PlaneProxy> = imageProxy.planes
+                    val buffer: ByteBuffer = planes[0].buffer;
+                    val data: ByteArray = ByteArray(buffer.capacity())
+                    buffer.rewind()
+                    buffer.get(data)
+
+
+                    if(handle==-1){
+                        Log.d("demo", "SDK无法使用")
+                    }else{
+                        val iFeaLen:Int = FaceRecog.cwGetFeatureLength(handle)
+                        Log.d("demo", "获取到人脸特征长度${iFeaLen}")
+                    }
+
+
+
+
+
+
+
+                    imageProxy.close()
+                }
+
+
+                try {
+                    cameraProvider?.unbindAll()//先解绑所有用例
+                    camera = cameraProvider?.bindToLifecycle(
+                        lifecycleOwner,
+                        cameraSelector,
+                        preview,
+                        imageCamera,
+                        imageAnalysis
+                    )!!//绑定用例
+                } catch (_: Exception) {
+
+                }
+
+
+
+
+                preview?.setSurfaceProvider(previewView?.surfaceProvider)
+
+
+
+        }, ContextCompat.getMainExecutor(context))
+    }
 
 
 

+ 0 - 358
app/src/main/java/com/ethan/psbc/ui/widgets/CameraScanView.kt

@@ -1,358 +0,0 @@
-package com.ethan.psbc.ui.widgets
-
-import android.Manifest
-import android.content.Context
-import android.content.pm.PackageManager
-import android.graphics.*
-import android.os.Environment
-import android.util.AttributeSet
-import android.util.Log
-import android.view.SurfaceHolder
-import android.view.SurfaceView
-import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
-import androidx.camera.core.*
-import androidx.camera.core.Camera
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.camera.view.PreviewView
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import cn.face.sdk.FaceInterface
-import cn.face.sdk.FaceRecog
-import java.io.File
-import java.io.FileOutputStream
-import java.io.InputStream
-import java.io.OutputStream
-import java.nio.ByteBuffer
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
-import kotlin.math.min
-
-
-class CameraScanView(mContext: Context, attrs:AttributeSet?, defStyleAttr:Int) : SurfaceView(mContext,attrs,defStyleAttr) {
-
-    constructor(context: Context) : this(context, null)
-    constructor(context: Context,  attrs: AttributeSet?) : this(context, attrs, 0)
-
-    private val mLogger: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(this.javaClass)
-
-    private var holder:SurfaceHolder
-
-    private var mSurfaceCallback= SurfaceCallback(mContext)
-
-    private lateinit    var faceConfigFile:File
-
-     var faceHandleRet:Int=-1
-
-    init {
-        holder=getHolder()
-        holder.addCallback(mSurfaceCallback)
-
-       Log.d("demo","访问外部文件权限"+Environment.getExternalStorageState())
-
-        try {
-            Log.d("demo", "访问外部文件权限" + Environment.getExternalStorageState())
-
-
-            val dirPath= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath+File.separator+"dd"
-
-
-
-            copyAssetsToMnt(mContext, "CWModels", dirPath)
-
-            faceConfigFile = File(dirPath, "CWR_Config_1_1.xml")
-
-            val licence = "MDQwNTE3NWQyNTkzNGM4MmQ3ZDk2MmQyM2UzODBhMmI1YjFjNjdjd2F1dGhvcml6Zd3n5+Xk5+fk/OXm5+fg5ef15efn5+Xk4Mfl4OXm5uvliObm6+Xm5uuR5ebm6+Xm5qLr5ebm6+Xmrubg5ufr5+v95+vn4OTk59Dk5ubr5+vn1uvn5+fm5Of+5g=="
-            Log.d("demo", "初始化人脸识别SDK")
-            FaceRecog.getInstance();
-            faceHandleRet = FaceRecog.cwCreateRecogHandle(faceConfigFile.path, licence, 0)
-            if (faceHandleRet >= FaceInterface.cw_errcode_t.CW_UNKNOWN_ERR) {
-                faceHandleRet = -1
-            } else {
-                faceHandleRet = 0
-            }
-
-
-        }catch (e:Exception){
-            Log.d("demo", "人脸SDK加载失败:"+e.message)
-            faceHandleRet = -1
-        }
-
-    }
-
-
-
-    fun deleteFolder(folder: File) {
-        if (folder.isDirectory) {
-            folder.listFiles()?.forEach {
-                deleteFolder(it)
-            }
-        }
-        folder.delete()
-    }
-    fun copyAssetsToMnt(context: Context, assetsPath: String, targetPath: String) {
-        val assetManager = context.assets
-        val files = assetManager.list(assetsPath) ?: emptyArray()
-
-        if (files.isNotEmpty()) {
-            val dir = File(targetPath, assetsPath)
-            if (!dir.exists()) {
-                dir.mkdirs()
-            }
-
-            for (fileName in files) {
-                val subAssetPath = if (assetsPath.isEmpty()) fileName else "$assetsPath/$fileName"
-                val subTargetPath = File(targetPath, subAssetPath)
-
-                if (subTargetPath.isDirectory) {
-                    copyAssetsToMnt(context, subAssetPath, targetPath)
-                } else {
-                    copyAssetFile(context, subAssetPath, subTargetPath.path)
-                }
-            }
-        } else {
-            // 如果是文件,直接复制
-            copyAssetFile(context, assetsPath, targetPath)
-        }
-    }
-
-    private fun copyAssetFile(context: Context, assetPath: String, targetPath: String) {
-        val assetManager = context.assets
-        val inputStream: InputStream = assetManager.open(assetPath)
-        val outputFile = File(targetPath)
-        if(outputFile.exists()){
-            outputFile.delete()
-        }
-        val outputStream: OutputStream = FileOutputStream(outputFile,true)
-
-        inputStream.use { input ->
-            outputStream.use { output ->
-                input.copyTo(output)
-            }
-        }
-    }
-
-    private var mWidth:Float=0.0f
-    private var mHeight:Float=0.0f
-    private var mCenterY:Float=0.0f
-    private var mCenterX:Float=0.0f
-    private var mRadius:Float=160f
-
-    lateinit   var mPreview:PreviewView
-    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-        mWidth = (measuredWidth - paddingLeft - paddingRight).toFloat()
-        mHeight = (measuredHeight - paddingTop - paddingBottom).toFloat()
-        mCenterX = mWidth / 2
-        mCenterY = mHeight / 2
-
-        val short = min(mWidth, mHeight)
-        if (mRadius == 0.0f || mRadius * 2 > short) {
-            mRadius = short / 2
-        }
-
-
-        mSurfaceCallback.mCenterX=this@CameraScanView.mCenterX
-        mSurfaceCallback.mCenterY=this@CameraScanView.mCenterY
-        mSurfaceCallback.mRadius=this@CameraScanView.mRadius
-        mSurfaceCallback.mPreview=this@CameraScanView.mPreview
-        mSurfaceCallback.faceConfigFile=this@CameraScanView.faceConfigFile
-        mSurfaceCallback.faceHandleRet = this@CameraScanView.faceHandleRet
-    }
-
-
-
-
-
-
-
-
-
-
-
-
-    class SurfaceCallback() :SurfaceHolder.Callback{
-
-            constructor(tContext:Context) : this() {
-                this.tContext=tContext
-            }
-
-
-        private lateinit var cameraExecutor: ExecutorService
-        private var cameraProvider: ProcessCameraProvider? = null
-        private var preview: Preview? = null
-        private var imageCamera: ImageCapture? = null
-        private lateinit var camera: Camera //相机对象
-        private lateinit   var imageAnalysis: ImageAnalysis
-        private lateinit   var tContext: Context
-
-        private val licence:String="MDQwNTE3NWQyNTkzNGM4MmQ3ZDk2MmQyM2UzODBhMmI1YjFjNjdjd2F1dGhvcml6Zd3n5+Xk5+fk/OXm5+fg5ef15efn5+Xk4Mfl4OXm5uvliObm6+Xm5uuR5ebm6+Xm5qLr5ebm6+Xmrubg5ufr5+v95+vn4OTk59Dk5ubr5+vn1uvn5+fm5Of+5g=="
-
-
-
-
-         var mCenterY:Float=0.0f
-         var mCenterX:Float=0.0f
-         var mRadius:Float=160f
-        lateinit   var mPreview:PreviewView
-        var faceConfigFile:File?=null
-        var faceHandleRet:Int=-1
-
-
-        override fun surfaceCreated(holder: SurfaceHolder) {
-
-
-            val canvas: Canvas =holder.lockCanvas()
-            var backLayer=Paint();
-            backLayer.run {
-
-                style = Paint.Style.FILL
-                isAntiAlias = true // 设置抗锯齿
-                isDither = true // 设置抖动
-
-            }
-            backLayer.color=Color.TRANSPARENT
-            var layerId=canvas.saveLayer(0.0F,0.0F,0.0F,0.0F,backLayer)
-            canvas.drawColor(Color.TRANSPARENT)
-            backLayer.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP) //这里选择DST_ATOP
-            backLayer.color = Color.TRANSPARENT //颜色设置成透明
-            canvas.drawCircle(mCenterX, mCenterY, mRadius, backLayer) //透明的圆形
-            backLayer.xfermode = null
-            canvas.restoreToCount(layerId)
-
-
-
-
-
-            val mCirclePaint= Paint()
-            mCirclePaint.run {
-                style = Paint.Style.STROKE
-                isAntiAlias = true
-                isDither = true
-                strokeWidth =10f
-                color =  Color.WHITE
-            }
-
-
-            //绘制白色框,圆弧
-            canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint)
-
-            holder.unlockCanvasAndPost(canvas);
-
-
-            runCamera(tContext,holder,mPreview, faceHandleRet)
-        }
-
-        override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
-
-        }
-
-        override fun surfaceDestroyed(holder: SurfaceHolder) {
-
-        }
-
-
-
-
-
-        fun runCamera(context: Context,holder: SurfaceHolder,previewView: PreviewView?, handle:Int){
-
-            if( PackageManager.PERMISSION_GRANTED!= ContextCompat.checkSelfPermission(context    , Manifest.permission.CAMERA)){
-                ActivityCompat.requestPermissions(context    as AppCompatActivity, arrayOf(Manifest.permission.CAMERA),1)
-                Toast.makeText(context    as AppCompatActivity, "申请相机权限", Toast.LENGTH_SHORT).show()
-            }else{
-                Toast.makeText(context    as AppCompatActivity, "已经拥有相机权限", Toast.LENGTH_SHORT).show()
-            }
-
-
-            val  cameraSelector: CameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
-
-
-
-
-
-            cameraExecutor = Executors.newSingleThreadExecutor()
-            val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
-            cameraProviderFuture.addListener({
-                cameraProvider = cameraProviderFuture.get()//获取相机信息
-
-                //预览配置
-                preview = Preview.Builder()
-                    .build()
-
-
-
-
-
-
-
-
-                imageCamera = ImageCapture.Builder()
-                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
-                    .build()
-
-
-                imageAnalysis = ImageAnalysis.Builder()
-                    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
-                    .build()
-
-
-                imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
-                    val planes: Array<ImageProxy.PlaneProxy> = imageProxy.planes
-                    val buffer: ByteBuffer = planes[0].buffer;
-                    val data: ByteArray = ByteArray(buffer.capacity())
-                    buffer.rewind()
-                    buffer.get(data)
-
-
-                    if(handle==-1){
-                        Log.d("demo", "SDK无法使用")
-                    }else{
-                        val iFeaLen:Int = FaceRecog.cwGetFeatureLength(handle)
-                        Log.d("demo", "获取到人脸特征长度${iFeaLen}")
-                    }
-
-
-
-
-
-
-
-                    imageProxy.close()
-                }
-
-
-
-                try {
-                    cameraProvider?.unbindAll()//先解绑所有用例
-                    camera = cameraProvider?.bindToLifecycle(
-                        context,
-                        cameraSelector,
-                        preview,
-                        imageCamera,
-                        imageAnalysis
-                    )!!//绑定用例
-                } catch (_: Exception) {
-
-                }
-
-
-
-
-                preview?.setSurfaceProvider(previewView?.surfaceProvider)
-
-
-
-            }, ContextCompat.getMainExecutor(context))
-        }
-
-
-    }
-
-}
-
-
-
-
-
-

+ 2 - 11
app/src/main/res/layout/dialog_validate_face.xml

@@ -14,17 +14,8 @@
         android:layout_height="350dp"
         android:gravity="center"
         android:layout_gravity="center"
-        android:background="@color/qmui_s_transparent">
-        <com.ethan.psbc.ui.widgets.CameraScanView
-            android:id="@+id/camera_scan"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintLeft_toLeftOf="parent"
-            app:layout_constraintRight_toRightOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
-            >
-        </com.ethan.psbc.ui.widgets.CameraScanView>
+        android:background="@color/qmui_s_transparent"  android:visibility="gone">
+