Android应用调用摄像头开发例子

这两天玩Android玩的废寝忘食,Blog都好几天没加东西了,惭愧!记录一下这两天最崩溃的一个问题。

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

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

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

parameters.setPreviewSize(w, h);

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

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?!如果确实想老版本上也用的话,怎么办??

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

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;
  }
 } 
}

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

@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一开始提供这个借口是做什么的?),难道就要把老机给抛弃了么??万万不能啊(穷人最理解穷人们了)!

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

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之后的也贴上吧,万一有用呢……

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)。手动转一下吧……

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,再转成亮度,吃饱了撑着不是。想到这里我立刻用头撞墙九九一百八十一次,一悼念我那白白死去的脑细胞的在天之灵。立刻重写,删除大量代码,快多了,效果也好~~ 鄙视一下两小时前的自己!

时间: 2024-12-28 01:15:57

Android应用调用摄像头开发例子的相关文章

Android实现调用摄像头_Android

应用场景: 在Android开发过程中,有时需要调用手机自身设备的功能,本文侧重摄像头拍照功能的调用. 知识点介绍: 使用权限:调用手机自身设备功能(摄像头拍照功能),应该确保已经在AndroidManifest.xml中正确声明了对摄像头的使用及其它相关的feature 1. 摄像头相关权限设置     <!--摄像头权限 --> <uses-permission android:name="android.permission.CAMERA" /> <

Android开发教程之调用摄像头功能的方法详解_Android

本文实例讲述了Android调用摄像头功能的方法.分享给大家供大家参考,具体如下: 我们要调用摄像头的拍照功能,显然 第一步必须加入调用摄像头硬件的权限,拍完照后我们要将图片保存在SD卡中,必须加入SD卡读写权限,所以第一步,我们应该在Android清单文件中加入以下代码 摄像头权限: <uses-permission android:name="android.permission.CAMERA"/> SD卡读写权限: <uses-permission androi

调用uvc摄像头-android 平台调用UVC摄像头

问题描述 android 平台调用UVC摄像头 1C 有没有哪位大神实现过安卓调用uvc摄像头啊?我最近查了网上资料都是讲得很不清楚而且不全的.!!跪求大神赐教啊!!! 解决方案 Android如何调用摄像头 解决方案二: 首先是 安卓平台识别usb摄像头吗? 识别就好说,假设识别: 安卓调用摄像头 首先获取摄像头列表 然后设置将要使用的设备......接下来就和安卓调用一样了

android-Android,关于摄像头开发问题求指导!

问题描述 Android,关于摄像头开发问题求指导! 程序需要调用摄像头,完成录像功能,要求在录制时可以对**视频质量**(分辨率,编码器,格式的MP4或3GP,码率,帧率)进行设置!(类似LG相机)

android 开发调用摄像头如何 拍摄格式为mp4的视频文件

问题描述 android 开发调用摄像头如何 拍摄格式为mp4的视频文件 现在拍摄的是3gp的 需要改格式 但是弄了好久 也没找到什么有用的资料 谁有好的完整性的demo或资源能够提供的啊 谢谢了 解决方案 // 视频文件的存放地址 private String strVideoPath = Environment.getExternalStorageDirectory() .getAbsolutePath() + "rec_video.mp4"; 存的时候用.mp4,调用摄像头原生的

Android开发教程之调用摄像头功能的方法详解

本文实例讲述了Android调用摄像头功能的方法.分享给大家供大家参考,具体如下: 我们要调用摄像头的拍照功能,显然 第一步必须加入调用摄像头硬件的权限,拍完照后我们要将图片保存在SD卡中,必须加入SD卡读写权限,所以第一步,我们应该在Android清单文件中加入以下代码 摄像头权限: <uses-permission android:name="android.permission.CAMERA"/> SD卡读写权限: <uses-permission androi

Android摄像头开发完美demo---(循环聚焦,缩放大小,旋转picture,查询支持的picturesize, ImageButton按键效果)

这个代码几乎涉及到了摄像头开发的所有方面,(除了PreviewCallback,这块东西我会结合android摄像头自动识别人脸/火灾来谈),且力求精简,是杂家的心血阿!相对之前改进之处有: 1,精简.只有一个ImageButton用来实现按下拍照.拍照后自动保存,进入预览界面. 不像原来的要三个按键:预览/拍照/保存. 2,聚焦方面实现不间断循环聚焦. 不像之前的,要按一下按键聚焦一次. 3,ImageButton增加了按下的效果.按之前示例如下:,点击后背景变暗,有种风车旋转的感觉. 4,增

Android下调用jni时进行的转码操作的例子

之前帮人家解决乱码问题时回过一个帖子 现在总结在博客里 留起来 bool handleString(char* name) { jstring rtnname = 0; int slenname = strlen(name); unsigned short * namebuffer = 0; if( slenname == 0 ) rtnname = mEnv->NewStringUTF(name); else { wchar_t* wname = NULL; setlocale(LC_ALL,

驱动开发-虚拟摄像头开发——如何在C#编程中调用虚拟摄像头驱动

问题描述 虚拟摄像头开发--如何在C#编程中调用虚拟摄像头驱动 我现在在做一个C#的桌面软件,需要使用虚拟摄像头,查了些关于虚拟摄像头开发的资料,稍微了解了一点,但是还是不是很清楚具体该怎么着手做. 请问下如何对虚拟摄像头采用什么编程语言,什么开发工具比较合适,谢谢~