添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

年初写了一篇CameraX的使用文章,帮到了一些朋友,也收到了一些建议。正值最近了解到华为ScanKit在扫码场景下的优秀表现,决定集成该方案,并进行一些功能改进。

之前做的Demo略显简陋,本次改进也对UI进行了调整。主要是给顶部操作栏添加了半透明背景,同时给切换按钮添加了半透明边框以提高对比度。另外对拍摄和录制场景的一些配色做了改动。

1. 华为ScanKit是什么

ScanKit可以提供便捷的二维码与条形码扫描、解析、生成能力,帮助您快速构建应用内的扫码功能。

它拥有诸多优势,包括支持多达13种码格式,在反光、污损、畸变、模糊等复杂场景下亦能良好识别,在远距离扫码的情况下能自适应放大码体,还支持多码识别功能等等。

ScanKit给开发者提供了四种集成模式,包括固定扫码界面的 Default View Mode ,自定义扫码界面的 Customized View Mode ,以及完全由开发者自定义画面和扫码流程的 Bitmap Mode MultiProcessor Mode

前两种模式的扫码流程均由ScanKit控制,其内部采用 Camera1 实现。如果要集成到 CameraX 上的话,只能选择后两种模式。 MultiProcessor Mode 适用于多码识别的场景,本次先集成单码识别的 Bitmap Mode

华为ScanKit更加详细的资料可查阅官网:
https://developer.huawei.com/consumer/cn/hms/huawei-scankit

以及易冬大神的完整演示:

https://juejin.cn/post/6967890062423883783

2. 扫码方案的选择

之前的扫码方案采用的是 Zxing ,本次集成 ScanKit 之后,为了对比学习将 Zxing 的使用也进行了保留。在点击扫码按钮之后,底部会弹出扫码方案的选择Fragment,选择之后通过ViewModel将对应的方案告知CameraX的ImageAnalysis。

※ Google的ML Kit是一个更为强大的OCR解决方案,后面也将集成进来

大家可能比较关心ScanKit相较于Zxing的优势,可以参考如下这篇测评文章:
https://developer.huawei.com/consumer/cn/forum/topic/0201248342859390343?fid=18

这篇文章里提到ScanKit在远距离扫码、码体倾斜、模糊扫码等场景下的识别速度和成功率都要优于Zxing。大家也可以使用本文的Demo,分别选择Zxing和ScanKit两个方案,实际对比一下扫码体验。

3. 集成ScanKit

在project的gradle文件里添加ScanKit的仓库地址,app的gradle文件里添加依赖,即可快速集成。※Demo依赖了识别能力更为出色的scanplus依赖包

// build.gradle
buildscript {
    repositories {
        mavenCentral()
        maven {url 'https://developer.huawei.com/repo/'}
allprojects {
    repositories {
        mavenCentral()
        maven {url 'https://developer.huawei.com/repo/'}
// app/build.gradle
dependencies {
    // Huawei scan kit
    implementation 'com.huawei.hms:scanplus:1.3.2.300'

3.1 ImageProxy转换Bitmap

CameraX图像分析ImageAnalysis回传的图像实例ImageProxy是YUV格式的,需要先通过YuvImage将其转换为Bitmap,之后再调用ScanKit的Bitmap扫码模式。

private fun proxyToBitmap(image: ImageProxy): Bitmap {
    val planes: Array<ImageProxy.PlaneProxy> = image.planes
    val yBuffer: ByteBuffer = planes[0].buffer
    val uBuffer: ByteBuffer = planes[1].buffer
    val vBuffer: ByteBuffer = planes[2].buffer
    val ySize: Int = yBuffer.remaining()
    val uSize: Int = uBuffer.remaining()
    val vSize: Int = vBuffer.remaining()
    val nv21 = ByteArray(ySize + uSize + vSize)
    yBuffer.get(nv21, 0, ySize)
    vBuffer.get(nv21, ySize, vSize)
    uBuffer.get(nv21, ySize + vSize, uSize)
    val yuvImage = YuvImage(nv21, ImageFormat.NV21, image.width, image.height, null)
    val out = ByteArrayOutputStream()
    yuvImage.compressToJpeg(Rect(0, 0, yuvImage.width, yuvImage.height), 75, out)
    val imageBytes = out.toByteArray()
    val opt = BitmapFactory.Options()
    opt.inPreferredConfig = Bitmap.Config.ARGB_8888
    var bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, opt)
    return bitmap

3.2 调用Bitmap扫码模式

创建下ScanKit专用的扫码参数,并将转换得到的Bitmap实例传递给ScanUtil,即可开始识别。返回的识别结果包括内容、坐标、四角位置等信息,被封装到HmsScan对象里。ScanUtil识别完成后实际返回的是HmsScan数组,其第一个元素即为单码的识别结果。HmsScan对象的originalValue属性则是解析出来的内容。

class HuaweiScanAnalysis: RealTimeAnalysis {
    override fun analyzeContent(imageProxy: ImageProxy, context: Context): AnalysisResult {
        val bitmap = proxyToBitmap(imageProxy)
        imageProxy.close()
        // 创建ScanKit扫码的参数
        val options = HmsScanAnalyzerOptions.Creator()
            .setHmsScanTypes(HmsScan.ALL_SCAN_TYPE)
            .setPhotoMode(false)
            .create()
        // 得到扫码结果
        val result = ScanUtil.decodeWithBitmap(
            context,
            bitmap,
            options
        val content = if (result != null && result.isNotEmpty() && result[0].originalValue != null)
            result[0].originalValue else ""
        // 将扫码结果封装为我们自定义的实例
        return AnalysisResult(content, scale, rect)

3.3 远距离扫码的自动放大

当远距离扫码或码体过小,ScanKit会计算得到适合的放大倍率,并赋值到HmsScan对象的zoomValue属性里。可以利用该数值及时通知CameraX调整图像采集的倍率,进而提升后续的识别率。实现思路非常简单,使用CameraControl提供的setZoomRatio放大图像预览和分析的倍率即可。为不影响下次的扫码体验,在扫码完成后须将倍率置。

class MyAnalyzer(...): Analyzer {
    override fun analyze(image: ImageProxy) {
        viewModel.analysePicture(image).also {
            if (Constants.DEFAULT_ZOOM_SCALE != it.zoomScale
                && Constants.MIN_ZOOM_SCALE != it.zoomScale
                callback.onZoomPreview(it.zoomScale)
            } else {
                callback.onAnalyzeResult(it)
fun onAnalyzeGo(view: View?) {
    if (mAnalyzer == null) {
        mAnalyzer = MyAnalyzer(viewModel, object : AnalyzeCallback {
            override fun onZoomPreview(scale: Double) {
                mCamera.cameraControl.setZoomRatio(scale.toFloat())

3.4 成功提示音和震动

为提高用户体验,可以在扫码成功的同时播放预设的提示音或震动反馈,可以利用开源的BeepManager工具类来实现。

fun onAnalyzeGo(view: View?) {
    if (mAnalyzer == null) {
        mAnalyzer = MyAnalyzer(viewModel, object : AnalyzeCallback {
            override fun onAnalyzeResult(result: AnalysisResult) {
                synchronized(isAnalyzing) {
                    showQRCodeResult(result.content)
private fun showQRCodeResult(result: String) {
    stopAnalysis()
    beepManager.playBeepSoundAndVibrate()

3.5 绘制码体指示位置

扫码成功的瞬间,微信和支付宝App会在二维码上展示一个圆点,这样的提示设计比较好。HmsScan类的borderRect属性代表码体的矩形框位置,通过计算得到的centerX和centerY可以帮忙获取码体的中心,在该位置可以展示一个指示View。

需要留意的是,竖屏模式下Analyse的图片会有90度的偏差,所以需要额外转换下位置坐标。当然如果Bitmap实例已经做过了90度旋转的处理的话,borderRect数值就不需要额外转换了。有些遗憾的是,坐标计算会有些误差,很难保证每次都将指示位置绘制在中心。

override fun onAnalyzeResult(result: AnalysisResult) {
    synchronized(isAnalyzing) {
        showQRCodeResult(result.content)
        val centerPoint = Utils.convertRectToPoint(result.rect, binding.previewView)
        showPointView(centerPoint)
private fun showPointView(point: Point) {
    val popupWindow = PopupWindow(
        ViewGroup.LayoutParams.WRAP_CONTENT,
        ViewGroup.LayoutParams.WRAP_CONTENT
    val imageView = ImageView(this)
    runOnUiThread {
        popupWindow.contentView = imageView
        imageView.setImageResource(R.drawable.ic_point_view)
        popupWindow.showAsDropDown(binding.previewView, point.x, point.y)
        binding.previewView.postDelayed({ popupWindow.dismiss() }, 1000)

3.6 绘制码体边框

在识别过程或成功的时候也可以展示二维码的边框辅助提示。尽管这样的设计并不十分必要,我们可以试着实现看看。

borderRect属性的原始数值就是框体的宽高,再依据上面的中心位置就可以在上面绘制一个矩形框。事实上除了borderRect,cornerPoints属性可以拿到码体四角的确切位置,也可以作为绘制框体的数据来源。

override fun onAnalyzeResult(result: AnalysisResult) {
    synchronized(isAnalyzing) {
        showQRCodeResult(result.content)
        val centerPoint = Utils.convertRectToPoint(result.rect, binding.previewView)
        showRectView(centerPoint, result.rect)
private fun showRectView(point: Point, rect: Rect) {
    val popupWindow = PopupWindow(
        rect.height(),
        rect.width()
    val imageView = ImageView(this)
    runOnUiThread {
        popupWindow.contentView = imageView
        imageView.setImageResource(R.drawable.ic_rect_view)
        imageView.scaleType = ImageView.ScaleType.FIT_XY
        try {
            popupWindow.showAsDropDown(binding.previewView,
            point.x - (rect.width() / 2), point.y)
        } catch (e: Exception) {}
        binding.previewView.postDelayed({ popupWindow.dismiss() }, 1000)

※ 不知道拍摄角度的问题还是ScanKit的识别存在误差,框体的绘制位置总有些偏差,官方Demo绘制的框体位置也不准确

4. 必要的手势支持

之前的Demo主要集中在CameraX的API使用上,忽略了支持必要的手势,本次一并加入常用的手势支持。

4.1 双击手势缩放

CameraControl提供的setLinearZoom() API可以将拍摄的视野线性地缩放,比较适合双击或者滑动缩放视图的场景。它接受的参数数值介于0~1之间,具体如下:

  • 0为最小缩放比例,即原始尺寸
  • 1为缩放至最大比例

通过监听双击手势,让拍摄的画面在原始比例0f和0.5F中间比例之间切换。

private fun listenGesture() {
    binding.previewView.setOnTouchListener { view, event ->
        // Zoom when double click.
        doubleClickZoom(event)
private fun doubleClickZoom(event: MotionEvent) {
    if (doubleClickDetector == null) {
        doubleClickDetector = GestureDetector(this@NewCameraXActivity,
            object : GestureDetector.SimpleOnGestureListener() {
                override fun onDoubleTap(e: MotionEvent?): Boolean {
                    cameraZoomState.value?.let {
                        val zoomRatio = it.zoomRatio
                        val minRatio = it.minZoomRatio
                        // Ratio parameter from 0f to 1f.
                        if (zoomRatio > minRatio) {
                            mCamera.cameraControl.setLinearZoom(Constants.MIN_ZOOM_SCALE.toFloat())
                        } else {
                            mCamera.cameraControl.setLinearZoom(Constants.MIDDLE_ZOOM_SCALE.toFloat())
                    return true
    doubleClickDetector?.onTouchEvent(event)

4.2 捏合手势缩放

CameraControl提供的setZoomRatio API在线性缩放的基础之上提供了更为准确的缩放比率,可以实现捏合手势的缩放场景。

private fun listenGesture() {
    binding.previewView.setOnTouchListener { view, event ->
        // Listen to zoom gesture.
        scalePreview(event)
private fun scalePreview(event: MotionEvent) {
    if (scaleDetector == null) {
        scaleDetector = ScaleGestureDetector(this@NewCameraXActivity,
            object : SimpleOnScaleGestureListener() {
                override fun onScale(detector: ScaleGestureDetector): Boolean {
                    cameraZoomState.value?.let {
                        val zoomRatio = it.zoomRatio
                        mCamera.cameraControl.setZoomRatio(zoomRatio * detector.scaleFactor)
                    return true
    scaleDetector?.onTouchEvent(event)

4.3 手动对焦的优化

之前是在Touch(ACITON_DOWN)的时候依据坐标进行手动聚焦,引入缩放手势的支持之后,缩放的过程中会误触对焦操作。改善方法在于将对焦的时机限制在SingleTap手势,即只有单击操作才会触发对焦。

private fun listenGesture() {
    binding.previewView.setOnTouchListener { view, event ->
        // Singe tap for focus.
        singleTapForFocus(event)
private fun singleTapForFocus(event: MotionEvent) {
    if (singleTapDetector == null) {
        singleTapDetector = GestureDetector(this@NewCameraXActivity,
            object : GestureDetector.SimpleOnGestureListener() {
                override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
                    focusOnPosition(event.x, event.y, true)
                    return super.onSingleTapConfirmed(e)
    singleTapDetector?.onTouchEvent(event)

5. 持续的代码改进

改进前很多逻辑都堆在了Activity里,现将各个UseCase的实现拆分出去,减轻Activity的负担。同时对CameraX使用的一些问题进行了改进。

5.1 防止反复进入的crash

展示相机预览的控件PreviewView尚未添加到视图Tree的时候,如果执行CameraX的绑定操作的话,会发生问题。现象上表现为拍摄画面结束后再次打开的时候会发生Crash。解决思路很简单:监听PreviewView控件的attach时机,在attach成功的回调里才执行CameraX的绑定操作。

override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(binding.root)
    startCameraWhenAttached()
private fun startCameraWhenAttached() {
    binding.previewView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener{
        override fun onViewAttachedToWindow(v: View?) {
            ensureCameraPermission()
private fun ensureCameraPermission() {
    setupCamera(binding.previewView)

记得在画面不可见的时候结束图像分析的调用,节省内存。

override fun onStop() {
    super.onStop()
    mImageAnalysis?.clearAnalyzer()

5.2 连续点击录屏的crash抑制

快速点击视频录制和停止的情况下偶尔会发生如下的crash。

java.lang.IllegalStateException: Failed to stop the muxer

看了CameraX的源码,录制开始和结束时Audio实例的请求和释放发生了错乱。本此改进加入了录制视频的状态控制,在录制开始的500ms内禁止终止录制,以缓解这种现象。

但在极快的录制和停止的反复操作下,录制的部分文件可能会发生损坏。由于CameraX的视频录制API仍处在实验性阶段,所以耐心等待CameraX的解决吧。

private fun videoRecordingPrepared() {
    isCameraXHandling = false
    // Keep disabled status for a while to avoid fast click error with "Muxer stop failed!".
    binding.capture.postDelayed({ binding.capture.isEnabled = true }, 500)

5.3 拍摄的镜像反转

CameraX拍摄的照片默认是镜像的,在拍摄前告知CameraX做下镜像反转,做到所见即所得。

private fun takenPictureInternal(isExternal: Boolean) {
    // Mirror image
    ImageCapture.Metadata().apply {
        isReversedHorizontal = true
    mImageCapture?.takePicture(outputFileOptions, lightExecutor, MyCaptureCallback(picCount, this))

5.4 选择指定摄像头

很多设备的前后并不止一个镜头,比如疫情期间非常流行的安全码和体温一体化检测设备。所以有时候镜头切换不能是简单地前后切换,而需要按镜头的ID指定切换。

private fun bindPreview(...) {
    // Select specified camera.
    val cameraSelector = CameraSelector.Builder().addCameraFilter(AllCameraFilter()).build()
class AllCameraFilter: CameraFilter {
    override fun filter(cameraInfos: MutableList<CameraInfo>): MutableList<CameraInfo> {
        val result: MutableList<CameraInfo> = mutableListOf()
        for (cameraInfo in cameraInfos) {
            val id = (cameraInfo as CameraInfoInternal).cameraId
            // Specify the camera id that U need, such as front camera which id is 0.
            if (CameraSelector.LENS_FACING_FRONT.equals(id)) {
                result.add(cameraInfo)
        return result

实际上CameraX最新版提供了新API(CameraInfo#getCameraSelector()),可返回某镜头对应的选择器实例。

6. 相关API总结

整理一下CameraX使用的主要API,供大家快速查阅。

管理相机实例的接口或实现作用
CameraController获取和管理相机实例的接口
LifecycleCameraController通过LifecycleOwner实现生命周期管理Camera实例的接口
ProcessCameraProviderLifecycleOwner的实现类,用以单例模式管理Camera实例
访问镜头功能和属性的API作用
Camera提供镜头操作的主要接口
CameraControl用以执行镜头缩放、聚焦等操作的接口,通过Camera接口获取实例
CameraInfo用以获取镜头参数的IF,比如缩放比率、是否有闪光灯等,其实例同样由Camera接口提供
CameraConfig用以获取Camera使用配置信息的接口,也通过Camera接口获取实例
CameraSelector过滤并匹配对应镜头的类,在CameraController执行的时候传入实例以初始化对应的镜头
场景UseCase类的实现类作用
Preview预览场景
ImageAnalysis图像分析
ImageCapture图像拍摄
VideoCapture视频录制
相机效果的扩展类作用
PreviewExtender展示预览扩展效果,实现类有美颜的BeautyPreviewExtender、夜拍的NightPreviewExtender等
ImageCaptureExtender展示拍摄扩展效果,同样有美颜等效果的实现类

以及ScanKit的部分API:

API作用
ScanUtilBitmap扫描码模式、压缩Bitmap等功能支持的工具类
HmsScanAnalyzerOptions指定扫码格式等参数类
HmsScan扫码结果封装类,包括内容、码体坐标、四角位置等信息

华为ScanKit的集成还是非常简单流畅的,在扫码技术选型的时候可以大胆尝试一下。对于识别率或速度担心的朋友可以下载ScanKitZxing的官方Apk进行体验和对比。

Scankit官方Sample下载地址:https://developer.huawei.com/consumer/en/doc/development/HMS-Examples/scan-sample-code4

Zxing官方Sample下载地址:https://play.google.com/store/apps/details?id=com.google.zxing.client.android

希望针对CameraX的详细集成和持续的实用改进,能够帮助到大家。

本文DEMO

https://github.com/ellisonchan/JetpackDemo

华为官方文档

完美替代ZXing,统一扫码服务

Android CameraX使用入门

从Preference组件的更迭看Jetpack的前世今生

Android 12上焕然一新的小组件:美观、便捷和实用

Android 12上全新的应用启动画面,还不适配一下?

年初写了一篇CameraX的使用文章,帮到了一些朋友,也收到了一些建议。正值最近了解到华为ScanKit在扫码场景下的优秀表现,决定集成该方案,并进行一些功能改进。之前做的Demo略显简陋,本次改进也对UI进行了调整。主要是给顶部操作栏添加了半透明背景,同时给切换按钮添加了半透明边框以提高对比度。另外对拍摄和录制场景的一些配色做了改动。1. 华为ScanKit是什么ScanKit可以提供便捷的二维码与条形码扫描、解析、生成能力,帮助您快速构建应用内的扫码功能。它拥有诸多优势,包括支持多达..
华为智能安防解决方案 业务概况及现状挑战 产品与解决方案 1)软件定义摄像机(SDC:Software Define Camera) 2)智能视频云监控 业绩案例 目录 算力 普惠易得,充裕且经济 算法 持续演进,丰富且聪明 数据 多维融合,标准且安全 协同 贯穿端云,全局且高效 开放 黑土地,应用百花齐放 未来:从洞见到预见 现在:事后研判为主 算力稀缺且昂贵 算法固化,场景约束多 数据孤岛,融合共享难 碎片化建设,缺乏整体协同 烟囱化系统,限制应用生态 阶段1:看见 阶段2:洞见 1990s 2010s 1970s 安防智能化程度 AI流行度 AI实战能力不足,场景受限 AI难以工程化(算力不足) 未来:预见 AI驱动,视频产业进入智能时代 轨道交通视频监控面临的挑战 设备多,资源共享难、协同能力差 不可靠,复杂环境下系统不稳定 线路联网难,视频共享低 如何让监控系统在复杂的环境中更加健壮? 车站机房小,全线设备多,难运维 多线路联网困难、视频共享低、管理复杂? 智能化程度低,有效信息检索难 智能摄像机数量少,结构化数据少 业务概况及现状挑战 产品与解决方案 1)智能视频云监控 2)软件定义摄像机 业绩案例 目录 8 传统视频监控与华为云监控架构对比 当 前 云监控 管理服务器 数据服务器 存储扩展柜 系统管理认证 数据管理 视频存储 视频流控 IP SAN控制器 转发服务器 "烟囱式建设、 系统复杂" 硬件单点故障影响无法避免 安全管理 调看存录 智能应用 资源管理平台 计算 资源池 存储 资源池 端口 资源池 视频监控系统业务 Cloud OS: 容器引擎&编排管理 超融合视频云节点:超融合、高可靠、高性能、可云化 视频云节点 视频管理服务器 流媒体转发服务器 数据管理服务器 录像点播服务器 设备接入服务器 磁盘阵列 备份服务器 超融合带来:方案成本降低10%、系统能耗降低 45%、机房空间节约25%、维护成本节省45%; 超融合、RAID失效录象不丢失技术、集群技术 保证视频数据完整性更高; 超融合/直存带来:单设备吞吐能力高达 3072Mbps(可并发支持:512路存+512路看 +512路点播或下载); 采用ONVIF协议,采用裸协议接入1800款前端; 采用SIP/SDK与第三方平台互通. 完全替代DVR,IPSAN,并大大提高性能,分布式组网,,实现 云平台,云存储 稳定可靠 高性能 低TCO 开放 平台+存储设备 视频监控云 云节点 云节点 云节点 云节点 华为云监控与流媒体服务器架构对比 服务器+IPSAN架构 视频云架构 存储系统 客户端 IP network 电视墙 解码器 视频 服务器 转发 服务器 存储 服务器 效率低 成本高 难维护 1 2 3 4 不安全 花费大量资源,进行媒体封装和解封装 非视频监控专用,大量功能废弃,使用效率低下 大量服务器及存储设备部署成本高 设备数量多,机房能耗大幅度上升 初始配置,花费大量部署时间 设备种类多,维护复杂 故障点多,故障复杂 系统安全性和建设成本的平衡点很难控制 问题和挑战 视频云 客户端 IP network 电视墙 解码器 避免无效封装、转发 时延和系统消耗降低,业务效率明显提升 方案与亮点 超融合,支持堆叠组网,降低建设成本 系统简单易管理,降低运行维护成本 集成视频监控基础管理软件,开放接口易集成 内嵌图像质量诊断软件,图像故障易发现 系统集成化程度高,安全风险有效降低 专用媒体优化技术有效保证视频数据与业务安全 视频监控云解决方案-此云非彼云 真正的视频监控云需要同时实现监控业务、计算、存储资源 端到端资源池化调度,系统无单点故障。 真正的视频监控云可以保障业务动态迁移、分布式并行运算 机制,效率提升与设备数量成正比。 视频云 LAN 球机 枪机 模拟摄像机 DVS 无线终端 基于传统视频服服务器+云存储的架构,在IaaS层进行集 群处理,混合业务、并发访问的业务需求服务; 传统监控软件的无法实现监控业务虚拟化,单点故障始 终无法避免。 云存储 监 控 平 台 软 件 球机 枪机 模拟摄像机 DVS 无线终端 视频服务器/元数据服务器 IAAS架构视频云 监控云 PAAS架构视频云=监控云 LAN 轨道交通CCTV系统主流架构对比—云架构简单,无单点故障 实况流 存储流 流媒体转 发服务器 磁盘阵列 客户端 视频服务器 IP媒体流架构 解码器 客户端 视频 服务器 流媒体转 发服务器 IP摄像机 编码器 各车站/车辆段 线路中心 IP SAN 客户端 视频/数据 管理服务器 直存架构 解码器 客户端 视频/数据 管理服务器 IP摄像机 编码器 各车站/车辆段 交换机(组播) 交换机(单播) 客户端 云监控架构 解码器 客户端 IP摄像机 编码器
Android 开发的历史中,Camera 的 API 是一直受人诟病的,直观的感觉就是配置复杂、臃肿、难用、不易理解,官方也在尝试着不断改进开发者关于Camera的使用体验,Camera 的 API 截止目前经历了 Camera(已废弃)、Camera2、CameraX 三个版本。 初代 Camera API从 5.0 开始已经宣布废弃,而 Camera2 的 API 特别难用,所以就有了 CameraX ,本文主要探索如何在 Jetpack Compose 中使用 CameraX。
超简单集成华为HMS Scankit扫码服务实现扫一扫二维码前言官网的Demo和Sample代码体验1 开发前准备1.1 android studio 安装1.2 在项目级gradle里添加华为maven仓1.3 在应用级的build.gradle里面加上SDK依赖1.4 加一下混淆配置1.5 Manifest申请静态权限和扫码页面声明:2 代码开发2.1 动态申请权限2.2 在权限申请回调中启动扫码界面2.3 在activity回调里面获取码结果结后语附官方开发指南 最近要做一个停车场扫码收费的app,在网上搜了一圈,首先接触到了ZXing,上手试了下,集成过程不复杂,但是感觉效果欠佳
最近要做一个停车场扫码收费的app,在网上搜了一圈,首先接触到了ZXing,上手试了下,集成过程不复杂,但是感觉效果欠佳,比如距离稍微远点儿就扫不出来了,另外角度对的不好,反光或者光线比较暗的时候,成功率也比较低,集成好给我们老大看了下,感觉不是很满意。最近偶尔看到了华为一个发布会里面有介绍扫码功能,稍微研究了下,居然是一款免费扫码神器,官方称之为Scan Kit,号称还同时支持Android和iOS,半信半疑上手搞了一把发现效果还真不错!测了下发现对于一些有反光,污损,形变,超远距离的场景都能很好地识别! 今天简单总结了下,分享给大家,码字不易,也算是疫情期间,给大家的一点小小福利。 华为统一扫码服务(Scan Kit)提供便捷的条形码和二维码扫描、解析、生成能力,帮助开发者快速构建应用内的扫码功能。 得益于华为在计算机视觉领域能力的积累,Scan Kit 可以实现远距离码或小型码的检测和自动放大,同时针对常见复杂扫码场景(如反光、暗光、污损、模糊、柱面)做了针对性识别优化,提升扫码成功率与用户体验。 Scan Kit 支持 Android 和 iOS 系统集成。其中,Android 系统集成 Scan Kit 后支持横屏扫码能力。 支持的设备     Zxing 是常用的第三方开源 SDK。但是,Zxing存在以下缺陷:仅实现扫描二维码的基本操作,不支持强光、弯曲、变形等更复杂的扫描环境。目前主流做法是基于Zxing对源码进行优化。但是,优化效果仍然不理想,很多人会在优化上花费大量时间。     华为Scan Kit服务提供便捷的条码和二维码扫描、解析、生成能力,帮助开发者快速构建应用中的二维码扫描功能。得益于华为在计算机视觉领域的长期积累,华为统一条码扫描服务(Scan Kit)可检测并自动放大远距离或小尺寸条码,优化识别常见复
在之前的博客中我介绍了Camera在单拍和连拍上的使用。大家应该有所了解了。但如果你运行了博客中的例子,或者自己根据讲解写了一个Demo,你会发现拍出的照片并不是很清晰,很大的一个原因是我们的照片支持的最大分辨率是1920*1080,所以对于现在动辄2000往上的分辨率来说却是不是很清晰。所以Android 5.0之后Camera2也应用而生。而和Camera2配合的显示控件也变成了TextureView。下面我们分别对这两个内容一一进行讲解。并在最后给出一个利用Camera2制作的自定义相机示例。 示例代码显示了如何使用HMS扫描套件的代码扫描功能来帮助开发人员在应用程序中快速构建代码扫描功能。 创建一个应用程序并在AppGallery Connect中配置该应用程序信息。 查看详细信息: 添加您的App的AppGallery Connect配置文件 为HMS Core SDK配置Maven存储库地址 受支持的环境 使用Android Studio和JDK1.8进行编译准备运行EMUI 3.0或更高版本,适应HMS APK 4.0.0.200或更高版本的设备 扫描工具包演示提供了四种模式,以适应不同情况下的代码扫描能力。 该应用程序直接调用HUAWEI Scan Kit扫描活动,并通过异步回调API获取扫描结果。 条形码可以使用设备相机或通过导入的图像进行扫描。 代码位置: package com.example.apidemo.util; * # ...
Scan Kit可以实现远距离码或小型码的检测和自动放大,同时针对常见复杂扫码场景(如反光、暗光、污损、模糊、柱面)做了针对性识别优化,提升扫码成功率与用户体验。扫码体验比 ZXing 更好,并且可以免费使用。 支持的码制式 Scan Kit支持扫描13种全球主流的码制式。如果您的应用只处理部分特定的码制式,您也可以在
很高兴为您选择国产MCU芯片。市面上有许多国产MCU芯片,以下是几款比较受欢迎的: 1. STM32系列:STM32是STMicroelectronics公司生产的MCU,性能优秀,支持多种开发环境,是驱动摄像头的首选。 2. ESP32系列:ESP32是Espressif公司生产的MCU,拥有Wi-Fi和蓝牙功能,性能优秀,是IoT领域的首选。 3. Allwinner V3/V5:Allwinner公司生产的MCU,性价比较高,支持高清视频编解码,是驱动摄像头的不错选择。 4. Huawei Hi3518E: 华为公司生产的MCU,支持高清视频编码,是驱动摄像头的良好选择。 希望以上信息能帮助您,如果需要更详细的信息,请告诉我您的具体需求。