Browse Source

:seedling: 整合人脸SDK

everywindchase 10 months ago
parent
commit
c5ac252812

+ 5 - 121
app/src/main/java/com/ethan/psbc/ui/dialogs/DialogValidateFace.kt

@@ -1,34 +1,15 @@
 package com.ethan.psbc.ui.dialogs
 
-import android.Manifest
 import android.content.Context
-import android.content.pm.PackageManager
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.ArcShape
-import android.util.Log
-import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
-import androidx.camera.core.*
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import androidx.lifecycle.lifecycleScope
-import cn.face.sdk.FaceDetTrack
-import cn.face.sdk.FaceInterface
-import cn.face.sdk.FaceParam
 import com.ethan.psbc.R
 import com.ethan.psbc.databinding.DialogValidateFaceBinding
 import com.lxj.xpopup.impl.FullScreenPopupView
-import kotlinx.coroutines.launch
-import java.nio.ByteBuffer
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
 
 /**
  * <p>人脸验证界面</p>
  * @author gy
  */
-class DialogValidateFace( val mContext: Context) : FullScreenPopupView(mContext) {
+class DialogValidateFace( mContext: Context) : FullScreenPopupView(mContext){
 
 
 
@@ -38,115 +19,18 @@ class DialogValidateFace( val mContext: Context) : FullScreenPopupView(mContext)
         return R.layout.dialog_validate_face
     }
 
-    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 val licence:String="MDQwNTE3NWQyNTkzNGM4MmQ3ZDk2MmQyM2UzODBhMmI1YjFjNjdjd2F1dGhvcml6Zd3n5+Xk5+fk/OXm5+fg5ef15efn5+Xk4Mfl4OXm5uvliObm6+Xm5uuR5ebm6+Xm5qLr5ebm6+Xmrubg5ufr5+v95+vn4OTk59Dk5ubr5+vn1uvn5+fm5Of+5g=="
 
 
 
 
-    override fun onCreate() {
+    override  fun onCreate() {
         super.onCreate()
         mBinding = DialogValidateFaceBinding.bind(popupContentView)
+        mBinding.cameraScan.bringToFront()
 
-
-
-       if( PackageManager.PERMISSION_GRANTED!= ContextCompat.checkSelfPermission(mContext    as AppCompatActivity, Manifest.permission.CAMERA)){
-           ActivityCompat.requestPermissions(mContext    as AppCompatActivity, arrayOf(Manifest.permission.CAMERA),1)
-           Toast.makeText(mContext    as AppCompatActivity, "申请相机权限", Toast.LENGTH_SHORT).show()
-       }else{
-           Toast.makeText(mContext    as AppCompatActivity, "已经拥有相机权限", Toast.LENGTH_SHORT).show()
-       }
-
-
-      val  cameraSelector: CameraSelector =CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
-
-
-
-
-
-        cameraExecutor = Executors.newSingleThreadExecutor()
-        val cameraProviderFuture = ProcessCameraProvider.getInstance(mContext)
-        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)
-
-
-
-                Log.d("demo", "正在识别图像信息")
-                FaceDetTrack.getInstance()
-                val ret: Int = FaceDetTrack.cwCreateDetHandle("", licence)
-                if (ret >= FaceInterface.cw_errcode_t.CW_UNKNOWN_ERR) {
-                    Log.d("demo", "启用识别程序SDK失败")
-                } else {
-                    Log.d("demo", "启用识别程序SDK成功")
-                    val faceParam = FaceParam()
-                    FaceDetTrack.cwGetFaceParam(ret, faceParam)
-                    faceParam.minSize = 30
-                    faceParam.maxSize = 400;
-                    FaceDetTrack.cwSetFaceParam(ret, faceParam, "");
-
-                }
-
-
-
-
-                imageProxy.close()
-            }
-
-
-
-            try {
-                cameraProvider?.unbindAll()//先解绑所有用例
-                camera = cameraProvider?.bindToLifecycle(
-                    this,
-                    cameraSelector,
-                    preview,
-                    imageCamera,
-                    imageAnalysis
-                )!!//绑定用例
-            } catch (_: Exception) {
-
-            }
-            val shapeDrawable =ShapeDrawable(ArcShape(30F,180F))
-            mBinding.btnDemoView.background=shapeDrawable
-
-
-
-            lifecycleScope.launch() {
-                preview?.setSurfaceProvider(mBinding.btnDemoView.surfaceProvider)
-            }
-
-
-        }, ContextCompat.getMainExecutor(mContext))
-
+            mBinding.cameraScan.startScan()
+            mBinding.cameraScan.runCamera(context)
 
     }
 

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

@@ -0,0 +1,311 @@
+package com.ethan.psbc.ui.widgets
+
+import android.Manifest
+import android.animation.ValueAnimator
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.TypedArray
+import android.graphics.*
+import android.graphics.Canvas.ALL_SAVE_FLAG
+import android.util.AttributeSet
+import android.util.Log
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.view.animation.LinearInterpolator
+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.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import cn.face.sdk.FaceDetTrack
+import cn.face.sdk.FaceInterface
+import cn.face.sdk.FaceParam
+import com.ethan.psbc.R
+import java.nio.ByteBuffer
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import kotlin.math.cos
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.sin
+
+class CameraScanView(mContext: Context, attrs:AttributeSet?, defStyleAttr:Int) : SurfaceView(mContext,attrs,defStyleAttr) ,SurfaceHolder.Callback {
+
+    constructor(context: Context) : this(context, null)
+    constructor(context: Context,  attrs: AttributeSet?) : this(context, attrs, 0)
+
+
+
+    private var mBackGroundPaint: Paint
+    private var mCirclePaint: Paint
+    private var mScanLinePaint: Paint
+
+    //渐变色的起始值
+    private var mStartColor: Int
+    //渐变色的终止值
+    private var mFinishColor: Int
+
+    //圆形半径
+    private var mRadius: Float
+    //整个view的宽度
+    private var mWidth: Float = 0.0f
+    //整个view的高度度
+    private var mHeight: Float = 0.0f
+    //圆心的横坐标
+    private var mCenterX: Float = 0.0f
+    //圆心的纵坐标
+    private var mCenterY: Float = 0.0f
+
+    //扫描线(梯形)右上角的点对圆心的角度
+    private var mDegree: Double = 0.0
+    //扫描线(梯形)右下角的点对圆心的角度-右上角的点对圆心的角度的差值,体现为扫描线的线宽
+    private var mDegreeAdd: Double = 1.0
+
+    private val mXfermode: Xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)
+    //遮罩的颜色
+    private var mBackGroundColor: Int
+    //扫描的动画时间
+    private var mDuration: Int
+    private var mAnimator: ValueAnimator? = null
+
+    init {
+        @SuppressLint("Recycle")
+        val typedArray: TypedArray =
+            context.obtainStyledAttributes(attrs, R.styleable.CameraScan)
+
+        mBackGroundPaint = Paint()
+        mBackGroundPaint.run {
+            style = Paint.Style.FILL
+            isAntiAlias = true // 设置抗锯齿
+            isDither = true // 设置抖动
+        }
+
+        mBackGroundColor = typedArray.getColor(R.styleable.CameraScan_backGroundColor, Color.WHITE)
+
+
+        mCirclePaint = Paint()
+        mCirclePaint.run {
+            style = Paint.Style.STROKE
+            isAntiAlias = true
+            isDither = true
+            strokeWidth = typedArray.getDimension(R.styleable.CameraScan_circleWidth, 10f)
+            color = typedArray.getColor(R.styleable.CameraScan_circleColor, Color.BLACK)
+        }
+        mRadius = typedArray.getDimension(R.styleable.CameraScan_circleRadius, 0.0f)
+
+
+        mScanLinePaint = Paint()
+        mScanLinePaint.run {
+            style = Paint.Style.FILL
+            strokeCap = Paint.Cap.ROUND
+            isAntiAlias = true
+            isDither = true
+            color = Color.GREEN
+        }
+        mDegreeAdd = typedArray.getFloat(R.styleable.CameraScan_scanLineWidth, 1.0f).toDouble()
+        mDuration = typedArray.getInt(R.styleable.CameraScan_scanDuration, 10000)
+        mStartColor = typedArray.getColor(R.styleable.CameraScan_scanLineStartColor, -1)
+        mFinishColor = typedArray.getColor(R.styleable.CameraScan_scanLineFinishColor, -1)
+
+        typedArray.recycle()
+    }
+
+
+    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
+        }
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+
+        //绘制黑色遮罩,先绘制黑色背景,再绘制透明圆形
+        mBackGroundPaint.color = Color.BLACK //随便设个颜色值,只要不是透明的就行,防止第二次绘制时,mBackGroundPaint.color 被下面赋值了Color.TRANSPARENT 导致canvas.drawColor(mBackGroundColor)不起效果
+        val layerID = canvas.saveLayer(0.0f, 0.0f, mWidth, mHeight, mBackGroundPaint, ALL_SAVE_FLAG)  //保存图层,下面通过estoreToCount恢复
+        canvas.drawColor(mBackGroundColor) //遮罩的颜色
+        mBackGroundPaint.xfermode = mXfermode //这里选择DST_ATOP
+        mBackGroundPaint.color = Color.TRANSPARENT //颜色设置成透明
+        canvas.drawCircle(mCenterX, mCenterY, mRadius, mBackGroundPaint) //透明的圆形
+        mBackGroundPaint.xfermode = null
+        canvas.restoreToCount(layerID)
+
+        //绘制白色框,圆弧
+        canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint)
+
+
+        //角度制转弧度制
+        val radians = Math.toRadians(mDegree).toFloat()
+        val radiansAdd = Math.toRadians(mDegree + mDegreeAdd).toFloat()
+
+        //着色器,实现扫描线的渐变效果
+        takeIf { mStartColor != -1 && mFinishColor != -1 }?.run {
+            val y0 = mCenterY - mRadius * cos(radians)
+            val y1 = mCenterY - mRadius * cos(radiansAdd)
+            mScanLinePaint.shader = LinearGradient(0.0f, max(y0, y1), 0.0f, min(y0, y1), mStartColor, mFinishColor, Shader.TileMode.CLAMP)
+        }
+
+        //绘制扫描线,梯形
+        val path = Path()
+        path.moveTo(mCenterX + mRadius * sin(-radians), mCenterY - mRadius * cos(radians))
+        path.lineTo(mCenterX + mRadius * sin(radians), mCenterY - mRadius * cos(radians))
+        path.lineTo(mCenterX + mRadius * sin(radiansAdd), mCenterY - mRadius * cos(radiansAdd))
+        path.lineTo(mCenterX + mRadius * sin(-radiansAdd), mCenterY - mRadius * cos(radiansAdd))
+        path.close()
+        canvas.drawPath(path, mScanLinePaint)
+    }
+
+
+    //扫描动画,通过改变角度,实现扫描线上下移动
+    fun startScan() {
+        mAnimator?.run { cancel() }
+        mAnimator = ValueAnimator.ofFloat(0.0f, 360.0f)
+        mAnimator?.let {
+            it.addUpdateListener { animation ->
+                this.mDegree = (animation.animatedValue as Float).toDouble()
+                invalidate()
+            }
+            it.duration = mDuration.toLong()
+            it.interpolator = LinearInterpolator()
+            it.repeatCount = ValueAnimator.INFINITE
+            it.repeatMode = ValueAnimator.RESTART
+            it.start()
+        }
+
+    }
+
+
+    override fun surfaceCreated(holder: SurfaceHolder) {
+
+    }
+
+
+    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+        TODO("Not yet implemented")
+    }
+
+    override fun surfaceDestroyed(holder: SurfaceHolder) {
+        TODO("Not yet implemented")
+    }
+
+
+
+    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 val licence:String="MDQwNTE3NWQyNTkzNGM4MmQ3ZDk2MmQyM2UzODBhMmI1YjFjNjdjd2F1dGhvcml6Zd3n5+Xk5+fk/OXm5+fg5ef15efn5+Xk4Mfl4OXm5uvliObm6+Xm5uuR5ebm6+Xm5qLr5ebm6+Xmrubg5ufr5+v95+vn4OTk59Dk5ubr5+vn1uvn5+fm5Of+5g=="
+
+
+
+
+     fun runCamera(context: Context){
+
+        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()
+        // mBinding.btnDemoView.implementationMode= PreviewView.ImplementationMode.COMPATIBLE
+
+
+
+
+        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)
+
+
+
+                Log.d("demo", "正在识别图像信息")
+                FaceDetTrack.getInstance()
+                val ret: Int = FaceDetTrack.cwCreateDetHandle("", licence)
+                if (ret >= FaceInterface.cw_errcode_t.CW_UNKNOWN_ERR) {
+                    Log.d("demo", "启用识别程序SDK失败")
+                } else {
+                    Log.d("demo", "启用识别程序SDK成功")
+                    val faceParam = FaceParam()
+                    FaceDetTrack.cwGetFaceParam(ret, faceParam)
+                    faceParam.minSize = 30
+                    faceParam.maxSize = 400;
+                    FaceDetTrack.cwSetFaceParam(ret, faceParam, "");
+
+                }
+
+
+
+
+                imageProxy.close()
+            }
+
+
+
+            try {
+                cameraProvider?.unbindAll()//先解绑所有用例
+                camera = cameraProvider?.bindToLifecycle(
+                    context,
+                    cameraSelector,
+                    preview,
+                    imageCamera,
+                    imageAnalysis
+                )!!//绑定用例
+            } catch (_: Exception) {
+
+            }
+
+            preview?.setSurfaceProvider(null)
+
+
+
+        }, ContextCompat.getMainExecutor(context))
+    }
+
+
+}

+ 28 - 7
app/src/main/res/layout/dialog_validate_face.xml

@@ -10,14 +10,35 @@
 
 
 
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/camera_layout"
+        android:layout_width="350dp"
+        android:layout_height="600dp"
+        android:layout_gravity="center">
 
-                <androidx.camera.view.PreviewView
-                    android:id="@+id/btn_demo_view"
-                    android:layout_width="@dimen/dp_350"
-                    android:layout_height="@dimen/dp_450"
-                    android:layout_gravity="center"
-                    android:orientation="vertical"
-                    />
+
+
+
+        <com.ethan.psbc.ui.widgets.CameraScanView
+            android:id="@+id/camera_scan"
+                     android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:backGroundColor="@color/qmui_s_transparent"
+            app:circleColor="@color/white_alpha_99"
+            app:circleRadius="160dp"
+            app:circleWidth="2dp"
+            app:scanLineFinishColor="#0000FA9A"
+            app:scanLineStartColor="#00FA9A"
+            app:scanLineWidth="5.0"
+            app:scanDuration = "7000">
+
+
+
+        </com.ethan.psbc.ui.widgets.CameraScanView>
+
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
 
 

+ 14 - 0
app/src/main/res/values/camera_scan.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <declare-styleable name="CameraScan">
+        <attr name="backGroundColor" format="color" />
+        <attr name="circleWidth" format="dimension" />
+        <attr name="circleRadius" format="dimension" />
+        <attr name="circleColor" format="color" />
+        <attr name="scanLineWidth" format="float" />
+        <attr name="scanLineStartColor" format="color" />
+        <attr name="scanLineFinishColor" format="color" />
+        <attr name="scanDuration" format="integer" />
+    </declare-styleable>
+
+</resources>