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-

Vaccae的往期经典


OpenCV

《C++ OpenCV案例实战---卡号获取

《C++ OpenCV案例实战---卡片截取(附代码)

《C++ OpenCV透视变换---切换手机正面图片》

《C++ OpenCV实战---获取数量

《C++ OpenCV实战---利用颜色分割获取数量》


Android

《Android利用SurfaceView结合科大讯飞修改语音实别UI

《Android关于语音识别的功能实现分析(一)---结构化思维》

《Android关于语音识别的功能实现分析(二)---语义解析》

《Android根据类生成签名字符串

《Android碎片化布局fragment的实战应用

《Android中RecyclerView嵌套RecyclerView

《Android里用AsyncTask后的接口回调


.Net C#

《C#自定义特性(Attribute)讲解与实际应用

《C#根据类生成签名字符串(附DEMO下载地址)

《C++创建动态库C#调用》

《C#与三菱PLC(型号FX2N)串口通讯类


数据库及其它

《Oracel存储过程写报表实战》

《Delphi轮播视频和图片程序(用于双屏显示程序)

《SQL随机增加销售数据的脚本编写(附脚本下载地址)

SQL Server中With As的介绍与应用(三)--递归的实战应用

《Oracle通过ODBC连接SQL Server数据库


长按下方二维码关注微卡智享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值