实现Camera对象

来源:互联网 发布:量化交易python和r 编辑:程序博客网 时间:2024/05/21 14:02

既然已经建立了活动及预览Surface,现在我们准备好开始使用实际的Camera对象。

当创建Surface时,由于SurfaceHolder.Callback的存在,它将在代码中的触发调用surfaceCreated方法。此时可以通过调用Camera类上的静态方法open获得Camera对象。
Camera camera;
public void surfaceCreated(SurfaceHolder holder) {
camera = Camera.open();

随后,我们想要将预览显示设置为正在使用的SurfaceHolder,它通过回调提供给我们的方法。需要将该方法包装在try...catch块中,因为它可能会抛出IOException。如果发生了这种情况,那么我们会希望释放该Camera对象;否则,它将绑定摄像头的硬件资源,使其不能用于其他应用程序。
try
{
camera.setPreviewDisplay(holder);
}
catch (IOException exception)
{
camera.release();
}

最后,启动摄像头预览。
camera.startPreview();
}

相应地,在surfaceDestroyed中也需要释放该Camera对象。我们将首先调用stopPreview,以确保应该释放的资源都被清理。
public void surfaceDestroyed(SurfaceHolder holder) {
camera.stopPreview();
camera.release();
}

运行这段代码,您可能会发现预览有些奇怪。它会逆时针旋转预览图像90°,如图2-1所示。


 

产生这种旋转的原因是Camera对象假定方向是水平或横向模式。修正旋转的最简单方法是使活动以横向模式显示。为此,可以在活动的onCreate方法中添加以下代码。
@Override
public void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

现在摄像头预览将会正确地显示,如图2-2所示。但是,我们的应用程序现在被限定在横向模式。


 

1. 设置Camera对象的参数

前面提及,Camera类有一个嵌套的Camera.Parameters类。这个类有一系列重要的属性或设置,可以用来改变Camera对象运作的方式。其中一个现在能够帮助我们的参数可用来处理在预览时遇到的旋转/横向问题。

可以对Camera对象使用的Parameters做如下修改:
Camera.Parameters parameters = camera.getParameters();

parameters.set("some parameter", "some value");
// 或者
parameters.set("some parameter", some_int);

camera.setParameters(parameters);

此处有两个不同的通用Parameters.set方法。第一个方法的参数名称和值都采用字符串,而第二个方法的参数名称为字符串,但是值为整数。

应该在创建Camera对象和指定它的预览Surface之后立即在surfaceCreated方法中设置Parameters。

以下代码展示了如何使用Parameters请求Camera对象采用纵向方向而非横向方向。
public void surfaceCreated(SurfaceHolder holder) {
          camera = Camera.open();
          try {
               Camera.Parameters parameters = camera.getParameters();
               if (this.getResources().getConfiguration().orientation !=
                    Configuration.ORIENTATION_LANDSCAPE) {
                    //这是一个众所周知但未文档化的特性
                    parameters.set("orientation", "portrait");

                    //对于Android 2.2及以上版本
                    //camera.setDisplayOrientation(90);

                    //对于Android 2.2及以上版本取消注释
                    //parameters.setRotation(90);
               } else {
                    //这是一个众所周知但未文档化的特性
                    parameters.set("orientation", "landscape");

                    //对于Android 2.2及以上版本
                    //camera.setDisplayOrientation(0);

                    //对于Android 2.2及以上版本取消注释
                    //parameters.setRotation(0);
               }
               camera.setParameters(parameters);
               camera.setPreviewDisplay(holder);
          } catch (IOException exception) {
               camera.release();
               Log.v(LOGTAG,exception.getMessage());
          }
          camera.startPreview();
}

上述代码首先检查设备配置(通过调用Context.getResources().getConfiguration())以查看当前的方向。如果方向不是横向模式,那么它设置Camera.Parameters的“orientation”值为“portrait”。此外,调用Camera.Parameters的setRotation方法,并传入90°的参数。该方法在API Level 5(2.0版)和更高版本上可用,它实际上并不执行任何旋转;相反,它会告诉Camera对象在EXIF数据中指定该图像应该旋转90°显示。如果没有包含该信息,那么在其他应用程序中查看该图像时,它可能会侧面显示。

注意:以上所示的通过使用Camera.Parameters修改Camera对象旋转的方法用于Android 2.1和更早的版本。在Android 2.2中引入了Camera类的一个新方法setDisplayOrientation (int degrees)。该方法接受一个整数,表示图像应该旋转的度数。有效的度数为0、90、180和270。

大多数可以或应该修改的参数都有与它们相关联的特定方法。如同我们所看到的setRotation方法一样,这些方法遵循Java的获取器和设置器设计模式。例如,可以使用setFlashMode(Camera.Parameters.FLASH_MODE_AUTO)来设置Camera对象的闪光灯模式,同时可以使用getFlashMode()获得它的当前值,而无须使用通用的Parameters.set方法。

从Android 2.0开始,存在一个可用于展示的有趣参数,使用该参数可以修改颜色效果。对应的获取器和设置器方法是getColorEffect和setColorEffect。同时还存在一个getSupportedColorEffects方法,它返回一个String对象的列表,对应特定设备上所支持的各种效果。事实上,这种方法对于所有具有获取器和设置器方法的参数都存在,用于在使用某个功能之前确保所请求的功能是可用的。
Camera.Parameters parameters = camera.getParameters();
List<String> colorEffects = parameters.getSupportedColorEffects();
Iterator<String> cei = colorEffects.iterator();
while (cei.hasNext()) {
     String currentEffect = cei.next();
     Log.v("SNAPSHOT","Checking " + currentEffect);
     if (currentEffect.equals(Camera.Parameters.EFFECT_SOLARIZE)) {
          Log.v("SNAPSHOT","Using SOLARIZE");
          parameters.setColorEffect(Camera.Parameters.EFFECT_SOLARIZE);
          break;
     }
}
Log.v("SNAPSHOT","Using Effect:" + parameters.getColorEffect());
camera.setParameters(parameters);

在上述代码中,首先查询Camera.Parameters对象,以通过getSupportedColorEffects方法查看所支持的效果列表。然后,使用迭代器循环查询该效果列表,并判断其中是否有一个效果能够匹配我们想要的效果,在当前情况下是Camera.Parameters.EFFECT_SOLARIZE。如果该效果出现在列表中,那么它是获得支持的,我们可以继续操作,在Camera.Parameters对象上调用setColorEffect,并传入EFFECT_SOLARIZE常量。图2-3显示了使用中的Camera. Parameters.EFFECT_SOLARIZE效果。


 

其他可能的效果也以常量的形式在Camera.Parameters类中列出。

● EFFECT_NONE

● EFFECT_MONO

● EFFECT_NEGATIVE

● EFFECT_SOLARIZE

● EFFECT_SEPIA

● EFFECT_POSTERIZE

● EFFECT_WHITEBOARD

● EFFECT_BLACKBOARD

● EFFECT_AQUA

还存在用于抗条带(antibanding)、闪光灯模式(flash mode)、聚焦模式(focus mode),情景模式(scene mode)及白平衡(white balance)等参数的类似常量。

2. 更改摄像头预览大小

另一个在Camera.Parameters中特别有用的设置是能够设置预览大小。与其他的设置一样,首先将查询参数对象并获得所支持的值。在获得所支持的大小列表之后,就可以在设置之前通过遍历它来确保所想要的大小是否获得支持。

在这个示例中,我们不是指定一个精确的大小,而是选择接近但不超过一对常量的大小。图2-4展示了这个示例的输出。
...
public static final int LARGEST_WIDTH = 200;
public static final int LARGEST_HEIGHT= 200;
...


 

与所有的Camera.Parameters一样,在已经打开Camera对象并设置它的预览显示Surface之后,就可以在surfaceCreated中获取和设置它们。
public void surfaceCreated(SurfaceHolder holder) {
     camera = Camera.open();
     try {
        camera.setPreviewDisplay(holder);
        Camera.Parameters parameters = camera.getParameters();

我们将采用以下两个变量来记录小于但最接近上述常量的值。
        int bestWidth = 0;
        int bestHeight = 0;

然后,就可以获得设备所支持的所有大小的列表。这将返回一个Camera.Size对象的列表,可以对其进行循环遍历。
          List<Camera.Size> previewSizes = parameters.
getSupportedPreviewSizes();
          if (previewSizes.size() > 1)
          {
               Iterator<Camera.Size> cei = previewSizes.iterator();
               while (cei.hasNext())
               {
                    Camera.Size aSize = cei.next();

如果该列表中的当前大小大于保存的最佳大小,并且小于或等于LARGEST_WIDTH和LARGEST_HEIGHT常量,那么将在bestWidth和bestHeight变量中保存这个高度和宽度并继续检查。
                    Log.v("SNAPSHOT","Checking " + aSize.width + " x "
 + aSize.height);
                    if (aSize.width > bestWidth && aSize.width <= LARGEST_WIDTH
                        && aSize.height > bestHeight
                        && aSize.height <= LARGEST_HEIGHT) {
                        // 迄今为止,它是最大的大小,且不超过屏幕尺寸
                            bestWidth = aSize.width;
                            bestHeight = aSize.height;
                    }
               }

在遍历完所有支持的大小之后,必须确保获得了所需要的值。如果bestHeight和bestWidth变量等于0,那么没有发现任何与我们的需要相匹配的大小,或者只存在一种支持的大小,从而不应采取任何操作。反之,如果它们有值,那么将使用bestWidth和bestHeight变量调用Camera.Parameters对象上的setPreviewSize方法。

另外,还需要告诉摄像头预览SurfaceView对象(即cameraView)以该大小进行显示。如果不这么做,那么SurfaceView不会改变大小,且来自摄像头的预览图像会扭曲或质量非常低。
               if (bestHeight != 0 && bestWidth != 0) {
                 Log.v("SNAPSHOT", "Using " + bestWidth + " x " + bestHeight);
                 parameters.setPreviewSize(bestWidth, bestHeight);
               cameraView.setLayoutParams(new LinearLayout.LayoutParams(
bestWidth, bestHeight));
               }
          }
          camera.setParameters(parameters);

在设置该参数之后,剩余的工作就是关闭surfaceCreated方法。

   }  catch (IOException exception) {
       camera.release();
   }
}

3. 捕获和保存图像

要采用Camera类捕获图像,必须调用takePicture方法。该方法接受3个或4个参数,所有这些参数都是回调方法。takePicture方法的最简单形式是将所有的参数都设置为null。尽管能够捕获照片,但是不能获得它的引用。因此,至少应该实现一种回调方法。一种最安全的回调方法是Camera.PictureCallback.onPictureTaken。它确保会被调用,并且在压缩图像时被调用。为了利用该方法,我们将在活动中实现Camera.PictureCallback,并添加一个onPictureTaken方法。
public class SnapShot extends Activity implements
   SurfaceHolder.Callback, Camera.PictureCallback {

   public void onPictureTaken(byte[] data, Camera camera) {
   }

该onPictureTaken方法有两个参数:第一个是实际的JPEG图像数据的字节数组,第二个是捕获该图像的Camera对象的引用。

由于给定了实际的JPEG数据,因此为了保存它,只需要将其写入磁盘的某个位置。正如我们已经知道的那样,可以利用MediaStore指定它的位置和元数据。

当执行onPictureTaken方法时,可以调用Camera对象上的startPreview。当调用takePicture方法时预览已经自动暂停,并且这个方法会告诉我们,现在可以安全地重新启动它。
public void onPictureTaken(byte[] data, Camera camera) {
     Uri imageFileUri = getContentResolver().insert(Media.EXTERNAL_
CONTENT_URI, new ContentValues());
     try {
       OutputStream imageFileOS = getContentResolver().
openOutputStream(imageFileUri);
       imageFileOS.write(data);
       imageFileOS.flush();
       imageFileOS.close();

     }catch (FileNotFoundException e) {
     } catch (IOException e) {
     }
     camera.startPreview();
}

上述的代码片段向MediaStore中插入了一条新记录,并返回一个URI。然后,利用这个URI可以获得一个OutputStream,用于写入JPEG数据。这将在MediaStore指定的位置中创建一个文件,并将它链接到新的记录。

如果后面想要更新存储在MediaStore记录中的元数据,那么如同第1章所描述的一样,可以利用一个新的ContentValues对象对记录进行更新。
ContentValues contentValues = new ContentValues(3);
contentValues.put(Media.DISPLAY_NAME, "This is a test title");
contentValues.put(Media.DESCRIPTION, "This is a test description");
getContentResolver().update(imageFileUri,contentValues,null,null);

最后,必须实际调用Camera.takePicture。为此,需要设置预览屏幕为“可单击(clickable)”,同时在onClick方法中完成照相。

在活动中将实现一个OnClickListener,并设置SurfaceView的onClickListener为活动本身。然后,使用setClickable(true)设置SurfaceView为“可单击”。另外,需要设置SurfaceView为“可聚焦(focusable)”。默认情况下SurfaceView不可聚焦,因此必须使用setFocusable(true)对它进行显式的设置。同样,当处于“触摸模式”时,通常会禁用焦点,所以必须使用setFocusInTouchMode(true)对其进行显式的设置,使这种情况不会发生。
public class SnapShot extends Activity implements OnClickListener,
 SurfaceHolder.Callback, Camera.PictureCallback {
...
   public void onCreate(Bundle savedInstanceState) {
...
           cameraView.setFocusable(true);
           cameraView.setFocusableInTouchMode(true);
           cameraView.setClickable(true);
           cameraView.setOnClickListener(this);
    }
    public void onClick(View v) {
      camera.takePicture(null, null, null, this);
  }

4. 其他的Camera回调方法

除了Camera.PictureCallback之外,还有其他一些值得提及的回调方法。

● Camera.PreviewCallback:定义了onPreviewFrame(byte[] data, Camera camera) 方法,当存在预览帧(preview frame)时调用该方法。可以传入保存当前图像像素的字节数组。在Camera对象上,有3种不同的方式使用这个回调:

• setPreviewCallback(Camera.PreviewCallback):使用此方法注册一个Camera. PreviewCallback,这将确保在屏幕上显示一个新的预览帧时调用onPreviewFrame方法。传递到onPreviewFrame方法中的数据字节数组最有可能采用YUV格式。但是,Android 2.2是第一个包含了YUV格式解码器(YuvImage)的版本;在以前的版本中,必须手动完成解码。

• setOneShotPreviewCallback(Camera.PreviewCallback):利用Camera对象上的这个方法注册Camera.PreviewCallback,从而当下一幅预览图像可用时调用一次onPreviewFrame。同样,传递到onPreviewFrame方法的预览图像数据最有可能采用YUV格式。可以通过使用ImageFormat中的常量检查Camera. getParameters(). getPreviewFormat()返回的结果来确定这一点。

• setPreviewCallbackWithBuffer(Camera.PreviewCallback):在Android 2.2中引入了该方法,其与setPreviewCallback的工作方式相同,但要求指定一个字节数组作为缓冲区,用于预览图像数据。这是为了能够更好地管理处理预览图像时使用的内存。

● Camera.AutoFocusCallback:定义了onAutoFocus方法,当完成一个自动聚焦活动时调用它。通过传入此回调接口的一个实例,在调用Camera对象上的autoFocus方法时会触发自动聚焦。

● Camera.ErrorCallback:定义了onError方法,当发生一个Camera错误时调用它。有两个常量可用于与传入的错误代码进行比较:CAMERA_ERROR_UNKNOWN和CAMERA_ERROR_SERVER_DIED。

● Camera.OnZoomChangeListener:定义了onZoomChange方法,当正在进行或完成“平滑缩放”(慢慢缩小或放大)时调用它。在Android 2.2 (API Level 8)中引入了这个类和方法。

● Camera.ShutterCallback:定义了onShutter方法,当捕获图像时立刻调用它。

0 0
原创粉丝点击