`
muyu114
  • 浏览: 135189 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

Android用摄像头的那点破事

 
阅读更多

前篇:

好早就装了开发环境,真正着手还是这两天,非常的生疏,虽然有SDK文档,那么多蚊子一般的字,实在没心思慢慢研究。这不想调用摄像头,原以为很容易就能搞定的,累计花了大概有一天的时间才只能保证不出错……至于效果嘛,难说啊!

先看API-examples里有调用 摄像头的例子,在模拟器上虽然看不出什么效果,毕竟还是能执行的,就是一个方块在黑白相间的背景上移动呗。

就这么一个Google提供的范例,传到我的HTC G2上也能一执行就报错,我对Google的尊敬之情顿时减少了0.0001%啊……(当然有可能是G2不够标准,但毕竟其他的软件都是能用的,看来是有不少健壮代码了啊)。联机调试看了一下,出错的这一行(android-7里的):

parameters.setPreviewSize(w, h);

查一下,摄像头不是所有随便的(w, h)都能够认识的,所以呢,我们有了下面这样的增强版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
List<Size> mSupportedPreviewSizes;
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}

后来的Sample里有了这段代码,看起来强大了不少。然而非常不幸的,首先getSupportedPreviewSizes()这个函数在2.1之后才有,我一开始是打算用1.6开发的……好吧我改,这个先不说,自己的手机已经刷到2.1了,这个函数的返回值居然是null?!如果确实想老版本上也用的话,怎么办??

有鉴于有软件可以达成,所以肯定是有方法的!得这么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class SupportedSizesReflect {
private static Method Parameters_getSupportedPreviewSizes = null;
private static Method Parameters_getSupportedPictureSizes = null;
static {
initCompatibility();
};
private static void initCompatibility() {
try {
Parameters_getSupportedPreviewSizes = Camera.Parameters.class.getMethod(
"getSupportedPreviewSizes", new Class[] {});
Parameters_getSupportedPictureSizes = Camera.Parameters.class.getMethod(
"getSupportedPictureSizes", new Class[] {});
} catch (NoSuchMethodException nsme) {
nsme.printStackTrace();
Parameters_getSupportedPreviewSizes = Parameters_getSupportedPictureSizes = null;
}
}
/**
* Android 2.1之后有效
* @param p
* @return Android1.x返回null
*/
public static List<Size> getSupportedPreviewSizes(Camera.Parameters p) {
return getSupportedSizes(p, Parameters_getSupportedPreviewSizes);
}
public static List<Size> getSupportedPictureSizes(Camera.Parameters p){
return getSupportedSizes(p, Parameters_getSupportedPictureSizes);
}
@SuppressWarnings("unchecked")
private static List<Size> getSupportedSizes(Camera.Parameters p, Method method){
try {
if (method != null) {
return (List<Size>) method.invoke(p);
} else {
return null;
}
} catch (InvocationTargetException ite) {
Throwable cause = ite.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw new RuntimeException(ite);
}
} catch (IllegalAccessException ie) {
return null;
}
}
}

啊啊~,リフレクションなんか、大嫌い……然后还要用类似这样的方法调用~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
Camera.Parameters params = camera.getParameters();
List<Size> supportedPictureSizes
= SupportedSizesReflect.getSupportedPictureSizes(params);
List<Size> supportedPreviewSizes
= SupportedSizesReflect.getSupportedPreviewSizes(params);
if ( supportedPictureSizes != null &&
supportedPreviewSizes != null &&
supportedPictureSizes.size() > 0 &&
supportedPreviewSizes.size() > 0) {
//2.x
pictureSize = supportedPictureSizes.get(0);
int maxSize = 1280;
if(maxSize > 0){
for(Size size : supportedPictureSizes){
if(maxSize >= Math.max(size.width,size.height)){
pictureSize = size;
break;
}
}
}
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
previewSize = getOptimalPreviewSize(
supportedPreviewSizes,
display.getWidth(),
display.getHeight());
params.setPictureSize(pictureSize.width, pictureSize.height);
params.setPreviewSize(previewSize.width, previewSize.height);
}
this.camera.setParameters(params);
try {
this.camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
this.camera.startPreview();
}

死机无数次之后总结出来的啊,发现程序写的一个不好强制结束了,摄像头都无法再次启用了,kill都不行,只能重新启动手机才好。重启一次还那么慢,谁知道有比较适合G2的row?

哦还有一个,预览画面90°的,2.X后可以用parameters.set(“rotation”, “90″),之前的话得写成parameters.set(“orientation”, “portrait”)。但是据说不是所有的机器都可以的……


续篇:

上次讲的是摄像头的初始化,如果觉得这么就万事OK的话,那就大错特错了。接下来的东西让人感到更加头痛。

在我的这个应用里,不需要把拍下来的图片存储,只需要把预览的图片数据处理一下就好,很自然的我只是用了onPreviewFrame调用,考虑处理传递进来的data数据流就是了。

网上很多帖子都说,然后用BitmapFactory的decodeByteArray()函数来解析图片就行了,我试了一下,发现这真是彻头彻尾的谎言,data字节流默认是YCbCr_420_SP(虽然可以改,但其他的格式未必兼容),decodeByteArray()压根儿不认!SDK2.2之后,似乎提供了一个YuvImage的类来转一下(那Google一开始提供这个借口是做什么的?),难道就要把老机给抛弃了么??万万不能啊(穷人最理解穷人们了)!

好在这个世界总是不缺少好人和牛人的,有人提供了这么一段转换的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;
rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}

我不是很清楚这里面的原理,但是它能在我这里工作,暂时可以了……然后你才可以吧处理完的rgb[]传给decodeByteArray()。

顺便好心的把使用SDK2.2之后的也贴上吧,万一有用呢……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void onPreviewFrame(byte[] data, Camera arg1) {
FileOutputStream outStream = null;
try {
YuvImage yuvimage = new YuvImage(data,ImageFormat.NV21,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height,null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0,0,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height), 80, baos);
outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
outStream.write(baos.toByteArray());
outStream.close();
Log.d(TAG, "onPreviewFrame - wrote bytes: " + data.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Preview.this.invalidate();
}

哦,得到的图像旋转了90°(似乎有的机型设置一下setRotation(90)可以搞定,但还是那句话,不通用啊,况且这个是2.1之后的API)。手动转一下吧……

1
2
3
4
5
6
Matrix matrix = new Matrix();
matrix.postRotate(90);
// 这里的rgb就是刚刚转换处理的东东
Bitmap bmp = Bitmap.createBitmap(rgb, 0, w, w, h, Bitmap.Config.ARGB_4444);
Bitmap nbmp = Bitmap.createBitmap(bmp,
0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);

终于正常了~~~

考虑到需要做识别,自然得先把它转成灰度图像,经典心理公式Gray = R*0.299 + G*0.587 + B*0.114出场了,但是手机的计算速度不那么快,这样的浮点运算还是尽量避免吧~ 于是考虑Gray = (R*299 + G*587 + B*114 + 500) / 1000或者Gray = (R*30 + G*59 + B*11 + 50) / 100。但是除法总是还是不够快,用移位吧……Gray = (R*19595 + G*38469 + B*7472) >> 16,稍微小一点,用Gray = (R*38 + G*75 + B*15) >> 7也足够了。

经过一番努力学习,把写就的代码兴致勃勃的在手机上跑了一下,虽然不够快结果出来了,想想也是大负荷运算啊,自我安慰客户应该可以有这样的耐心吧。

就在这个时候,我突然想起一件很重要的事情!
我需要的是灰度图,也就是亮度风量,而最开始的YUV,不就是亮度色度饱和度么?!那么Y分类不就是我需要的灰度值吗!!我在做什么,辛辛苦苦转成RGB,再转成亮度,吃饱了撑着不是。想到这里我立刻用头撞墙九九一百八十一次,一悼念我那白白死去的脑细胞的在天之灵。立刻重写,删除大量代码,快多了,效果也好~~ 鄙视一下两小时前的自己!

另外,今天去看变形金刚3了,还不错吧,人好多,坐前排脖子都抬酸了……

转自:http://eyehere.net/2011/android-camera-2/


分享到:
评论

相关推荐

    AndroidUSB摄像头含拍照

    在Android平台上,USB摄像头的集成和使用是一项技术性较强的任务,尤其涉及到硬件接口的检测、设备识别以及拍照功能的实现。本项目"Android USB摄像头含拍照"提供了一个完整的解决方案,帮助开发者实现Android设备...

    AndroidUsb摄像头Demo

    综上所述,"AndroidUsb摄像头Demo"项目涵盖了Android平台与USB摄像头交互的多个关键技术点,从设备枚举、权限管理到实时预览、录像拍照及录音功能的实现,开发者可以通过这个项目学习到如何扩展Android设备的多媒体...

    Android 网络摄像头实现

    在Android平台上实现网络摄像头功能,主要是为了让Android设备能够作为摄像头,通过网络进行视频流传输,以便其他设备(如PC、手机或智能设备)能够在局域网内实时监控。这一技术通常依赖于Socket通信,它是互联网...

    Android虚拟摄像头无人直播技术源码

    在本文中,我们将深入探讨Android虚拟摄像头无人直播技术的核心知识点,包括其原理、实现方式以及与音视频处理的关联。这个技术源码采用的是hook技术,适用于root过的Android设备,可以实现在没有人工操作的情况下...

    Android前后摄像头同时预览

    总的来说,"Android前后摄像头同时预览"涉及到Android Camera API的使用、多摄像头管理、视图渲染、性能优化以及设备兼容性等多个方面,是Android开发中一个复杂但有趣的实践案例。通过分析和理解`DoubleCameraDemo`...

    android调用摄像头实时预览

    在Android平台上,调用摄像头进行实时预览是开发过程中常见的需求,特别是在开发摄影、视频聊天或者AR应用时。本文将详细讲解如何在320*320像素、mdpi屏幕分辨率下设置摄像头预览。 首先,理解Android的屏幕密度...

    android开发摄像头实现前置后置切换

    在Android平台上进行应用程序开发时,摄像头的使用是一个常见的需求,特别是在构建相机相关的应用时。本教程将详细讲解如何实现一个Android小程序,以实现在运行时切换前后摄像头的功能。这一过程涉及到了Android...

    android网络摄像头的各种实现

    在Android平台上实现网络摄像头功能,通常涉及到多个技术层面,包括硬件接口、软件协议、网络通信以及用户界面设计。本文将详细探讨如何在Android系统中构建一个网络摄像头应用,以满足实时视频流传输的需求。 首先...

    Android 版手机摄像头

    1. **Android 应用开发**:Android版手机摄像头应用通常由Java或Kotlin编写,使用Android Studio作为集成开发环境。开发者需要理解Android SDK,学习如何创建活动(Activity)、服务(Service)以及处理用户界面(UI...

    android使用usb外接摄像头拍照并保存照片

    这个"android使用usb外接摄像头拍照并保存照片"的示例项目,旨在解决这个问题,提供了一个完整的解决方案,涵盖了从连接摄像头到捕获图像再到本地存储的全过程。 首先,我们需要了解Android对USB设备的支持。...

    Android 调用外接摄像头

    在Android平台上,调用外接摄像头是一项常见的...以上就是Android调用外接摄像头的基本流程和关键知识点,希望对你开发相关功能有所帮助。在实际项目中,可以结合提供的`android Demo`代码进一步理解和实践这些概念。

    android 的摄像头 和soundPool播放声音实例

    本实例将探讨如何使用Android SDK中的相关组件来实现摄像头功能以及通过SoundPool播放声音。 1. 摄像头操作 在Android中,摄像头功能主要通过Camera类和Camera2 API来实现。Camera类是早期版本Android中的主要接口...

    Android调用摄像头拍照(兼容7.0)

    在Android应用开发中,调用摄像头进行拍照是常见的功能之一,尤其在社交、影像记录类应用中不可或缺。然而,随着Android系统的不断更新,对于权限管理、API接口的改动,使得在不同版本上实现这一功能变得稍有复杂。...

    Android UVCCamera USB外接摄像头源码

    使用USB摄像头需要在AndroidManifest.xml中添加`&lt;uses-feature&gt;`标签声明USB主机功能,并请求`android.permission.ACCESS_USB`权限。此外,应用还需要在运行时请求用户授权访问特定的USB设备。 6. **设备枚举和...

    android通过USB调用摄像头

    android通过USB外接摄像头这是一个例子,通过C调用底层驱动。在网上找过了例子总出现select timeout异常,改造解决了。有问题可以发邮件505738005.

    Android USB摄像头源码

    在Android平台上,USB摄像头的接入和使用涉及到一系列的技术细节,主要涵盖了Android系统对硬件设备的管理、JNI(Java Native Interface)技术以及与USB设备的通信。以下是对标题和描述中涉及知识点的详细说明: 1....

    android摄像头开发完美例程(几乎涉及所有有关摄像头开发的东东)

    Android摄像头开发完美demo,包括摄像头循环聚焦,缩放大小,旋转picture,查询picturesize, 增加ImageButton的按键效果。整个代码写的简洁,几乎涉及所有有关摄像头开发的东东。参见:...

    android wifi网络摄像头源代码

    【标题】"Android WiFi网络摄像头源代码"是一个开源项目,主要目标是实现通过WiFi网络将Android设备转换为网络摄像头的功能。这个项目对于开发者而言,是一个深入理解Android系统、网络编程以及多媒体处理的好资源。...

    Android前置摄像头预览并检测人脸,获取人脸区域亮度

    综上所述,"Android前置摄像头预览并检测人脸,获取人脸区域亮度"这个项目涵盖了Android摄像头控制、图像预览、人脸检测算法的应用、图像处理技术以及性能优化等多个关键知识点。通过理解并熟练掌握这些技术,开发者...

Global site tag (gtag.js) - Google Analytics