前言
前一章《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
Android
《Android利用SurfaceView结合科大讯飞修改语音实别UI》
《Android关于语音识别的功能实现分析(一)---结构化思维》
《Android关于语音识别的功能实现分析(二)---语义解析》
《Android中RecyclerView嵌套RecyclerView》
.Net C#
数据库及其它
《SQL Server中With As的介绍与应用(三)--递归的实战应用》
长按下方二维码关注微卡智享