Android利用SurfaceView显示Camera图像爬坑记(二)

文章介绍了如何在Android中利用SurfaceView和Camera的PreviewCallback来捕获每一帧图像,并将其转换为Bitmap进行OpenCV图像处理。通过YuvImage和BitmapFactory将NV21数据转化为Bitmap,然后在Canvas上绘制。遇到的问题包括图像方向错误,通过矩阵旋转解决了此问题。最终目标是实现处理后的图像实时显示。

前言
在这里插入图片描述

前一章《[Android利用SurfaceView显示Camera图像爬坑记(一)我们已经实现了利用SurfaceView将Camera中的实时帧图像显示出来了,我们做这个的主要目录是想把每一帧的数据取出后通过OpenCV图像处理后,再实时显示出处理后的图像。

要实现这个情况,我们首先要把Camera的实时数据存成Bitbmp的图像然后通过自己的处理显示出来,接下来我们就看看怎么样把Camera的实时图像都通过Bitbmp的方式显示出来。

代码实现

我们还是接上一篇的代码接着开始,还记得上一篇中我们的VaccaeSurfaceView类中定义了Camera的回调方法吗?

我们在程序运行后的LogCat里面可以查看到日志,输入的Log里面会不停的发送good字符串,如下图

上面就说明了我们的回调方法已经成功了,想到我们自己把图像处理显示出来,就可以在这个回调的方法中进行图片的处理。

这里简单介绍一下代码中的synchronized(this),当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

简单的说就是我们获取的每一帧图像都会在这个代码里面处理完成了才处理下一帧图像。当然这里也可以用AnsycTask来进行实现。

实现原理及核心代码

我们在图像上按获取到的图片Bitbmp通过我们创建的SurfaceHolder来生成Canavas,然后在这个Canavas中能过drawBitbmp的方法绘制图片即可。

回调函数的代码

    private Camera.PreviewCallback previewCallback=new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] bytes, Camera camera) {
            synchronized (this) {
                Log.i("frame", "good");
                int width=camera.getParameters().getPreviewSize().width;
                int height=camera.getParameters().getPreviewSize().height;
                Canvas canvas=holder.lockCanvas();
                if (canvas != null) {
                    canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                    Bitmap cacheBitmap=nv21ToBitmap(bytes, width, height);
                    canvas.drawBitmap(cacheBitmap, 0, 0, null);
                    holder.unlockCanvasAndPost(canvas);
                }
            }
        }
    };

上图中对图像有一个nv21toBitmap的方法,就是用来生成图像的,我们看一下这个方法。

nv21ToBitmap

    //输出图像
    private static Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
        Bitmap bitmap=null;
        try {
            YuvImage image=new YuvImage(nv21, ImageFormat.NV21, width, height, null);
            ByteArrayOutputStream stream=new ByteArrayOutputStream();
            image.compressToJpeg(new Rect(0, 0, width, height), 80, stream);
            //将rawImage转换成bitmap
            BitmapFactory.Options options=new BitmapFactory.Options();
            options.inPreferredConfig=Bitmap.Config.ARGB_8888;
            bitmap=BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size(), options);

            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

到目前为止看来我们已经完成了,接下来我们运行程序,程序中倒是一直在正常运行,不过我们看了一下LogCat中的记录,发现一直报错

在网上查找了一下原因,可能是camera.setPreviewDisplay(holder)这句有问题,相机一直在用这个holder像surfaceview输出图像,后面程序中再使用surfaceholder来绘制新的图片,就会冲突,然后报错。

找了找解决办法,发现可以用SurfaceTexture来代替实现这个图像的绘制,那我们按这个方法来试试

首先定义一个SurfaceTexture

然后在VaccaeSurfaceView构造函数中实例化这个SurfaceTexture

最后在StartCamera的方法加加入setPreviewTexture,并且屏蔽原来的seetPreviewDisplay的方法

接下来我们运行程序后,发现每一帧也都显示出来了,不过图像的方向不对,如下图

解决这个问题也比较简单,我们把我们的nv21ToBitmap处理图片的方法改造一下,让其直接也旋转90度即可。

nv21ToBitmap

    private static Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
        Bitmap bitmap=null;
        try {
            YuvImage image=new YuvImage(nv21, ImageFormat.NV21, width, height, null);
            ByteArrayOutputStream stream=new ByteArrayOutputStream();
            image.compressToJpeg(new Rect(0, 0, width, height), 80, stream);
            //将rawImage转换成bitmap
            BitmapFactory.Options options=new BitmapFactory.Options();
            options.inPreferredConfig=Bitmap.Config.ARGB_8888;
            bitmap=BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size(), options);

            //加入图像旋转
            Matrix m=new Matrix();
            m.postRotate(90);
            bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                    m, true);

            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

我们重新运行程序的视频效果

上面视频可以看到,我们的图像已经正常了,但是图像显示出来的大小和我们的界面布局不一致,我们下一篇就针对这个问题来看看怎么处理。

最后

更多Android 学习资料可以扫码免费领取!

《Android编程入门指南》+Android基础课程资料包

内容概要:Android编程入门指南、Android基础课程资料包;

内容特点:条理清晰,含图像化表示更加易懂;

完整文档领取方式👇

# **《Android编程入门指南》**

目录

img

第一章 Java语言基础从入门到熟悉

​ ● Java综述

​ ● Java基础

​ ● class

​ ● 面向对象

​ ● 容器

​ ● 多线程

​ ● IO

​ ● 正则表达式

​ ● 日历

​ ● 注解使用与简单说明

​ ● Java使用加密算法

img

第二章 Kotlin语言基础从入门到熟悉

​ ● Kotlin系列简介

​ ● 新建项目

​ ● 概念介绍

​ ● 常用写法

​ ● lambda

​ ● 高阶函数

​ ● 扩展函数

​ ● 协程

img

第三章 Android 技术栈从入门到熟悉

​ ● 开始

​ ● Activity

​ ● Service 服务

​ ● Broadcast 广播机制

​ ● Fragment

​ ● res应用资源

​ ● 权限

​ ● TextView 显示文字

​ ● EditText 用户输入

​ ● Button 按钮

​ ● ImageView 图片显示

​ ● Layout布局

​ ● DrawerLayout侧滑栏

​ ● RecyclerView 显示列表

​ ● 多线程

​ ● IO 操作

​ ● 存储

​ ● NetWork 网络

​ ● ProgressBar进度条

​ ● View

​ ● 自定义View

​ ● 动画

​ ● WebView

​ ● StatusBar

​ ● Camera相机

​ ● Media

​ ● Bluetooth 蓝牙

​ ● WiFi连接

​ ● 获取手机当前角度

​ ● launcher

​ ● AIDL

​ ● zip

​ ● Binder

​ ● Notification 通知

​ ● PackageManager

​ ● RTFSC

​ ● 设计模式

​ ● 第三方库

​ ● Android Studio

​ ● 模块化

​ ● Gradle

​ ● NDK

​ ● 调试

img

第四章 Android Jetpack 全家桶全面学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值