作者mamaya3 (mamaya)
看板AndroidDev
标题[分享] 取得摄影机预览资料并回馈到萤幕上
时间Tue Apr 26 11:02:46 2011
原本不想写的..因为架构上需要用到三个class 另外有些细节部分我也不很熟
怕写出来有误导大众之嫌 不过最近还是会有人问类似的问题 所以我乾脆抽点时间
开个新专案把主要机制 重新整理出来 大家也比较方便了解
在一切起头前 这边首先介绍会用到的class 一共有三个
ViewToDraw class
从View继承而来 功能就是提供画布功能让你画东西
Camera preview class
很一般的摄影机预览class 唯一不同的地方是它会有个变数来存ViewToDraw
这样每次预览更新 就可以丢图像资料直接到画布上 顺便提醒它更新画面
main class
功能不多 主要就是锁住萤幕的旋转方向在lanscape(因为2.2以前不支援potrait)
还有取得全画面(如果你想要的话) 并建立起主要的layout
好了 这边了解後接下来就开始动工了 首先你会需要修改AndroidManifest.xml
这应该写过摄影机功能的人都知道 要加入下面两行
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
这边就不多谈了
再来就是改layout 不过由於你的layout会用到custom view而你还没实作 所以会有错误
讯息...先不管它吧 anyway, 整个xml如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="
http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<FrameLayout android:id="@+id/FrameLayout01"
android:layout_height="fill_parent" android:layout_width="fill_parent">
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/preview">
</FrameLayout>
<com.maya.mcp.ViewToDraw
android:id="@+id/vtd"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
</com.maya.mcp.ViewToDraw>
</FrameLayout>
</LinearLayout>
ID为preview的FrameLayout之後会加上摄影机预览的surfaceView
ID为vtd的cutsom View就是等会要拿来画画的画布
搞定了layout後接下来开始实作class 首先从画布开始...
在这边 你会需要几个变数 首先是输入图像的长跟宽 由於它不一定跟萤幕解析度相同
所以你等会需要它做长宽比缩放
另外 你当然也需要一个byte的阵列去存preview callback时丢过来的图像资料
还有 你也要一个变数确定Camera已经架好运作 资料已经丢来的旗标 不然甚麽图像都没
读到时就让程式傻傻跑onDraw 结果一定是会crash的
ok..变数搞定了 接下来是method... 变数设定存取的那些就不管了 相信你都会写
这边你还会需要一个把YUV420SP资料转存的method 由於在我的范例中我只来简单判断
亮度 所以程式相当简单如下
private boolean getBoolean(int x, int y){
int l;
l = (0xff & ((int) image[x+y*imgWidth])) - 16;
if (l > 128){
return true;
}else{
return false;
}
}
如果你要转成RGB 我也有附在档案里 不过是从网路上挖来的..请自行研究
至於onDraw()要做甚麽呢? 以我的范例是要把读到的图二元化後涂回画布去
所以是用二层回圈把图像资料解读出来 然後再丢白或黑点上去
@Override
protected void onDraw(Canvas canvas){
int i, j;
//将预览图资料与画布做长宽缩放比运算
float hscale = (float)canvas.getHeight()/imgHeight;
float wscale = (float)canvas.getWidth()/imgWidth;
if(isCameraSet){
for(i=0;i<imgWidth;i+=8){
for(j=0;j<imgHeight;j+=8){
if(getBoolean(i,j)){
canvas.drawPoint(i*wscale, j*hscale,
whitePaint);
}else{
canvas.drawPoint(i*wscale, j*hscale,
blackPaint);
}
}
}
}
}
接下来是cameraView class 大部分都跟一般开相机功能的范例一样 主要不同点在於
使用了setPreViewCallbackWithBuffer 设定callback来处理相机预览画面的资料更新
这是一个2.2新增的method 在2.1以前请用setPreViewCallback 两者差别只在於前者
要先产生多个buffer给callback预备使用 让传回效率可以进一步提升 程式如下
//产生 buffer
PixelFormat p = new PixelFormat();
PixelFormat.getPixelFormatInfo(parameters.getPreviewFormat(),p);
int bufSize = (pickedW*pickedH*p.bitsPerPixel)/8;
//把buffer给preview callback备用
byte[] buffer = new byte[bufSize];
mycamera.addCallbackBuffer(buffer);
buffer = new byte[bufSize];
mycamera.addCallbackBuffer(buffer);
buffer = new byte[bufSize];
mycamera.addCallbackBuffer(buffer);
//设定预览画面更新时的callback
mycamera.setPreviewCallbackWithBuffer(new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
vtd.putImage(data);
vtd.CameraSet();
//更新画布 (call onDraw())
vtd.invalidate();
//把buffer丢回给callback重新利用
mycamera.addCallbackBuffer(data);
}
});
最後是处理main class 这部分就很简单 主要有三
1.把画面锁在landscape
2.取得ViewToDraw
3.产生摄影机预览的surfaceView 并把ViewToDraw pass过去
4.把上面的surfaceView加到FrameLayout
程式如下
// 取得全萤幕
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
setContentView(R.layout.main);
//锁住萤幕方向
setRequestedOrientation(0);
//取得画图的View
ViewToDraw dtw = (ViewToDraw) findViewById(R.id.vtd);
//产生摄影机预览surfaceView
CameraView cameraView = new CameraView(this, dtw, this.getApplicationContext());
//把预览的surfaceView加到名为preview的FrameLayout
((FrameLayout) findViewById(R.id.preview)).addView(cameraView);
整个范例程式码可以到下面下载 不保证跑在每台手机上都能很顺就是了XD
http://www.megaupload.com/?d=S0U489LO
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 71.109.152.84
※ 编辑: mamaya3 来自: 71.109.152.84 (04/26 11:23)
1F:推 laiis:这个赞! 04/26 12:04
2F:推 ericinttu:先推再看. 04/26 12:17
3F:推 zxc190:先推 04/26 14:29
4F:推 nowar100:已收录 04/26 17:18
5F:推 aiueoH:非常感谢大大分享!!! 我需要的就是这些 04/26 17:31
6F:推 givemepass:给个推 04/26 17:41
7F:推 hohoww:先推 先赞, 谢谢分享 04/26 19:21
8F:推 CIHM:推!! 不错说! 04/26 20:24
9F:推 jason860421:未看先推 04/26 21:38
10F:推 rickylee288:推推推~感谢无私分享 04/26 22:11
11F:推 coronach:推推推!!! 04/26 23:16
12F:推 fred104:很实用耶!! 04/27 15:50
13F:推 italk1983:感谢分享! 04/28 15:31
14F:推 ninewords:太棒了!! 谢谢分享!! 04/29 17:01
15F:→ aiueoH:请问 YCrCb 就是 YUV421吗?? ...因为看到getBoolean这个 04/30 13:47
16F:→ aiueoH:地方的注解是YUV421 ... 可是我看DEV网站是说YCrCb 04/30 13:48
17F:→ aiueoH:预设是 NV21 (YCrCb) 04/30 13:49
18F:推 ninewords:YUV即YCrCb,亮度与彩度~~~ 04/30 13:53
19F:推 aiueoH:了解 谢谢 04/30 14:03
20F:→ aiueoH:想请问 为什麽我抓到的PreviewFormat是YUV422SP格式的? 04/30 17:55
21F:→ aiueoH:使用setPreviewFormat也无法设定成其他格式 04/30 17:56
22F:→ aiueoH:网路上都是YUV420SP转RGB ... 不知道有没有YUV422SP的~"~ 04/30 17:56
23F:推 kenru:推...好棒 07/26 17:37