前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android CameraX结合LibYUV和GPUImage自定义相机滤镜

Android CameraX结合LibYUV和GPUImage自定义相机滤镜

原创
作者头像
用户9239674
发布于 2021-12-02 02:36:38
发布于 2021-12-02 02:36:38
1.3K0
举报

作者:itfitness 链接:https://www.jianshu.com/p/f084082cc0c6

本文目录:

前言

之前使用Camera实现了一个自定义相机滤镜(Android自定义相机滤镜 ),但是运行起来有点卡顿,这次用Camerax来实现一样的效果发现很流畅,在此记录一下,也希望能帮到有需要的同学。

实现效果

实现步骤

1.引入依赖库

这里我引入的依赖库有CameraXGPUImage(滤镜库)、Utilcodex(一款好用的工具类)

代码语言:txt
AI代码解释
复制
// CameraX core library using camera2 implementation
    implementation "androidx.camera:camera-camera2:1.0.1"
// CameraX Lifecycle Library
    implementation "androidx.camera:camera-lifecycle:1.0.1"
// CameraX View class
    implementation "androidx.camera:camera-view:1.0.0-alpha27"

    implementation'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
    implementation 'com.blankj:utilcodex:1.30.6'
2.引入libyuv

这里我用的是这个案例(https://github.com/theeasiestway/android-yuv-utils)里面的libyuv,如下

3.编写CameraX预览代码

布局代码如下

代码语言:txt
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <androidx.camera.view.PreviewView
        android:id="@+id/viewFinder"
        android:layout_width="0dp"
        android:layout_height="0dp" />
</FrameLayout>

Activity中开启相机预览代码如下,基本都是Google官方提供的案例代码

代码语言:txt
AI代码解释
复制
class MainActivity : AppCompatActivity() {
    private lateinit var cameraExecutor: ExecutorService
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        cameraExecutor = Executors.newSingleThreadExecutor()
        // Request camera permissions
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }
    }

    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            baseContext, it) == PackageManager.PERMISSION_GRANTED
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults:
        IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(this,
                    "Permissions not granted by the user.",
                    Toast.LENGTH_SHORT).show()
                finish()
            }
        }
    }
    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(Runnable {
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
            val preview = Preview.Builder()
                .build()
                .also {
                    it.setSurfaceProvider(viewFinder.surfaceProvider)
                }
            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
            try {
                cameraProvider.unbindAll()
                cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview)
            } catch(exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }
        }, ContextCompat.getMainExecutor(this))
    }

    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }

    companion object {
        private const val TAG = "CameraXBasic"
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)

    }
}

到这里就可以实现相机预览了

4.增加相机数据回调

我们要增加滤镜效果就必须对相机的数据进行操作,这里我们通过获取相机数据回调来获取可修改的数据

代码语言:txt
AI代码解释
复制
val imageAnalyzer = ImageAnalysis.Builder()
                //设置回调数据的比例为16:9
                .setTargetAspectRatio(AspectRatio.RATIO_16_9)
                .build()
                .also {
                    it.setAnalyzer(cameraExecutor,this@MainActivity)
                }

这里我们还需要进行绑定

图片
图片

除此之外我们还需要在Activity中实现ImageAnalysis.Analyzer接口,数据的获取就在此接口的回调方法中获取,如下所示,其中ImageProxy就包含了图像数据

代码语言:txt
AI代码解释
复制
override fun analyze(image: ImageProxy) {

}
5.对回调数据进行处理

我们在相机数据回调的方法中对图像进行处理并添加滤镜,当然在此之前我们还需要创建GPUImage对象并设置滤镜类型

代码语言:txt
AI代码解释
复制
private var bitmap:Bitmap? = null
private var gpuImage:GPUImage? = null
//创建GPUImage对象并设置滤镜类型,这里我使用的是素描滤镜
private fun initFilter() {
        gpuImage = GPUImage(this)
        gpuImage!!.setFilter(GPUImageSketchFilter())
    }
@SuppressLint("UnsafeOptInUsageError")
    override fun analyze(image: ImageProxy) {
        //将Android的YUV数据转为libYuv的数据
        var yuvFrame = yuvUtils.convertToI420(image.image!!)
        //对图像进行旋转(由于回调的相机数据是横着的因此需要旋转90度)
        yuvFrame = yuvUtils.rotate(yuvFrame, 90)
        //根据图像大小创建Bitmap
        bitmap = Bitmap.createBitmap(yuvFrame.width, yuvFrame.height, Bitmap.Config.ARGB_8888)
        //将图像转为Argb格式的并填充到Bitmap上
        yuvUtils.yuv420ToArgb(yuvFrame,bitmap!!)
        //利用GpuImage给图像添加滤镜
        bitmap = gpuImage!!.getBitmapWithFilterApplied(bitmap)
        //由于这不是UI线程因此需要在UI线程更新UI
        img.post {
            img.setImageBitmap(bitmap)
            //关闭ImageProxy,才会回调下一次的数据
            image.close()
        }

    }
6.拍摄照片

这里我们加一个拍照的按钮

代码语言:txt
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <androidx.camera.view.PreviewView
        android:id="@+id/viewFinder"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <ImageView
        android:id="@+id/img"
        android:scaleType="centerCrop"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <Button
        android:id="@+id/bt_takepicture"
        android:layout_gravity="center_horizontal|bottom"
        android:layout_marginBottom="100dp"
        android:text="拍照"
        android:layout_width="70dp"
        android:layout_height="70dp"/>
</FrameLayout>

然后我们在Activity中添加拍照的逻辑,其实就是将Bitmap转为图片保存到SD卡,这里我们使用了之前引入的Utilcodex工具,当我们点击按钮的时候isTakePhoto 会变为true,然后在相机的回调中就会进行保存图片的处理

代码语言:txt
AI代码解释
复制
bt_takepicture.setOnClickListener {
            isTakePhoto = true
        }

并且我们加入变量控制,在拍照的时候不处理回调数据

代码语言:txt
AI代码解释
复制
@SuppressLint("UnsafeOptInUsageError")
    override fun analyze(image: ImageProxy) {
        if(!isTakePhoto){
            //将Android的YUV数据转为libYuv的数据
            var yuvFrame = yuvUtils.convertToI420(image.image!!)
            //对图像进行旋转(由于回调的相机数据是横着的因此需要旋转90度)
            yuvFrame = yuvUtils.rotate(yuvFrame, 90)
            //根据图像大小创建Bitmap
            bitmap = Bitmap.createBitmap(yuvFrame.width, yuvFrame.height, Bitmap.Config.ARGB_8888)
            //将图像转为Argb格式的并填充到Bitmap上
            yuvUtils.yuv420ToArgb(yuvFrame,bitmap!!)
            //利用GpuImage给图像添加滤镜
            bitmap = gpuImage!!.getBitmapWithFilterApplied(bitmap)
            //由于这不是UI线程因此需要在UI线程更新UI
            img.post {
                img.setImageBitmap(bitmap)
                if(isTakePhoto){
                    takePhoto()
                }
                //关闭ImageProxy,才会回调下一次的数据
                image.close()
            }
        }else{
            image.close()
        }
    }
 /**
     * 拍照
     */
    private fun takePhoto() {
        Thread{
            val filePath = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),"${System.currentTimeMillis()}save.png")
            ImageUtils.save(bitmap,filePath.absolutePath,Bitmap.CompressFormat.PNG)
            ToastUtils.showShort("拍摄成功")
            isTakePhoto = false
        }.start()
    }

效果如下

保存的图片在如下目录

保存的图片如下

只有不断的学习进步,才能不被时代淘汰。关注我,每天分享知识干货!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
Word2Vec——使用GloVe训练中文词向量
准备好中文语料:中文语料库,或者使用Leavingseason准备好的语料进行训练:http://pan.baidu.com/s/1jHZCvvo
量子态的沐子呓
2020/02/12
4.2K0
【NLP】文本分类任务之逻辑回归
在某些平台评论中会经常出现一些有毒评论(即一些粗鲁,不尊重或者可能让某人离开讨论的评论),这使得许多人不愿意再表达自己并放弃在平台中评论。因此,为了促进用户对话,提出一系列的方案,来缓解这一问题。我们将其看作一个文本分类问题,来介绍一系列的文本分类方案。
yuquanle
2020/02/14
1.1K0
文本分类指南:你真的要错过 Python 吗?
文本分类作为自然语言处理任务之一,被广泛应用于解决各种商业领域的问题。文本分类的目的是将 文本/文档 自动地归类为一种或多种预定义的类别。常见的文本分类应用如下:
AI研习社
2018/07/26
2.5K0
文本分类指南:你真的要错过 Python 吗?
用机器学习方法对影评与观影者情感判定
朴素贝叶斯常见的应用场景之一是情感分析。又上Kaggle溜达了一圈,扒下来一个类似场景的比赛。比赛的名字叫做当词袋/Bag of words 遇上 爆米花/Bags of Popcorn,地址为https://www.kaggle.com/c/word2vec-nlp-tutorial/,有兴趣的同学可以上去瞄一眼。 8.1 背景介绍 这个比赛的背景大概是:国外有一个类似豆瓣电影一样的IMDB,http://www.imdb.com/ 也是你看完电影,可以上去打个分,吐个槽的地方。然后大家就在想,有这么多
机器学习AI算法工程
2018/03/14
1.3K0
用机器学习方法对影评与观影者情感判定
(二)中文文本分类--机器学习算法原理与编程实践 - 简书
本章知识点:中文分词,向量空间模型,TF-IDF方法,文本分类算法和评价指标 使用的算法:朴素的贝叶斯算法,KNN最近邻算法 python库:jieba分词,Scikit-Learning 本章目标:实现小型的文本分类系统 本章主要讲解文本分类的整体流程和相关算法
会呼吸的Coder
2020/02/17
1.5K0
手把手教你在Python中实现文本分类(附代码、数据集)
文本分类是商业问题中常见的自然语言处理任务,目标是自动将文本文件分到一个或多个已定义好的类别中。文本分类的一些例子如下:
数据派THU
2018/07/30
12.8K2
手把手教你在Python中实现文本分类(附代码、数据集)
python 中文文本分类[通俗易懂]
即已经分好类的文本资料(例如:语料库里是一系列txt文章,这些文章按照主题归入到不同分类的目录中,如 .\art\21.txt) 推荐语料库:复旦中文文本分类语料库,下载链接:http://download.csdn.net/detail/github_36326955/9747927
全栈程序员站长
2022/07/01
1.3K0
python 中文文本分类[通俗易懂]
Keras文本分类实战(下)
在上一节Keras文本分类实战(上),讲述了关于NLP的基本知识。这部分,将学会以不同方式将单词表示为向量。
用户3578099
2019/08/15
1.3K0
sklearn+gensim︱jieba分词、词袋doc2bow、TfidfVectorizer
版权声明:博主原创文章,微信公众号:素质云笔记,转载请注明来源“素质云博客”,谢谢合作!! https://blog.csdn.net/sinat_26917383/article/details/71436563
悟乙己
2019/05/26
3.8K0
入门 | CNN也能用于NLP任务,一文简述文本分类任务的7个模型
本文是我之前写过的一篇基于推特数据进行情感分析的文章(https://ahmedbesbes.com/sentiment-analysis-on-twitter-using-word2vec-and-keras.html)的延伸内容。那时我建立了一个简单的模型:基于 keras 训练的两层前馈神经网络。用组成推文的词嵌入的加权平均值作为文档向量来表示输入推文。
机器之心
2018/07/26
1.8K0
入门 | CNN也能用于NLP任务,一文简述文本分类任务的7个模型
使用 NLP 和文本分析进行情感分类
我们今天生活在一个数字世界中。从一天的开始到我们对所爱的人说“晚安”,我们以视觉、音乐/音频、网络、文本和更多来源的形式消耗大量数据。
磐创AI
2021/11/10
1.8K0
XGBoost 实现文本分类与sklearn NLP库TfidfVectorizer
在文本分类任务中经常使用XGBoost快速建立baseline,在处理文本数据时需要引入TFIDF将文本转换成基于词频的向量才能输入到XGBoost进行分类。这篇博客将简单阐述XGB进行文本分类的实现与部分原理。
大鹅
2021/12/23
2.8K1
基于深度学习和经典方法的文本分类
本文研究了一种使用CNN和LSTM进行文本分类的方法,通过实验和对比,发现CNN在新闻分类任务上表现较好,LSTM在短文本分类任务上表现较好。同时,本文还探讨了预训练词向量在文本分类中的作用,并提出了改进的预训练词向量方法。
段石石
2017/06/27
9.9K0
基于深度学习和经典方法的文本分类
基于深度学习的文本分类应用!
在基于机器学习的文本分类中,我们介绍了几种常见的文本表示方法:One-hot、Bags of Words、N-gram、TF-IDF。这些方法存在两个共同的问题:一是转换得到的向量维度很高,需要较长的训练实践;二是没有考虑到单词与单词之间的关系,只是进行了统计。
Datawhale
2020/08/17
6400
【入门】PyTorch文本分类
文本分类是NLP领域的较为容易的入门问题,本文记录文本分类任务的基本流程,大部分操作使用了torch和torchtext两个库。
zenRRan
2020/02/18
1.9K0
文本数据处理的终极指南-[NLP入门]
简介 实现任何程度或者级别的人工智能所必需的最大突破之一就是拥有可以处理文本数据的机器。值得庆幸的是,全世界文本数据的数量在最近几年已经实现指数级增长。这也迫切需要人们从文本数据中挖掘新知识、新观点。
用户1332428
2018/03/30
1.4K0
文本数据处理的终极指南-[NLP入门]
用Python开始机器学习:文本特征抽取与向量化
假设我们刚看完诺兰的大片《星际穿越》,设想如何让机器来自动分析各位观众对电影的评价到底是“赞”(positive)还是“踩”(negative)呢? 这类问题就属于情感分析问题。这类问题处理的第一步,就是将文本转换为特征。 因此,这章我们只学习第一步,如何从文本中抽取特征,并将其向量化。 由于中文的处理涉及到分词问题,本文用一个简单的例子来说明如何使用Python的机器学习库,对英文进行特征提取。 1、数据准备 Python的sklearn.datasets支持从目录读取所有分类好的文本。不过目录必须按照一
机器学习AI算法工程
2018/03/13
3.1K0
用Python开始机器学习:文本特征抽取与向量化
用深度学习实现自然语言处理:word embedding,单词向量化
前几年,腾讯新闻曾发出一片具有爆炸性的文章。并不是文章的内容有什么新奇之处,而是文章的作者与众不同,写文章的不是人,而是网络机器人,或者说是人工智能,是算法通过分析大量财经文章后,学会了人如何编写财经报道,然后根据相关模式,把各种财经数据组织起来,自动化的生成一篇文章,当人阅读时,根本无法意识到文章不是人写,而是电脑生成的。
望月从良
2018/08/16
1.3K0
用深度学习实现自然语言处理:word embedding,单词向量化
使用经典ML方法和LSTM方法检测灾难tweet
在本文中,我将对分类任务应用两种不同的方法。我将首先应用一个经典的机器学习分类算法-梯度增强分类器。
磐创AI
2021/08/05
1.1K0
使用经典ML方法和LSTM方法检测灾难tweet
数据分析:文本分类
本章节中所涉及的知识点偏向于机器学习的范畴,那么机器学习和数据分析有什么区别呢。简单来讲,数据分析是少量数据采样分析而机器学习是海量数据全部分析。比较好的理解一点是,数据分析会总结过去已经发生的事情,而机器学习是为了预测未来发生的事情。这两者也是有相辅相成的关系。我们可以通过机器学习预测的结果,进行数据分析,得到一个相对准确的结论,辅助人们进行决策判断等等。
马拉松程序员
2023/09/02
4150
数据分析:文本分类
推荐阅读
相关推荐
Word2Vec——使用GloVe训练中文词向量
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档