专栏首页微卡智享Android利用SurfaceView显示Camera图像爬坑记(二)

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

前言

前一章《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;
    }

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

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

-END-

本文分享自微信公众号 - 微卡智享(VaccaeShare),作者:Vaccae

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++ OpenCV形态学操作--提取水平和垂直线

    图像形态学操作时候,可以通过自定义的结构元素实惠结构元素对输入图像一些对象敏感,另外一些不敏感,这样就会让敏感的对象改变而不敏感的对象保留输出,通过使用两个最基...

    Vaccae
  • C++ OpenCV线性混合操作

    void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, do...

    Vaccae
  • C++ OpenCV图像的矩

    图像识别的一个核心问题是图像的特征提取,简单描述即为用一组简单的数据(数据描述量)来描述整个图像,这组数据月简单越有代表性越好。良好的特征不受光线、噪点、几何形...

    Vaccae
  • 实现HTTP协议Get、Post和文件上传功能——设计和模块

            之前写过一遍《使用WinHttp接口实现HTTP协议Get、Post和文件上传功能》,其中谈到了如何使用WinHttp接口实现Http的Get、...

    方亮
  • 为什么S/4HANA的销售订单创建会触发生产订单的创建

    版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

    Jerry Wang
  • SQL优化误用'append'案例一则

    编辑手记:SQL是数据库系统的核心,因SQL问题引发的系统蝴蝶效应屡见不鲜,今天继续学习SQL优化的技巧。。 这是某客户关键系统的一个TOP SQL: ? 根据...

    数据和云
  • 19个Linux备份压缩命令

    ? 文 | 云豆 来源 | 菜鸟教程 ? 云豆贴心提醒,本文阅读时间5分钟,文末有秘密! Linux ar命令 Linux ar命令用于建立或修改备存...

    小小科
  • HDR质量评价技术

    本文聚焦HDR质量评价技术,对于编解码、色调映射以及逆色调映射等不同任务,通常会采取不同的评价方法。本部分先从主观评价和客观评价两个角度对常用的HDR视觉质量评...

    用户1324186
  • 干货分享:让你分分钟学会 javascript 闭包 一像素

    闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它。...

    zhisheng
  • 让你分分钟学会 javascript 闭包

    闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它。...

    用户1667431

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动