diff --git a/README.md b/README.md index b9923ea..8c9cdde 100644 --- a/README.md +++ b/README.md @@ -1,118 +1,145 @@ -# CameraXlib +# CameraX-Helper -集成了拍照,录制视频,人脸识别等的camerax库。 +可以组合任意用例,实现拍照,录制视频,人脸检测、识别。可在图像分析的同时录制视频或拍照。 +已适配存储,可使用saf、mediaStore、file等,仅需要一行简单的配置。 +有activity、fragment实现的相机界面,可以直接集成使用,也可以自由定制ui界面,实现自定义相机。 -适配了Android10以上的分区存储,可以将图片和视频存储到app私有目录,相册和相册下文件夹,其他SAF能授予文件夹权限的位置。 -Android10 以下,大家都很熟悉。 - -- 内置人脸检测,图像绘制,并预留出来了改变分析器使用其他图像分析的方法。 -- 内置人脸识别,使用tensorflow进行检测,并输出特征点。请查看TestFileDecActivity文件 +介绍: +- 人脸检测,图像绘制,并预留出来了改变分析器使用其他图像分析的方法。 +- 人脸识别,使用tensorflow进行检测,并输出特征点。请查看TestFileDecActivity文件 - 可以拍照,录制,暂停/继续录制,双指缩放,点按对焦,闪光灯,手电筒。 +- 可以组合任意的用例,比如 预览+图像分析+拍照 、预览+图像分析+录视频。 +- 支持以file、mediaStore、saf等存储方式,配置简单。 - 自定义配置相机功能,例如拍照时水平翻转或垂直翻转,分辨率和宽高比;视频的镜像翻转,文件或时长限制,视频清晰度等。 * 示例代码在app目录下。 +2024-06-28 +- 可任意组合不同用例。 + +2024-05-18 +- 文档正在施工 [地址](https://knightwood.github.io/CameraX-mlkit-FaceDetection/) + 2024-05-09 - 将mlkit和tensorflow拆分出来,新增`BaseCameraXFragment`实现相机,提供自定义布局功能等。 - 下一步计划:1.文档已经过于陈旧,需要大改。2.实现在compose中使用相机 -2024-05-18 -- 文档正在施工 [地址](https://knightwood.github.io/CameraX-mlkit-FaceDetection/) # 截图 -# [最新文档地址](https://knightwood.github.io/CameraX-mlkit-FaceDetection/) +# [最新文档地址](https://knightwood.github.io/CameraX-Helper/) # 用法 -`camerax_lib` module 中提供了`BaseCameraXActivity`和`CameraXFragment`类,后者持有`cameraHolder`实现相机功能, + +`camerax_lib` module 中提供了`BaseCameraXActivity`和`CameraXFragment`类,后者持有`cameraHolder` +实现相机功能, 前者则持有`CameraXFragment`,提供更进一步的封装。 整体的相机实现示例可以看app module下的`CameraExampleActivity`类。 -'CameraX'版本:1.3.0 +'CameraX'版本:1.3.4 _长期维护中_ - 使用: 1. 克隆代码到本地 + ```shell - git clone git@github.com:Knightwood/CameraX-mlkit-FaceDetection.git + git clone git@github.com:Knightwood/CameraX-Helper.git ``` + 2. 引入依赖 在AndroidStudio中,File->New->Import Module... - 将`camerax_lib`、`camerax_analyzer`、`camerax_analyzer_tensorflow`三个module导入到项目中。 - 如果不需要mlkit或tensorflow,可以不导入`camerax_analyzer`、`camerax_analyzer_tensorflow` 这两个module。 + 将`camerax_lib`、`camerax_analyzer_mlkit`、`camerax_analyzer_tensorflow`三个module导入到项目中。 + 如果不需要mlkit或tensorflow,可以不导入`camerax_analyzer_mlkit`、`camerax_analyzer_tensorflow` 这两个module。 + + 复制项目的build.gradle.kts文件中的ext + ```kotlin + ext { + this["version"] = "1.3.1" + this["abi"] = listOf("arm64-v8a") //listOf("armeabi", "armeabi-v7a", "arm64-v8a") + } + ``` + 3. app module的build.gradle文件添加依赖 + ```kotlin dependencies { - implementation(project(":camerax_lib")) - implementation(project(":camerax_analyzer")) //可选 + implementation(project(":camerax_lib_mlkit")) + implementation(project(":camerax_analyzer_mlkit")) //可选 implementation(project(":camerax_analyzer_tensorflow")) //可选 } ``` - ## 配置 + 以`CameraExampleActivity`为例 + ### 屏幕方向 + ```html //首先是配置: //屏幕方向这个可选,可以固定竖屏、横屏、不设置。 //需要在清单文件的相机activity中添加如下配置,另持有相机的activity在旋转屏幕时不被销毁重建 android:configChanges="orientation|screenSize" ``` + ### `CameraExampleActivity` 相机示例,配置存储,拍照,录像,分析器等 + #### 相机配置: + 例如可以从`MainActivity`启动`CameraExampleActivity`完成拍照和录制 所以,相机的配置可以通过intent传入。 一共有三种模式:拍照,录制,图像分析。 示例: + ```kotlin class CameraExampleActivity : BaseCameraXActivity() { // CameraExampleActivity中通过重写configAll 可配置相机一些内容,intent中的键值对为自定义的内容,与库无关 // 接收到intent,对相机进行配置 - override fun configAll(intent: Intent): ManagerConfig { - //视频录制配置(可选) - val videoRecordConfig = VideoRecordConfig( - quality = CameraRecordQuality.HD,//设置视频拍摄质量 + override fun configAll(intent: Intent): ManagerConfig { + //视频录制配置(可选) + val videoRecordConfig = VideoRecordConfig( + quality = CameraRecordQuality.HD,//设置视频拍摄质量 // fileSizeLimit=100.mb, //文件大小限制。 // durationLimitMillis =1000*15, //录制时长限制,单位毫秒 - //...省略 - ) - //拍照配置(可选) - val imageCaptureConfig =ImageCaptureConfig( - horizontalMirrorMode= MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, //水平翻转 - verticalMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, //垂直翻转 - //...省略 - ) - //整体的配置 - val useImageDetection = intent.getBooleanExtra(ImageDetection, false) //是否使用图像分析 - return ManagerConfig().apply { - this.recordConfig = videoRecordConfig - //这里使用了默认的用例组合 - this.useCaseBundle = - if (useImageDetection) UseCaseMode.imageAnalysis else UseCaseMode.takePhoto - - //当然,也可以使用自定义的用例组合 -// this.useCaseBundle = //通过调用UseCaseMode.customGroup方法自定义了一个可以预览,录像,图像分析的用例组合 + //...省略 + ) + //拍照配置(可选) + val imageCaptureConfig = ImageCaptureConfig( + horizontalMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, //水平翻转 + verticalMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY, //垂直翻转 + //...省略 + ) + //整体的配置 + val useImageDetection = intent.getBooleanExtra(ImageDetection, false) //是否使用图像分析 + return ManagerConfig().apply { + this.recordConfig = videoRecordConfig + //这里使用了默认的用例组合 + this.useCaseMode = + if (useImageDetection) UseCaseMode.imageAnalysis else UseCaseMode.takePhoto + + //当然,也可以使用自定义的用例组合 +// this.useCaseMode = //通过调用UseCaseMode.customGroup方法自定义了一个可以预览,录像,图像分析的用例组合 // UseCaseMode.customGroup( // UseCaseHexStatus.USE_CASE_PREVIEW, // UseCaseHexStatus.USE_CASE_IMAGE_ANALYZE, // UseCaseHexStatus.USE_CASE_VIDEO_CAPTURE // ) - - this.flashMode = FlashModel.CAMERA_FLASH_AUTO - this.size = Size(1920, 1080)//拍照,预览的分辨率,期望值,不一定会用这个值 + + this.flashMode = FlashModel.CAMERA_FLASH_AUTO + this.size = Size(1920, 1080)//拍照,预览的分辨率,期望值,不一定会用这个值 + } } - } } ``` #### 存储配置: + 全局的统一配置类: `CameraXStoreConfig`,若不进行配置,则默认存储到相册`CameraX`文件夹下 + 1. 调用CameraXStoreConfig.configPhoto()配置图片存储位置 2. 调用CameraXStoreConfig.configVideo()配置录制存储位置,使用方式与配置图片没有区别,仅方法名称不同 @@ -120,67 +147,73 @@ class CameraExampleActivity : BaseCameraXActivity() { ```kotlin class CameraExampleActivity : BaseCameraXActivity() { - + //对于拍摄和录制,可以分别配置存储位置,如果不进行配置,则默认存储到相册文件夹。 //例如:对于拍照的存储配置 - fun initPhotoStore() { - val relativePath = "fff" - //使用file绝对路径存储 - CameraXStoreConfig.configPhoto( - IStore.FileStoreConfig( - application.cacheDir.absolutePath, - relativePath - ) - ) - //使用MediaStore存储 - CameraXStoreConfig.configPhoto( - IStore.MediaStoreConfig( - saveCollection = FileLocate.IMAGE.uri, - mediaFolder = Environment.DIRECTORY_DCIM, - targetFolder = relativePath - ) - ) - //使用SAF框架存储到任意文件夹 - StoreX.with(this).safHelper.requestOneFolder { it -> - //使用SAF框架获取某一个文件夹的授权和uri,然后配置存储 - CameraXStoreConfig.configPhoto( - IStore.SAFStoreConfig(it) - ) + fun initPhotoStore() { + val relativePath = "fff" + //使用file绝对路径存储 + CameraXStoreConfig.configPhoto( + IStore.FileStoreConfig( + application.cacheDir.absolutePath, + relativePath + ) + ) + //使用MediaStore存储 + CameraXStoreConfig.configPhoto( + IStore.MediaStoreConfig( + saveCollection = FileLocate.IMAGE.uri, + mediaFolder = Environment.DIRECTORY_DCIM, + targetFolder = relativePath + ) + ) + //使用SAF框架存储到任意文件夹 + StoreX.with(this).safHelper.requestOneFolder { it -> + //使用SAF框架获取某一个文件夹的授权和uri,然后配置存储 + CameraXStoreConfig.configPhoto( + IStore.SAFStoreConfig(it) + ) + } } - } //视频的存储配置同拍照的存储配置相同,不过是把名称从`configPhoto`换成`configVideo`.可以参考app示例中的MainActivity } ``` + 完成配置即可使用相机功能。 +## `UseCaseHolder` -## `UseCaseHolder` * `UseCaseHolder` 类初始化预览,拍照用例,录像用例,图像分析用例 * 提供设置初始化用例方法 * 提供分辨率和纵横比筛选方式 ### 指定预览与拍照所需要的分辨率与纵横比筛选 - 指定[resolutionSelector],提供预览与拍照所需要的分辨率与纵横比筛选 + +指定[resolutionSelector],提供预览与拍照所需要的分辨率与纵横比筛选 + ```kotlin //不提供自己的实现,仅指定筛选条件用于预览和拍照 - UseCaseHolder.resolutionSelector = ResolutionSelector.Builder() - //分辨率筛选 - .setResolutionFilter { supportedSizes, rotationDegrees -> - supportedSizes - } - //纵横比选择策略 16:9 比例 - .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY) - //分辨率策略选择最高可用分辨率 - .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY) - //设置允许的分辨率模式。 - .setAllowedResolutionMode(ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION) - .build() +UseCaseHolder.resolutionSelector = ResolutionSelector.Builder() + //分辨率筛选 + .setResolutionFilter { supportedSizes, rotationDegrees -> + supportedSizes + } + //纵横比选择策略 16:9 比例 + .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY) + //分辨率策略选择最高可用分辨率 + .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY) + //设置允许的分辨率模式。 + .setAllowedResolutionMode(ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION) + .build() ``` + ### 提供自己所需要的初始化UseCase + 如默认的初始化不满足需要,可以调用[setInitImpl]方法,提供自己所需要的初始化 示例:使某个类继承自IUseCaseHelper,并实现初始化预览,拍照用例,录像用例,图像分析用例的方法 + ``` class IMPL:IUseCaseHelper{ ........省略 @@ -208,10 +241,12 @@ class CameraExampleActivity : BaseCameraXActivity() { 3. 或者可以自己实现一个activity,内部放置一个`CameraXFragment`实现相机功能 ## 示例相机 + CameraExampleActivity 继承自 BaseCameraXActivity, 后者在内部维护了一个cameraxFragment,此类持有了cameraholder实现相机功能, BaseCameraXActivity还生成了一个CameraXF类实现ICameraXF接口,功能委托给CameraXFragment, 如此可屏蔽fragment相关实现,方便相机操作,还可以获取cameraholder,此类可提供更多的相机操作 + ``` class CameraXF(private val cameraXF: CameraXFragment) : ICameraXF by cameraXF @@ -346,7 +381,8 @@ class CameraExampleActivity : BaseCameraXActivity() { ## 其他介绍 -* `CameraXFragment`实现` ICameraXF`接口,对外提供各种相机方法,实际上各类相机操作实现是由内部的`cameraHolder`实现。 +* `CameraXFragment`实现` ICameraXF` + 接口,对外提供各种相机方法,实际上各类相机操作实现是由内部的`cameraHolder`实现。 * `ICameraXF`接口则是为了屏蔽fragment的相关方法 * `CameraXFragment`内部创建`CameraHolder` @@ -439,18 +475,21 @@ interface CaptureResultListener { ## 人脸检测 ### google ml-kit教程: + #### ml-kit的检测器 + * 定义一个类,在类里加载ml-kit的检测器,然后,提供图像即可进行处理,本示例不包含连接到相机分析流部分 ````kotlin -val process=BaseImageAnalyzer() +val process = BaseImageAnalyzer() //传入bitmap调用分析 -process.processBitmap(bitmap){list-> - +process.processBitmap(bitmap) { list -> + } //这个类里维护了ml-kit 分析器 class BaseImageAnalyzer { private val executor = ScopedExecutor(TaskExecutors.MAIN_THREAD) + //ml-kit的人脸检测器 private val detector: FaceDetector @@ -473,9 +512,10 @@ class BaseImageAnalyzer { fun stop() { detector.close() } + //使用检测器开始处理图片 fun processBitmap(bitmap: Bitmap, listener: OnSuccessListener>) { - detector.process(InputImage.fromBitmap(bitmap, 0)) + detector.process(InputImage.fromBitmap(bitmap, 0)) .addOnSuccessListener(executor, listener) .addOnFailureListener( executor, @@ -489,61 +529,72 @@ class BaseImageAnalyzer { } ```` + #### 连接到相机分析流 -需要使类继承自ImageAnalysis.Analyzer,重写ImageAnalysis.Analyzer 的analyze方法,将此类作为analyzer usecase 绑定到相机后,相机自动调用其analyze方法,提供分析数据 + +需要使类继承自ImageAnalysis.Analyzer,重写ImageAnalysis.Analyzer 的analyze方法,将此类作为analyzer +usecase 绑定到相机后,相机自动调用其analyze方法,提供分析数据 + * camerax绑定用例示例(在CameraXManager类中,不需要手动调用): + ```kotlin cameraProvider.bindToLifecycle(lifeOwner, cameraSelector, preview, imageAnalyzer) ``` + * 继承ImageAnalysis.Analyzer,并调用ml-kit示例: -继承ImageAnalysis.Analyzer,在analyze方法中调用ml-kit的检测器,将相机数据交给ml-kit做人脸检测,检测器部分和上面是一样的。 + 继承ImageAnalysis.Analyzer,在analyze方法中调用ml-kit的检测器,将相机数据交给ml-kit做人脸检测,检测器部分和上面是一样的。 + ```kotlin class BaseImageAnalyzer : ImageAnalysis.Analyzer { - private val executor = ScopedExecutor(TaskExecutors.MAIN_THREAD) - //mlkit的人脸检测器 - private val detector: FaceDetector + private val executor = ScopedExecutor(TaskExecutors.MAIN_THREAD) + + //mlkit的人脸检测器 + private val detector: FaceDetector //...省略初始化detector和其他东西 - - //重写ImageAnalysis.Analyzer 的analyze方法 - @SuppressLint("UnsafeExperimentalUsageError", "UnsafeOptInUsageError") - override fun analyze(imageProxy: ImageProxy) { - val mediaImage = imageProxy.image - val rotationDegrees = imageProxy.imageInfo.rotationDegrees - - mediaImage?.let { - //检测 - detector.detectInImage(InputImage.fromMediaImage(it, rotationDegrees)) - .addOnSuccessListener { results -> - onSuccess( - imageProxy, - results, - graphicOverlay, - ) - } - .addOnFailureListener { - graphicOverlay.clear() - graphicOverlay.postInvalidate() - onFailure(it) - } - .addOnCompleteListener { - imageProxy.close() + + //重写ImageAnalysis.Analyzer 的analyze方法 + @SuppressLint("UnsafeExperimentalUsageError", "UnsafeOptInUsageError") + override fun analyze(imageProxy: ImageProxy) { + val mediaImage = imageProxy.image + val rotationDegrees = imageProxy.imageInfo.rotationDegrees + + mediaImage?.let { + //检测 + detector.detectInImage(InputImage.fromMediaImage(it, rotationDegrees)) + .addOnSuccessListener { results -> + onSuccess( + imageProxy, + results, + graphicOverlay, + ) + } + .addOnFailureListener { + graphicOverlay.clear() + graphicOverlay.postInvalidate() + onFailure(it) + } + .addOnCompleteListener { + imageProxy.close() + } } } - } } ``` + ### 本库中ml-kit使用: + * 使用google ml-kit 并连接到相机分析流示例: -还是以CameraExampleActivity 为例: + 还是以CameraExampleActivity 为例: 1. 需要在配置相机时,指定模式为CaptureMode.imageAnalysis + ````kotlin override fun configAll(intent: Intent): ManagerConfig { - //....省略 + //....省略 return ManagerConfig().apply { this.captureMode = CaptureMode.imageAnalysis//设置为分析图像模式 this.size = Size(1920, 1080)//拍照,预览的分辨率,期望值,不一定会用这个值 - } + } } ```` @@ -551,96 +602,101 @@ class BaseImageAnalyzer : ImageAnalysis.Analyzer { 2. 在cameraHolderInitStart方法中,调用 cameraHolder.changeAnalyzer()设置图像分析器,这样即可完成上面所说将分析器绑定到相机 * 示例1 使用ml-kit处理图像,绘制人脸边框和“特征点”的连线 - FaceContourDetectionProcessor继承自BaseImageAnalyzer,实现了绘制人脸图像框体,点位连线等的内容 + FaceContourDetectionProcessor继承自BaseImageAnalyzer,实现了绘制人脸图像框体,点位连线等的内容 + ```kotlin override fun cameraHolderInitStart(cameraHolder: CameraHolder) { - super.cameraHolderInitStart(cameraHolder) - val cameraPreview = cameraHolder.cameraPreview - - //使用mlkit进行人脸检测,并绘制人脸框体和点位 - val analyzer = FaceContourDetectionProcessor( - cameraPreview, - page.graphicOverlayFinder, - ).also { - cameraHolder.changeAnalyzer(it)//设置图像分析器 - } - //监听分析结果 - (analyzer as FaceContourDetectionProcessor).analyzeListener = - AnalyzeResultListener { - // when analyze success - } + super.cameraHolderInitStart(cameraHolder) + val cameraPreview = cameraHolder.cameraPreview + + //使用mlkit进行人脸检测,并绘制人脸框体和点位 + val analyzer = FaceContourDetectionProcessor( + cameraPreview, + page.graphicOverlayFinder, + ).also { + cameraHolder.changeAnalyzer(it)//设置图像分析器 } + //监听分析结果 + (analyzer as FaceContourDetectionProcessor).analyzeListener = + AnalyzeResultListener { + // when analyze success + } +} ``` * 示例2 还是使用ml-kit处理图像,得到包含人脸信息的照片,然后将其交给tensorflow lite模型处理,得到面部特征点。 + ```kotlin override fun cameraHolderInitStart(cameraHolder: CameraHolder) { - super.cameraHolderInitStart(cameraHolder) - //加载tensorflow lite模型,这里仅做演示 - val model = FaceDetection.create( - this.assets, - TestFileDecActivity.TF_OD_API_MODEL_FILE, - TestFileDecActivity.TF_OD_API_LABELS_FILE, - TestFileDecActivity.TF_OD_API_IS_QUANTIZED - ) - //TensorFlowLink继承自ImageAnalysis.Analyzer,将其作为分析器设置给相机即可拿到分析流 - val tensorFlowLink = TensorFlowLink {image: ImageProxy -> - //获取图像 - val bitmap= image.toBitmap() - //这里使用了ml-kit分析是否包含面部数据,如果包含,则将面部图像裁剪下来 - MyFileProcessor.process(bitmap){ - it?.let { - //将裁剪后的面部图像转换成特定尺寸bitmap - val tmp = FaceDetection.convertBitmap(it) - //将处理好的面部图像交给模型处理,获取特征点 - val masks = model.detectionBitmap(tmp) - } + super.cameraHolderInitStart(cameraHolder) + //加载tensorflow lite模型,这里仅做演示 + val model = FaceDetection.create( + this.assets, + TestFileDecActivity.TF_OD_API_MODEL_FILE, + TestFileDecActivity.TF_OD_API_LABELS_FILE, + TestFileDecActivity.TF_OD_API_IS_QUANTIZED + ) + //TensorFlowLink继承自ImageAnalysis.Analyzer,将其作为分析器设置给相机即可拿到分析流 + val tensorFlowLink = TensorFlowLink { image: ImageProxy -> + //获取图像 + val bitmap = image.toBitmap() + //这里使用了ml-kit分析是否包含面部数据,如果包含,则将面部图像裁剪下来 + MyFileProcessor.process(bitmap) { + it?.let { + //将裁剪后的面部图像转换成特定尺寸bitmap + val tmp = FaceDetection.convertBitmap(it) + //将处理好的面部图像交给模型处理,获取特征点 + val masks = model.detectionBitmap(tmp) } - - }.also { - cameraHolder.changeAnalyzer(it)//设置图像分析器 } + }.also { + cameraHolder.changeAnalyzer(it)//设置图像分析器 } +} + ``` + ### 除使用相机的分析流之外,还可以手动获取相机图片进行分析 + 示例:CameraExampleActivity文件中 + ```kotlin /** - * 每隔20ms从预览视图中获取bitmap - * 然后运行图像分析,绘制矩形框 - * 但是这种方式分析图象后,绘制框体会有延迟、卡顿感,不如直接使用图像分析流畅 - */ - suspend fun runFaceDetection(interval: Long = 20L) { - if (cameraConfig.isUsingImageAnalyzer() || stopAnalyzer) { - Log.d(TAG, "runFaceDetection: 已使用图像分析或stopAnalyzer==true") - return - } else { - BitmapProcessor.analyzeListener = AnalyzeResultListener { - // when analyze success - } - flow { - while (true) { - delay(interval) - emit(stopAnalyzer) - if (stopAnalyzer) { - break - } + * 每隔20ms从预览视图中获取bitmap + * 然后运行图像分析,绘制矩形框 + * 但是这种方式分析图象后,绘制框体会有延迟、卡顿感,不如直接使用图像分析流畅 + */ +suspend fun runFaceDetection(interval: Long = 20L) { + if (cameraConfig.isUsingImageAnalyzer() || stopAnalyzer) { + Log.d(TAG, "runFaceDetection: 已使用图像分析或stopAnalyzer==true") + return + } else { + BitmapProcessor.analyzeListener = AnalyzeResultListener { + // when analyze success + } + flow { + while (true) { + delay(interval) + emit(stopAnalyzer) + if (stopAnalyzer) { + break } - }.collect { - cameraXF.provideBitmap()?.let { originalBitmap -> - //识别图像 - BitmapProcessor.process(originalBitmap) { faces: List -> - //上面依据识别成功,得到了返回数据,我们在这里调用了一个普通方法来使用识别出来的数据 - BitmapProcessor.onSuccess(faces, page.graphicOverlayFinder) - } + } + }.collect { + cameraXF.provideBitmap()?.let { originalBitmap -> + //识别图像 + BitmapProcessor.process(originalBitmap) { faces: List -> + //上面依据识别成功,得到了返回数据,我们在这里调用了一个普通方法来使用识别出来的数据 + BitmapProcessor.onSuccess(faces, page.graphicOverlayFinder) } - } + } } +} ``` ## 面部识别,特征点计算 @@ -648,9 +704,11 @@ class BaseImageAnalyzer : ImageAnalysis.Analyzer { > 来源 > [文章链接](https://medium.com/@estebanuri/real-time-face-recognition-with-android-tensorflow-lite-14e9c6cc53a5) + * TestFileDecActivity * 若需要连接到相机分析流,请看上面章节 * 加载tensorflow lite模型,运行检测,请看`FaceDetection.kt`文件 + ``` //使用TensorFlow Lite 模型的处理器 private val model = FaceDetection.create( @@ -760,17 +818,19 @@ https://discuss.tensorflow.org/t/tensorflow-lite-aar-2-9-0-android-integration/1 详情参考https://tensorflow.google.cn/lite/inference_with_metadata/overview?hl=zh-cn ## 工具类 DataSize,使用时数字加上".单位" + [来源](https://github.com/forJrking/KotlinSkillsUpgrade/blob/main/kup/src/main/java/com/example/kup/DataSize.kt) 简化单位换算 例如: + ```kotlin //原来的方式 val tenMegabytes = 10 * 1024 * 1024//10mb //简化后,会自动换算为bytes -val tenMegabytes =10.mb +val tenMegabytes = 10.mb //其他例子: - 100.mb - 100.kb - 100.gb - 100.bytes +100.mb +100.kb +100.gb +100.bytes ``` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 76e82e2..c3b8db5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -19,7 +19,7 @@ android { //设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so) // abiFilters += listOf("armeabi", "armeabi-v7a", "arm64-v8a") //noinspection ChromeOsAbiSupport - abiFilters += listOf("armeabi-v7a") + abiFilters += rootProject.ext["abi"] as List } } @@ -58,7 +58,7 @@ dependencies { implementation ("com.google.android.material:material:1.9.0") implementation ("androidx.constraintlayout:constraintlayout:2.1.4") implementation(project(":camerax_lib")) - implementation(project(":camerax_analyzer")) + implementation(project(":camerax_analyzer_mlkit")) implementation(project(":camerax_analyzer_tensorflow")) implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") diff --git a/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity.kt b/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity.kt index ce1a4e2..9f7c91c 100644 --- a/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity.kt +++ b/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity.kt @@ -4,7 +4,7 @@ import android.content.Intent import android.util.Log import android.util.Size import com.google.mlkit.vision.face.Face -import com.kiylx.camera.camerax_analyzer.face.FaceContourDetectionProcessor +import com.kiylx.camera.camerax_analyzer_mlkit.face.FaceContourDetectionProcessor import com.kiylx.camerax_lib.R import com.kiylx.camerax_lib.main.manager.CameraHolder import com.kiylx.camerax_lib.main.manager.analyer.base.AnalyzeResultListener @@ -17,6 +17,7 @@ import com.kiylx.camerax_lib.main.store.ImageCaptureConfig import com.kiylx.camerax_lib.main.store.SaveFileData import com.kiylx.camerax_lib.main.store.VideoRecordConfig import com.kiylx.camerax_lib.main.ui.BaseCameraXActivity +import com.kiylx.camerax_lib.view.ControllerPanelUseCaseMode import com.kiylx.cameraxexample.graphic2.BitmapProcessor import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow @@ -24,7 +25,9 @@ import kotlinx.coroutines.flow.flow class CameraExampleActivity : BaseCameraXActivity() { - /** 这里直接构建了配置,我没有使用intent传入配置。 */ + /** + * 这里直接构建了配置,我没有使用intent传入配置。 + */ override fun configAll(intent: Intent): ManagerConfig { val useImageDetection = intent.getBooleanExtra(ImageDetection, false) //视频录制配置(可选) @@ -40,7 +43,7 @@ class CameraExampleActivity : BaseCameraXActivity() { return ManagerConfig().apply { this.recordConfig = videoRecordConfig //这里指定了用例组合,当然你也可以调用UseCaseMode.customGroup方法自定义用例组合 - this.useCaseBundle = + this.useCaseMode = if (useImageDetection) UseCaseMode.imageAnalysis else UseCaseMode.takePhoto this.flashMode = FlashModel.CAMERA_FLASH_AUTO //android R以下时,在少数display为null的情况下,设置预览,拍照的默认分辨率 @@ -57,6 +60,7 @@ class CameraExampleActivity : BaseCameraXActivity() { }, 500) } } + lateinit var analyzer: FaceContourDetectionProcessor override fun cameraHolderInitStart(cameraHolder: CameraHolder) { super.cameraHolderInitStart(cameraHolder) @@ -105,19 +109,29 @@ class CameraExampleActivity : BaseCameraXActivity() { override fun cameraHolderInitFinish(cameraHolder: CameraHolder) { super.cameraHolderInitFinish(cameraHolder) if (cameraConfig.isUsingImageAnalyzer()) {//使用了图像分析 + if (cameraConfig.isUsingVideoRecorder()) { + controllerPanel.switchBetweenCaptureAndRecord(ControllerPanelUseCaseMode.recordVideo) + } else { + if (!cameraConfig.isUsingImageCapture()) { + controllerPanel.showHideControllerButton(true) + } + } controllerPanel.showHideUseCaseSwitch(true) - controllerPanel.showHideControllerButton(true) } } - /** 拍完照片 */ + /** + * 拍完照片 + */ override fun onPhotoTaken(saveFileData: SaveFileData?) { super.onPhotoTaken(saveFileData) Log.d("CameraXFragment", "onPhotoTaken: $saveFileData") cameraXF.indicateTakePhoto()//拍照闪光 } - /** 录完视频 */ + /** + * 录完视频 + */ override fun onVideoRecorded(saveFileData: SaveFileData?) { super.onVideoRecorded(saveFileData) saveFileData?.let { diff --git a/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity2.kt b/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity2.kt index b33dd50..bb4a5b8 100644 --- a/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity2.kt +++ b/app/src/main/java/com/kiylx/cameraxexample/CameraExampleActivity2.kt @@ -50,7 +50,7 @@ class CameraExampleActivity2 : AppCompatActivity(), CameraXFragmentEventListener //整体的配置 return ManagerConfig().apply { this.recordConfig = videoRecordConfig - this.useCaseBundle = + this.useCaseMode = if (useImageDetection) UseCaseMode.imageAnalysis else UseCaseMode.takePhoto this.flashMode = FlashModel.CAMERA_FLASH_AUTO this.size = Size(1920, 1080)//拍照,预览的分辨率,期望值,不一定会用这个值 diff --git a/app/src/main/java/com/kiylx/cameraxexample/TestFileDecActivity.kt b/app/src/main/java/com/kiylx/cameraxexample/TestFileDecActivity.kt index 43164a6..77ae36c 100644 --- a/app/src/main/java/com/kiylx/cameraxexample/TestFileDecActivity.kt +++ b/app/src/main/java/com/kiylx/cameraxexample/TestFileDecActivity.kt @@ -3,9 +3,10 @@ package com.kiylx.cameraxexample import android.os.Bundle import android.widget.Button import android.widget.ImageView +import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.core.view.updatePadding -import com.kiylx.camera.camerax_analyzer.filevision.MyFileProcessor +import com.kiylx.camera.camerax_analyzer_mlkit.filevision.MyFileProcessor import com.kiylx.camera.camerax_analyzer_tensorflow.facedetection.FaceDetection import com.kiylx.camerax_lib.main.manager.util.setWindowEdgeToEdge import com.kiylx.camerax_lib.main.manager.util.statusBarTheme @@ -14,21 +15,21 @@ import com.kiylx.store_lib.StoreX class TestFileDecActivity : AppCompatActivity() { private lateinit var model: FaceDetection - private lateinit var page :ActivityTestFileDecBinding + private lateinit var page: ActivityTestFileDecBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - page= ActivityTestFileDecBinding.inflate(layoutInflater) + page = ActivityTestFileDecBinding.inflate(layoutInflater) setContentView(page.root) - setWindowEdgeToEdge{ - page.root.updatePadding(top=it.top,bottom=it.bottom) + setWindowEdgeToEdge { + page.root.updatePadding(top = it.top, bottom = it.bottom) } statusBarTheme(true) findViewById