Browse Source

refactor(更新) :
- 虹软激活信息改为从配置文件中读取

周文健 3 tháng trước cách đây
mục cha
commit
ce5606c2cd

+ 34 - 11
app/src/main/java/com/grkj/iscs_mars/util/ArcSoftUtil.kt

@@ -27,7 +27,6 @@ import com.grkj.iscs_mars.view.activity.test.face.arcsoft.CameraHelper
 import com.grkj.iscs_mars.view.activity.test.face.arcsoft.CameraListener
 import com.grkj.iscs_mars.view.activity.test.face.arcsoft.NV21ToBitmap
 import com.grkj.iscs_mars.view.widget.FaceOverlayView
-import com.sik.sikcore.extension.file
 import com.sik.sikcore.extension.toJson
 import com.sik.sikcore.file.FileStorageUtils
 import java.io.File
@@ -60,23 +59,47 @@ object ArcSoftUtil {
     )
 
     fun checkActiveStatus(context: Context) {
-        val arcSoftLicenseFile =
-            "${FileUtil.getRootFolder(context)?.absolutePath}${FileUtil.CONFIG_DIR}${File.separator}${configFileName}".file()
-        val configJson = if (arcSoftLicenseFile.exists()) {
-            arcSoftLicenseFile.readText()
-        } else {
+        val configJson = try {
+            val arcSoftLicenseFile =
+                File("${FileUtil.getRootFolder(context)?.absolutePath}${FileUtil.CONFIG_DIR}${File.separator}${configFileName}")
+            File("${FileUtil.getRootFolder(context)?.absolutePath}${FileUtil.CONFIG_DIR}").apply {
+                if (!exists()) {
+                    mkdirs()
+                }
+            }
+            LogUtil.i("配置文件路径:${arcSoftLicenseFile.absolutePath}")
+            if (arcSoftLicenseFile.exists()) {
+                arcSoftLicenseFile.readText()
+            } else {
+                arcSoftLicenseFile.createNewFile()
+                LogUtil.i("文件不存在,写一份")
+                val defaultJson = ArcSoftLicenseConfig(
+                    Constants.APP_ID,
+                    Constants.SDK_KEY,
+                    Constants.ACTIVE_KEY
+                ).toJson()
+                arcSoftLicenseFile.writeText(defaultJson)
+                defaultJson
+            }
+        } catch (e: Exception) {
+            val arcSoftLicenseFile =
+                File("${FileUtil.getRootFolder(context)?.absolutePath}${FileUtil.CONFIG_DIR}${File.separator}${configFileName}")
+            LogUtil.i("获取文件报错,写一份")
+            File("${FileUtil.getRootFolder(context)?.absolutePath}${FileUtil.CONFIG_DIR}").apply {
+                if (!exists()) {
+                    mkdirs()
+                }
+            }
+            arcSoftLicenseFile.createNewFile()
             val defaultJson = ArcSoftLicenseConfig(
                 Constants.APP_ID,
                 Constants.SDK_KEY,
                 Constants.ACTIVE_KEY
             ).toJson()
-            FileStorageUtils.writeText(
-                "${FileUtil.getRootFolder(context)?.absolutePath}${FileUtil.CONFIG_DIR}",
-                "$configFileName",
-                defaultJson
-            )
+            arcSoftLicenseFile.writeText(defaultJson)
             defaultJson
         }
+        LogUtil.i("配置文件内容:${configJson}")
         val arcSoftLicenseConfig: ArcSoftLicenseConfig =
             gson.fromJson(configJson, ArcSoftLicenseConfig::class.java)
         val activeCode = FaceEngine.activeOnline(

+ 23 - 11
app/src/main/java/com/grkj/iscs_mars/view/activity/test/face/arcsoft/ArcsoftTestActivity.kt

@@ -27,17 +27,19 @@ import com.grkj.iscs_mars.view.base.BaseActivity
 class ArcsoftTestActivity : BaseActivity<ActivityArcsoftTestBinding>(), OnGlobalLayoutListener {
 
     private var cameraHelper: CameraHelper? = null
-//    private val drawHelper: DrawHelper? = null
+
+    //    private val drawHelper: DrawHelper? = null
     private var previewSize: Camera.Size? = null
     private val rgbCameraId = Camera.CameraInfo.CAMERA_FACING_BACK
     private var faceEngine: FaceEngine? = null
     private var afCode = -1
     private val processMask: Int =
-        FaceEngine.ASF_AGE or FaceEngine.ASF_FACE3DANGLE or FaceEngine.ASF_GENDER or FaceEngine.ASF_LIVENESS
+        FaceEngine.ASF_AGE or FaceEngine.ASF_GENDER or FaceEngine.ASF_LIVENESS
 
 
     companion object {
         private const val ACTION_REQUEST_PERMISSIONS: Int = 0x001
+
         /**
          * 所需的所有权限信息
          */
@@ -57,14 +59,17 @@ class ArcsoftTestActivity : BaseActivity<ActivityArcsoftTestBinding>(), OnGlobal
     }
 
     private fun checkActiveStatus() {
-        val activeCode = FaceEngine.activeOnline(this, Constants.APP_ID, Constants.SDK_KEY)
+        val activeCode =
+            FaceEngine.activeOnline(this, Constants.ACTIVE_KEY, Constants.APP_ID, Constants.SDK_KEY)
         when (activeCode) {
             ErrorInfo.MOK -> {
                 ToastUtils.tip("active success")
             }
+
             ErrorInfo.MERR_ASF_ALREADY_ACTIVATED -> {
                 ToastUtils.tip("already activated")
             }
+
             else -> {
                 ToastUtils.tip("active failed")
             }
@@ -95,10 +100,9 @@ class ArcsoftTestActivity : BaseActivity<ActivityArcsoftTestBinding>(), OnGlobal
         afCode = faceEngine!!.init(
             this,
             DetectMode.ASF_DETECT_MODE_VIDEO,
-            DetectFaceOrientPriority.valueOf("ASF_OP_0_ONLY"),
-            16,
-            20,
-            FaceEngine.ASF_FACE_DETECT or FaceEngine.ASF_AGE or FaceEngine.ASF_FACE3DANGLE or FaceEngine.ASF_GENDER or FaceEngine.ASF_LIVENESS
+            DetectFaceOrientPriority.ASF_OP_0_ONLY,
+            1,
+            FaceEngine.ASF_FACE_DETECT or FaceEngine.ASF_AGE or FaceEngine.ASF_GENDER or FaceEngine.ASF_LIVENESS
         )
         LogUtil.i("initEngine:  init: $afCode")
         if (afCode != ErrorInfo.MOK) {
@@ -175,11 +179,10 @@ class ArcsoftTestActivity : BaseActivity<ActivityArcsoftTestBinding>(), OnGlobal
                 val faceLivenessInfoList: List<LivenessInfo> = ArrayList()
                 val ageCode = faceEngine!!.getAge(ageInfoList)
                 val genderCode = faceEngine!!.getGender(genderInfoList)
-                val face3DAngleCode = faceEngine!!.getFace3DAngle(face3DAngleList)
                 val livenessCode = faceEngine!!.getLiveness(faceLivenessInfoList)
 
                 // 有其中一个的错误码不为ErrorInfo.MOK,return
-                if ((ageCode or genderCode or face3DAngleCode or livenessCode) != ErrorInfo.MOK) {
+                if ((ageCode or genderCode or livenessCode) != ErrorInfo.MOK) {
                     return
                 }
 
@@ -189,7 +192,11 @@ class ArcsoftTestActivity : BaseActivity<ActivityArcsoftTestBinding>(), OnGlobal
                     return
                 }
 //                val bitmap = FaceUtils.getFaceBitmap(nv21!!, previewSize!!.width, previewSize!!.height, faceInfoList[0])
-                val bitmap = NV21ToBitmap(this@ArcsoftTestActivity).nv21ToBitmap(nv21, previewSize!!.width, previewSize!!.height)
+                val bitmap = NV21ToBitmap(this@ArcsoftTestActivity).nv21ToBitmap(
+                    nv21,
+                    previewSize!!.width,
+                    previewSize!!.height
+                )
 //                val bitmap = BitmapUtil.convertNV21ToBitmap(nv21, previewSize!!.width, previewSize!!.height)
                 LogUtil.i("识别结果 : ${bitmap == null} - $faceInfoList")
                 mBinding?.ivCapture?.setImageBitmap(bitmap)
@@ -227,7 +234,12 @@ class ArcsoftTestActivity : BaseActivity<ActivityArcsoftTestBinding>(), OnGlobal
             }
         }
         cameraHelper = CameraHelper.Builder()
-            .previewViewSize(Point(mBinding?.texturePreview!!.getMeasuredWidth(), mBinding?.texturePreview!!.getMeasuredHeight()))
+            .previewViewSize(
+                Point(
+                    mBinding?.texturePreview!!.getMeasuredWidth(),
+                    mBinding?.texturePreview!!.getMeasuredHeight()
+                )
+            )
             .rotation(windowManager.defaultDisplay.rotation)
             .specificCameraId(rgbCameraId ?: Camera.CameraInfo.CAMERA_FACING_FRONT)
             .isMirror(false)

+ 0 - 480
app/src/main/java/com/grkj/iscs_mars/view/activity/test/face/arcsoft/FaceUtils.kt

@@ -1,480 +0,0 @@
-package com.grkj.iscs_mars.view.activity.test.face.arcsoft
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.ImageFormat
-import android.graphics.Rect
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.media.Image
-import android.media.ImageReader
-import android.os.Environment
-import android.util.Log
-import android.util.Size
-import com.arcsoft.face.*
-import com.arcsoft.face.enums.DetectFaceOrientPriority
-import com.arcsoft.face.enums.DetectMode
-import com.arcsoft.imageutil.ArcSoftImageFormat
-import com.arcsoft.imageutil.ArcSoftImageUtil
-import com.arcsoft.imageutil.ArcSoftImageUtilError
-import com.arcsoft.imageutil.ArcSoftRotateDegree
-import java.io.File
-import java.io.FileInputStream
-import java.io.FileOutputStream
-import java.io.IOException
-import kotlin.math.abs
-
-object FaceUtils {
-    val EXT_ROOT_PATH : String = Environment.getExternalStorageDirectory().absolutePath
-    private var faceEngine: FaceEngine? = null
-//    private val faceDataList = ArrayList<FaceBean>()
-
-    /**
-     * 存放特征的目录
-     */
-    private const val NEW_FEATURE_DIR = "face-features"
-
-    fun extFile(path: String) : File {
-        return File(EXT_ROOT_PATH + File.separator + "refuse-class" + File.separator + path)
-    }
-    
-    fun moveOldFolderToNew() {
-        val oldRegDir = File(EXT_ROOT_PATH + File.separator + "register")
-        if (!oldRegDir.exists() || !oldRegDir.isDirectory) {
-            Log.i("FACE", "旧目录 register 不存在")
-            return
-        }
-        val oldDir = File(oldRegDir.path + File.separator + "features")
-        if (!oldDir.exists() || !oldDir.isDirectory) {
-            Log.i("FACE", "旧目录 register/features 不存在")
-            return
-        }
-        val newDir = extFile(NEW_FEATURE_DIR)
-        if (!newDir.exists()) {
-            val success = newDir.mkdirs()
-            Log.i("FACE", "创建新目录 /refuse-class/face-features : ${success}")
-        }
-        for (oldFile in oldDir.listFiles()) {
-            val newFile = extFile(NEW_FEATURE_DIR + File.separator + oldFile.name)
-            if (!newFile.exists()) {
-                newFile.createNewFile()
-            }
-            val data = ByteArray(FaceFeature.FEATURE_SIZE)
-            val fis = FileInputStream(oldFile)
-            fis.read(data)
-            fis.close()
-            Log.i("FACE", "读取旧目录人脸特征:${oldFile.name}")
-            val fos = FileOutputStream(newFile)
-            fos.write(data)
-            fos.close()
-            Log.i("FACE", "写入新目录人脸特征:${oldFile.name}")
-            oldFile.delete()
-            Log.i("FACE", "删除旧目录人脸特征:${oldFile.name}")
-        }
-        oldDir.delete()
-        Log.i("FACE", "删除旧目录 register/features")
-        oldRegDir.delete()
-        Log.i("FACE", "删除旧目录 register")
-    }
-
-
-    /**
-     * 初始化
-     *
-     * @param context 上下文对象
-     * @return 是否初始化成功
-     */
-    fun init(context: Context): Int {
-        synchronized(this) {
-            if (faceEngine == null) {
-                val t0 = System.currentTimeMillis()
-                faceEngine = FaceEngine()
-                val engineCode = faceEngine!!.init(
-                    context,
-                    DetectMode.ASF_DETECT_MODE_VIDEO,
-                    DetectFaceOrientPriority.ASF_OP_ALL_OUT,
-                    16,
-                    1,
-                    // 人脸监测 | 人脸特征 | RGB 活体
-                    FaceEngine.ASF_FACE_DETECT or FaceEngine.ASF_FACE_RECOGNITION or FaceEngine.ASF_LIVENESS
-                )
-                val tt = System.currentTimeMillis() - t0
-                Log.i("face", "人脸识别引擎初始化:$engineCode, 耗时:$tt")
-                if (engineCode == ErrorInfo.MOK) {
-                    initFaceList()
-                } else {
-                    faceEngine = null
-                }
-                return engineCode
-            }
-            return ErrorInfo.MOK
-        }
-    }
-
-    /**
-     * 销毁
-     */
-//    fun unInit() {
-//        synchronized(this) {
-//            faceDataList.clear()
-//            faceEngine?.unInit()
-//            faceEngine = null
-//        }
-//    }
-
-    /**
-     * 清空人脸特性
-     */
-    fun clearFaces() {
-        extFile(NEW_FEATURE_DIR).delete()
-    }
-
-    /**
-     * 初始化人脸特征数据以及人脸特征数据对应的注册图
-     *
-     */
-    private fun initFaceList() {
-        synchronized(this) {
-            val featureDir = extFile(NEW_FEATURE_DIR)
-            if (!featureDir.exists() || !featureDir.isDirectory) {
-                return
-            }
-            for (featureFile in featureDir.listFiles()) {
-                try {
-                    val fis = FileInputStream(featureFile)
-                    val feature = ByteArray(FaceFeature.FEATURE_SIZE)
-                    fis.read(feature)
-                    fis.close()
-//                    faceDataList.add(FaceBean(featureFile.name, feature))
-                } catch (e: IOException) {
-                    Log.e("face", "加载人脸数据异常", e)
-                }
-            }
-        }
-    }
-
-    /**
-     * 检测人脸信息
-     */
-    fun detectFace(nv21: ByteArray, width: Int, height: Int) : FaceInfo? {
-        return faceEngine?.run {
-            val faceInfos = ArrayList<FaceInfo>()
-            val code = detectFaces(nv21, width, height, FaceEngine.CP_PAF_NV21, faceInfos)
-            if (code == ErrorInfo.MOK && faceInfos.size > 0) {
-                Log.d("face", "检测到 ${faceInfos.size} 个人脸")
-                return faceInfos.maxByOrNull { faceInfo ->
-                    val r = faceInfo.rect
-                    abs((r.left - r.right) * (r.top - r.bottom))
-                }
-            } else {
-                Log.i("face", "未检测到人脸: $code")
-                return null
-            }
-        }
-    }
-
-    /**
-     * 活体检测
-     */
-    fun detectLive(nv21: ByteArray, faceInfo: FaceInfo, width: Int, height: Int) : LivenessInfo? {
-        return faceEngine?.run {
-            val pcode = process(nv21, width, height, FaceEngine.CP_PAF_NV21, listOf(faceInfo), FaceEngine.ASF_LIVENESS)
-            val livenesses = ArrayList<LivenessInfo>()
-            if (pcode == ErrorInfo.MOK) {
-                val lcode = getLiveness(livenesses)
-                if (lcode == ErrorInfo.MOK && livenesses.size > 0) {
-                    return livenesses[0]
-                } else {
-                    Log.d("face", "提取 RGB 活体信息失败:$lcode")
-                }
-            } else {
-                Log.i("face", "人脸属性检测失败:$pcode")
-            }
-            return null
-        }
-    }
-
-    /**
-     * 人脸特征提取
-     */
-    fun getFaceFeature(nv21: ByteArray, faceInfo: FaceInfo, width: Int, height: Int) : FaceFeature? {
-        return faceEngine?.run {
-            val faceFeature = FaceFeature()
-            val code = extractFaceFeature(nv21, width, height, FaceEngine.CP_PAF_NV21, faceInfo, faceFeature)
-            if (code == ErrorInfo.MOK) {
-                return faceFeature
-            } else {
-                Log.i("face", "人脸特征提取失败:$code")
-            }
-            return null
-        }
-    }
-
-    /**
-     * 在特征库中搜索最相似的一个
-     * 原名:getTopOfFaceLib
-     * @param feature 传入特征数据
-     * @return 比对结果
-     */
-//    fun findMostSimilarFace(feature: FaceFeature): FaceResultBean? {
-//        if (faceDataList.size == 0) {
-//            return null
-//        }
-//        return faceEngine?.run {
-//            val tmpFeature = FaceFeature()
-//            val faceSimilar = FaceSimilar()
-//            var maxSimilarScore = 0f
-//            var maxSimilarIndex = -1
-//            for (i in faceDataList.indices) {
-//                tmpFeature.featureData = faceDataList[i].featureData
-//
-//                compareFaceFeature(feature, tmpFeature, faceSimilar)
-//
-//                if (faceSimilar.score > maxSimilarScore) {
-//                    maxSimilarScore = faceSimilar.score
-//                    maxSimilarIndex = i
-//                }
-//            }
-//            return if (maxSimilarIndex > -1) {
-//                FaceResultBean(faceDataList[maxSimilarIndex].name, maxSimilarScore)
-//            } else null
-//        }
-//    }
-
-    /**
-     * 获取面部图像
-     */
-    fun getFaceBitmap(nv21: ByteArray, width: Int, height: Int, faceInfo: FaceInfo): Bitmap? {
-        if (width % 4 != 0 || nv21.size != width * height * 3 / 2) {
-            Log.e("face", "invalid params")
-            return null
-        }
-        return faceEngine?.run {
-            // 保存注册结果(注册图、特征数据)
-            // 为了美观,扩大rect截取注册图
-            val cropRect = getBestRect(width, height, faceInfo.rect)
-            if (cropRect == null) {
-                Log.e("face", "registerNv21: cropRect is null!")
-                return null
-            }
-            cropRect.left = cropRect.left and 3.inv()
-            cropRect.top = cropRect.top and 3.inv()
-            cropRect.right = cropRect.right and 3.inv()
-            cropRect.bottom = cropRect.bottom and 3.inv()
-            // 创建一个头像的 Bitmap,存放旋转结果图
-            return getHeadImage(nv21, width, height, faceInfo.orient, cropRect)
-        }
-    }
-
-    /**
-     * 保存人脸特征
-     */
-//    fun saveFeature(feature: FaceFeature, name: String): Boolean {
-//        synchronized(this) {
-//            //特征存储的文件夹
-//            val featureDir = extFile(NEW_FEATURE_DIR)
-//            if (!featureDir.exists() && !featureDir.mkdirs()) {
-//                Log.e("face", "registerNv21: can not create feature directory")
-//                return false
-//            }
-//            try {
-//                val fosFeature = FileOutputStream(featureDir.path + File.separator + name)
-//                fosFeature.write(feature.featureData)
-//                fosFeature.close()
-//                //内存中的数据同步
-//                faceDataList.add(FaceBean(name, feature.featureData))
-//                return true
-//            } catch (e: IOException) {
-//                Log.e("face", "保存图片特征异常", e)
-//                return false
-//            }
-//        }
-//    }
-
-    /**
-     * 截取合适的头像并旋转,保存为注册头像
-     *
-     * @param originImageData 原始的BGR24数据
-     * @param width           BGR24图像宽度
-     * @param height          BGR24图像高度
-     * @param orient          人脸角度
-     * @param cropRect        裁剪的位置
-     * @param imageFormat     图像格式
-     * @return 头像的图像数据
-     */
-    private fun getHeadImage(
-        originImageData: ByteArray,
-        width: Int,
-        height: Int,
-        orient: Int,
-        cropRect: Rect,
-        imageFormat: ArcSoftImageFormat = ArcSoftImageFormat.NV21
-    ): Bitmap {
-        val headImageData = ArcSoftImageUtil.createImageData(cropRect.width(), cropRect.height(), imageFormat)
-        val cropCode = ArcSoftImageUtil.cropImage(
-            originImageData,
-            headImageData,
-            width,
-            height,
-            cropRect,
-            imageFormat
-        )
-        if (cropCode != ArcSoftImageUtilError.CODE_SUCCESS) {
-            throw RuntimeException("crop image failed, code is $cropCode")
-        }
-
-        //判断人脸旋转角度,若不为0度则旋转注册图
-        var rotateHeadImageData: ByteArray? = null
-        val rotateCode: Int
-        val cropImageWidth: Int
-        val cropImageHeight: Int
-        // 90度或270度的情况,需要宽高互换
-        if (orient == FaceEngine.ASF_OC_90 || orient == FaceEngine.ASF_OC_270) {
-            cropImageWidth = cropRect.height()
-            cropImageHeight = cropRect.width()
-        } else {
-            cropImageWidth = cropRect.width()
-            cropImageHeight = cropRect.height()
-        }
-        var rotateDegree: ArcSoftRotateDegree? = null
-        when (orient) {
-            FaceEngine.ASF_OC_90 -> rotateDegree = ArcSoftRotateDegree.DEGREE_270
-            FaceEngine.ASF_OC_180 -> rotateDegree = ArcSoftRotateDegree.DEGREE_180
-            FaceEngine.ASF_OC_270 -> rotateDegree = ArcSoftRotateDegree.DEGREE_90
-            FaceEngine.ASF_OC_0 -> rotateHeadImageData = headImageData
-            else -> rotateHeadImageData = headImageData
-        }
-        // 非0度的情况,旋转图像
-        if (rotateDegree != null) {
-            rotateHeadImageData = ByteArray(headImageData.size)
-            rotateCode = ArcSoftImageUtil.rotateImage(
-                headImageData,
-                rotateHeadImageData,
-                cropRect.width(),
-                cropRect.height(),
-                rotateDegree,
-                imageFormat
-            )
-            if (rotateCode != ArcSoftImageUtilError.CODE_SUCCESS) {
-                throw RuntimeException("rotate image failed, code is $rotateCode")
-            }
-        }
-        // 将创建一个Bitmap,并将图像数据存放到Bitmap中
-        val headBmp = Bitmap.createBitmap(cropImageWidth, cropImageHeight, Bitmap.Config.RGB_565)
-        if (ArcSoftImageUtil.imageDataToBitmap(
-                rotateHeadImageData,
-                headBmp,
-                imageFormat
-            ) != ArcSoftImageUtilError.CODE_SUCCESS
-        ) {
-            throw RuntimeException("failed to transform image data to bitmap")
-        }
-        return headBmp
-    }
-
-    /**
-     * 将图像中需要截取的Rect向外扩张一倍,若扩张一倍会溢出,则扩张到边界,若Rect已溢出,则收缩到边界
-     *
-     * @param width   图像宽度
-     * @param height  图像高度
-     * @param srcRect 原Rect
-     * @return 调整后的Rect
-     */
-    private fun getBestRect(
-        width: Int,
-        height: Int,
-        srcRect: Rect?
-    ): Rect? {
-        if (srcRect == null) {
-            return null
-        }
-        val rect = Rect(srcRect)
-        // 原rect边界已溢出宽高的情况
-        val maxOverFlow = Math.max(
-            -rect.left,
-            Math.max(
-                -rect.top,
-                Math.max(rect.right - width, rect.bottom - height)
-            )
-        )
-        if (maxOverFlow >= 0) {
-            rect.inset(maxOverFlow, maxOverFlow)
-            return rect
-        }
-        // 原rect边界未溢出宽高的情况
-        var padding = rect.height() / 2
-        // 若以此padding扩张rect会溢出,取最大padding为四个边距的最小值
-        if (!(rect.left - padding > 0 && rect.right + padding < width && rect.top - padding > 0 && rect.bottom + padding < height)) {
-            padding = Math.min(
-                Math.min(
-                    Math.min(rect.left, width - rect.right),
-                    height - rect.bottom
-                ),
-                rect.top
-            )
-        }
-        rect.inset(-padding, -padding)
-        return rect
-    }
-
-    fun optimalOutputSize(cameraManager: CameraManager,cameraId: String, width: Int, height: Int) : Size {
-        return cameraManager.getCameraCharacteristics(cameraId).get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
-            ?.getOutputSizes(ImageReader::class.java)
-            ?.minByOrNull {
-                (it.width - height) * (it.height - width)
-            }
-            ?: Size(width, height)
-    }
-
-    fun getDataByNV21(cropRect:Rect,format:Int,planes:Array<Image.Plane>) : ByteArray {
-        val width: Int = cropRect.width()
-        val height: Int = cropRect.height()
-        val data = ByteArray(width * height * ImageFormat.getBitsPerPixel(format) / 8)
-        val rowData = ByteArray(planes[0].rowStride)
-        var channelOffset = 0
-        var outputStride = 1
-        for (i in planes.indices) {
-            when (i) {
-                0 -> {
-                    channelOffset = 0
-                    outputStride = 1
-                }
-                1 -> {
-                    channelOffset = width * height + 1
-                    outputStride = 2
-                }
-                2 -> {
-                    channelOffset = width * height
-                    outputStride = 2
-                }
-            }
-            val buffer = planes[i].buffer
-            val rowStride = planes[i].rowStride
-            val pixelStride = planes[i].pixelStride
-            val shift = if (i == 0) 0 else 1
-            val w = width shr shift
-            val h = height shr shift
-            buffer.position(rowStride * (cropRect.top shr shift) + pixelStride * (cropRect.left shr shift))
-            for (row in 0 until h) {
-                var length: Int
-                if (pixelStride == 1 && outputStride == 1) {
-                    length = w
-                    buffer.get(data, channelOffset, length)
-                    channelOffset += length
-                } else {
-                    length = (w - 1) * pixelStride + 1
-                    buffer.get(rowData, 0, length)
-                    for (col in 0 until w) {
-                        data[channelOffset] = rowData[col * pixelStride]
-                        channelOffset += outputStride
-                    }
-                }
-                if (row < h - 1) {
-                    buffer.position(buffer.position() + rowStride - length)
-                }
-            }
-        }
-        return data
-    }
-
-}