写在前面

最近在从零开始写一个移动端的AR系统,坑实在是太多了!!!整个项目使用了OpenCV第三方库,但对于摄像机来说,和原生Camera的方法基本相同。

实现

以OpenCV的JavaCameraView为例,首先需要定制自己的Camera,主要代码如下:
  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. import org.opencv.android.JavaCameraView;
  5.  
  6. import android.R.integer;
  7. import android.content.Context;
  8. import android.graphics.Rect;
  9. import android.graphics.RectF;
  10. import android.hardware.Camera;
  11. import android.hardware.Camera.AutoFocusCallback;
  12. import android.util.AttributeSet;
  13. import android.view.MotionEvent;
  14. import android.widget.Toast;
  15.  
  16. public class MTCameraView extends JavaCameraView implements AutoFocusCallback {
  17.  
  18. public MTCameraView(Context context, int attrs) {
  19. super(context, attrs);
  20. // TODO Auto-generated constructor stub
  21. }
  22.  
  23. public List<Camera.Size> getResolutionList() {
  24. return mCamera.getParameters().getSupportedPreviewSizes();
  25. }
  26.  
  27. public Camera.Size getResolution() {
  28. Camera.Parameters params = mCamera.getParameters();
  29. Camera.Size s = params.getPreviewSize();
  30. return s;
  31. }
  32.  
  33. public void setResolution(Camera.Size resolution) {
  34. disconnectCamera();
  35. connectCamera((int)resolution.width, (int)resolution.height);
  36. }
  37.  
  38. public void focusOnTouch(MotionEvent event) {
  39. Rect focusRect = calculateTapArea(event.getRawX(), event.getRawY(), 1f);
  40. Rect meteringRect = calculateTapArea(event.getRawX(), event.getRawY(), 1.5f);
  41.  
  42. Camera.Parameters parameters = mCamera.getParameters();
  43. parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
  44.  
  45. if (parameters.getMaxNumFocusAreas() > 0) {
  46. List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
  47. focusAreas.add(new Camera.Area(focusRect, 1000));
  48.  
  49. parameters.setFocusAreas(focusAreas);
  50. }
  51.  
  52. if (parameters.getMaxNumMeteringAreas() > 0) {
  53. List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
  54. meteringAreas.add(new Camera.Area(meteringRect, 1000));
  55.  
  56. parameters.setMeteringAreas(meteringAreas);
  57. }
  58.  
  59. mCamera.setParameters(parameters);
  60. mCamera.autoFocus(this);
  61. }
  62.  
  63. /**
  64. * Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000.
  65. */
  66. private Rect calculateTapArea(float x, float y, float coefficient) {
  67. float focusAreaSize = 300;
  68. int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();
  69.  
  70. int centerX = (int) (x / getResolution().width * 2000 - 1000);
  71. int centerY = (int) (y / getResolution().height * 2000 - 1000);
  72.  
  73. int left = clamp(centerX - areaSize / 2, -1000, 1000);
  74. int right = clamp(left + areaSize, -1000, 1000);
  75. int top = clamp(centerY - areaSize / 2, -1000, 1000);
  76. int bottom = clamp(top + areaSize, -1000, 1000);
  77.  
  78. return new Rect(left, top, right, bottom);
  79. }
  80.  
  81. private int clamp(int x, int min, int max) {
  82. if (x > max) {
  83. return max;
  84. }
  85. if (x < min) {
  86. return min;
  87. }
  88. return x;
  89. }
  90.  
  91. public void setFocusMode (Context item, int type){
  92. Camera.Parameters params = mCamera.getParameters();
  93. List<String> FocusModes = params.getSupportedFocusModes();
  94.  
  95. switch (type){
  96. case 0:
  97. if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO))
  98. params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
  99. else
  100. Toast.makeText(item, "Auto Mode not supported", Toast.LENGTH_SHORT).show();
  101. break;
  102. case 1:
  103. if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
  104. params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
  105. else
  106. Toast.makeText(item, "Continuous Mode not supported", Toast.LENGTH_SHORT).show();
  107. break;
  108. case 2:
  109. if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_EDOF))
  110. params.setFocusMode(Camera.Parameters.FOCUS_MODE_EDOF);
  111. else
  112. Toast.makeText(item, "EDOF Mode not supported", Toast.LENGTH_SHORT).show();
  113. break;
  114. case 3:
  115. if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_FIXED))
  116. params.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
  117. else
  118. Toast.makeText(item, "Fixed Mode not supported", Toast.LENGTH_SHORT).show();
  119. break;
  120. case 4:
  121. if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_INFINITY))
  122. params.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
  123. else
  124. Toast.makeText(item, "Infinity Mode not supported", Toast.LENGTH_SHORT).show();
  125. break;
  126. case 5:
  127. if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_MACRO))
  128. params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
  129. else
  130. Toast.makeText(item, "Macro Mode not supported", Toast.LENGTH_SHORT).show();
  131. break;
  132. }
  133.  
  134. mCamera.setParameters(params);
  135. }
  136.  
  137. public void setFlashMode (Context item, int type){
  138. Camera.Parameters params = mCamera.getParameters();
  139. List<String> FlashModes = params.getSupportedFlashModes();
  140.  
  141. switch (type){
  142. case 0:
  143. if (FlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO))
  144. params.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
  145. else
  146. Toast.makeText(item, "Auto Mode not supported", Toast.LENGTH_SHORT).show();
  147. break;
  148. case 1:
  149. if (FlashModes.contains(Camera.Parameters.FLASH_MODE_OFF))
  150. params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
  151. else
  152. Toast.makeText(item, "Off Mode not supported", Toast.LENGTH_SHORT).show();
  153. break;
  154. case 2:
  155. if (FlashModes.contains(Camera.Parameters.FLASH_MODE_ON))
  156. params.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
  157. else
  158. Toast.makeText(item, "On Mode not supported", Toast.LENGTH_SHORT).show();
  159. break;
  160. case 3:
  161. if (FlashModes.contains(Camera.Parameters.FLASH_MODE_RED_EYE))
  162. params.setFlashMode(Camera.Parameters.FLASH_MODE_RED_EYE);
  163. else
  164. Toast.makeText(item, "Red Eye Mode not supported", Toast.LENGTH_SHORT).show();
  165. break;
  166. case 4:
  167. if (FlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH))
  168. params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
  169. else
  170. Toast.makeText(item, "Torch Mode not supported", Toast.LENGTH_SHORT).show();
  171. break;
  172. }
  173.  
  174. mCamera.setParameters(params);
  175. }
  176.  
  177. @Override
  178. public void onAutoFocus(boolean arg0, Camera arg1) {
  179.  
  180. }
  181. }

在MainActivity中需要初始化MTCamera,并且实现OnTouchListener接口,以便在触摸屏幕时可以调用onTouch函数。其中主要代码如下:

  1. private MTCameraView mOpenCvCameraView;
  2.  
  3. public void init() {
  4. mOpenCvCameraView = new MTCameraView(this, -1);
  5. mOpenCvCameraView.setCvCameraViewListener(this);
  6. mOpenCvCameraView.setFocusable(true);
  7. mOpenCvCameraView.setOnTouchListener(MainActivity.this);
  8. mOpenCvCameraView.enableView();
  9.  
  10. FrameLayout frame = new FrameLayout(this);
  11. frame.addView(mOpenCvCameraView);
  12.  
  13. setContentView(frame);
  14. }
  15.  
  16. @Override
  17. public boolean onTouch(View arg0, MotionEvent arg1) {
  18. // TODO Auto-generated method stub
  19. mOpenCvCameraView.focusOnTouch(arg1);
  20. return true;
  21. }

init()函数是自定义的初始化函数,可以在onCreate时使用。由于这里需要使用OpenCV库,所以本项目是在加载完OpenCV库并判断成功后才调用init()函数的。

解释

在发生触摸事件时,MainActivity由于实现了OnTouchListener接口,因此会调用重写的onTouch函数,并把它的第二个参数MotionEvent传递给MTCamera,以便定位触摸位置。
MTCamera的focusOnTouch函数继续工作。它首先根据触摸位置计算对焦和测光(metering)区域的大小(通过calculateTapArea函数),然后创建新的Camera.Parameters,并设置摄像机的对焦模式为Auto。
然后,它分别判断该设备的相机是否支持设置对焦区域和测光区域,如果支持就分别为parameters设置之前计算好的聚焦和测光区域。
最后,让Camera自动对焦。
  • calculateTapArea函数

    这个函数主要实现从屏幕坐标系到对焦坐标系的转换。由MotionEvent.getRowX()得到的是以屏幕坐标系(即屏幕左上角为原点,右下角为你的当前屏幕分辨率,单位是一个像素)为准的坐标,而setFocusAreas接受的List<Area>中的每一个Area的范围是(-1000,-1000)到(1000, 1000),也就是说屏幕中心为原点,左上角为(-1000,-1000),右下角为(1000,1000)。注意,如果超出这个范围的话,会报setParemeters failed的错误哦!除此之外,我们还提前定义了一个对焦框(测光框)的大小,并且接受一个参数(第三个参数coefficient)作为百分比进行调节。

至此完成了触摸对焦的功能。

但是,可以发现MTCamera里还有很大部分代码,主要是两个函数setFocusMode和setFlashMode。这两个函数,主要是因为在项目中我的图像经常是模糊的,但不知道系统支持那么对焦模式。这时,就可以使用这两个函数进行测试。这还需要在MainActivity中添加菜单栏的代码,以便进行选择。代码如下:
  1. private List<Camera.Size> mResolutionList;
  2.  
  3. private MenuItem[] mResolutionMenuItems;
  4. private MenuItem[] mFocusListItems;
  5. private MenuItem[] mFlashListItems;
  6.  
  7. private SubMenu mResolutionMenu;
  8. private SubMenu mFocusMenu;
  9. private SubMenu mFlashMenu;
  10.  
  11. @Override
  12. public boolean onCreateOptionsMenu(Menu menu) {
  13. Log.i(TAG, "called onCreateOptionsMenu");
  14.  
  15. List<String> mFocusList = new LinkedList<String>();
  16. int idx =0;
  17.  
  18. mFocusMenu = menu.addSubMenu("Focus");
  19.  
  20. mFocusList.add("Auto");
  21. mFocusList.add("Continuous Video");
  22. mFocusList.add("EDOF");
  23. mFocusList.add("Fixed");
  24. mFocusList.add("Infinity");
  25. mFocusList.add("Makro");
  26. mFocusList.add("Continuous Picture");
  27.  
  28. mFocusListItems = new MenuItem[mFocusList.size()];
  29.  
  30. ListIterator<String> FocusItr = mFocusList.listIterator();
  31. while(FocusItr.hasNext()){
  32. // add the element to the mDetectorMenu submenu
  33. String element = FocusItr.next();
  34. mFocusListItems[idx] = mFocusMenu.add(2,idx,Menu.NONE,element);
  35. idx++;
  36. }
  37.  
  38. List<String> mFlashList = new LinkedList<String>();
  39. idx = 0;
  40.  
  41. mFlashMenu = menu.addSubMenu("Flash");
  42.  
  43. mFlashList.add("Auto");
  44. mFlashList.add("Off");
  45. mFlashList.add("On");
  46. mFlashList.add("Red-Eye");
  47. mFlashList.add("Torch");
  48.  
  49. mFlashListItems = new MenuItem[mFlashList.size()];
  50.  
  51. ListIterator<String> FlashItr = mFlashList.listIterator();
  52. while(FlashItr.hasNext()){
  53. // add the element to the mDetectorMenu submenu
  54. String element = FlashItr.next();
  55. mFlashListItems[idx] = mFlashMenu.add(3,idx,Menu.NONE,element);
  56. idx++;
  57. }
  58.  
  59. mResolutionMenu = menu.addSubMenu("Resolution");
  60. mResolutionList = mOpenCvCameraView.getResolutionList();
  61. mResolutionMenuItems = new MenuItem[mResolutionList.size()];
  62.  
  63. ListIterator<Camera.Size> resolutionItr = mResolutionList.listIterator();
  64. idx = 0;
  65. while(resolutionItr.hasNext()) {
  66. Camera.Size element = resolutionItr.next();
  67. mResolutionMenuItems[idx] = mResolutionMenu.add(1, idx, Menu.NONE,
  68. Integer.valueOf((int) element.width).toString() + "x" + Integer.valueOf((int) element.height).toString());
  69. idx++;
  70. }
  71.  
  72. return true;
  73. }
  74.  
  75. public boolean onOptionsItemSelected(MenuItem item) {
  76. Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
  77.  
  78. if (item.getGroupId() == 1)
  79. {
  80. int id = item.getItemId();
  81. Camera.Size resolution = mResolutionList.get(id);
  82. mOpenCvCameraView.setResolution(resolution);
  83. resolution = mOpenCvCameraView.getResolution();
  84. String caption = Integer.valueOf((int) resolution.width).toString() + "x" + Integer.valueOf((int) resolution.height).toString();
  85. Toast.makeText(this, caption, Toast.LENGTH_SHORT).show();
  86. }
  87. else if (item.getGroupId()==2){
  88.  
  89. int focusType = item.getItemId();
  90.  
  91. mOpenCvCameraView.setFocusMode(this, focusType);
  92. }
  93. else if (item.getGroupId()==3){
  94.  
  95. int flashType = item.getItemId();
  96.  
  97. mOpenCvCameraView.setFlashMode(this, flashType);
  98. }
  99.  
  100. return true;
  101. }

这样运行后,点击菜单就可以看见有三个菜篮列表:Focus(对焦模式),Flash(视频模式),Resolution(支持的分辨率)。对焦模式和视频模式中提供了几种常见的模式供选择,代码会判断当前设备是否支持该模式。而分辨率菜单栏会显示出当前设备支持的所有分辨率种类。

参考

[Android编程心得] Camera(OpenCV)自动对焦和触摸对焦的实现的更多相关文章

  1. 【转】[Android编程心得] Camera(OpenCV)自动对焦和触摸对焦的实现

    参考http://stackoverflow.com/questions/18460647/android-setfocusarea-and-auto-focus http://blog.csdn.n ...

  2. Android编程心得-在任意类中获取当前屏幕宽高

    进行Android编程时,很多时候都需要获取当前屏幕的宽度与高度,但是当我们需要在别的类中调用屏幕宽高时,直接用原来的方法是不行的,下面我来介绍如何在任意类中调用宽度高度的两种方法. public v ...

  3. Android编程心得-ListView的Item高亮显示的办法

    在我们使用ListView的时候,经常会遇到某一项(Item)需要高亮显示的情况,如下图,有人说当我们点击子项的时候会变亮,但有时候业务逻辑需要让ITEM根据条件自动变亮,下面我来介绍一下我自己的解决 ...

  4. Android编程心得-JSON使用心得(二)

    在Android开发中,我们经常会用到JSON来与网络数据进行交互,下面我来介绍如何对JSON数据进行解析与制造 1.当我们需要对如下JSON串进行制造时: { "download" ...

  5. Android编程心得-设计一个可重用的自定义Dialog

            我们在实际开发过程中,会遇到一个问题,我们的Dialog如果使用一般的方法进行设置调用出来,会有很多的重复代码,如何将Dialog按照自己的思路设计呢,并让其可重用呢,下面我来介绍一下 ...

  6. Android编程心得-图片自适应心得

    在Android 的开发过程中,我们知道存放图片资源的文件夹是drawable,与它类似的名字的文件夹drawble-hdpi,drawable-ldpi,drawable-mdpi,drawable ...

  7. Android编程心得-Service数据绑定初步

    在Android里,Service的数据绑定是一种重要的用法,我们知道Service与Activity一样是运行在当前应用进程的主线程里面的,他们之间交互的方式有多种,下面我来介绍一下如何使用数据绑定 ...

  8. Android编程心得-使用ActionBar+Fragment+ViewPager实现动态切换Menu效果

    1.首先上效果图 2.本例实现的效果主要适用于当前页面有多个页签时.进行Fragment切换时,能够利用不同的Menu样式与当前Fragment中的内容进行配合,能够大大添加复用性,看到效果图后,以下 ...

  9. Android编程心得-Handler与子线程的交互初步

    在编写项目的时候,本人发现一个关于线程与Handler很容易犯的错误. 我有两个Activity,一个Activity在后台创建了一个线程并且启动,这个线程对象对应的实体实在另外一个Activity的 ...

随机推荐

  1. php闭包类外操作私有属性

    Closure::bind() Closure::bindTo(); class person{ private $age; private $sex; public function __const ...

  2. gravity和layout_gravity的区别

    一.gravity和layout_gravity相同处 两者都是设置对齐方式的属性.内部的属性值相同. 根据英文意思也能理解其中的意思.如center_horizontal表示在水平方向上的位置为中间 ...

  3. js强大的日期格式化函数,不仅可以格式化日期,还可以查询星期,一年中第几天等

    js强大的日期格式化,timestamp支持10位或13位的时间戳,或是时间字符串,同时支持android ios的处理,不只是日期的格式化还有其它方法,比如获 获取某月有多少天 .获取某个日期在这一 ...

  4. strut2接收参数的三种方式

    strut2接收参数有三种方式(普通属性\领域对象\模型驱动),分别对三种进行一个总结: 一.普通属性 Jsp代码 <body> <h1>普通属性</h1> < ...

  5. Redis Cluster架构优化

    Redis Cluster架构优化 在<全面剖析Redis Cluster原理和应用>中,我们已经详细剖析了现阶段Redis Cluster的缺点: 无中心化架构 Gossip消息的开销 ...

  6. Objective-C语法概述

    Objective-C语法概述 简称OC 面向对象的C语言 完全兼容C语言 可以在OC里面混入C/C++代码 可以开发IOS和Mac OS X平台应用 语法预览 关键字 基本上都是以@开头(为了与C语 ...

  7. 【mybatis深度历险系列】延迟加载

    在前面的博文中,小编主要简单的介绍了mybatis中的高级映射,小伙伴们可以把mybatis和hibernate的因素进行对比,更加有利于理解.今天这篇博文,小编主要来简单介绍一下mybatis中的延 ...

  8. Swift基础之两指拉动图片变大变小

    我们在使用APP的时候,有时会发现有些图片可以通过两指进行放大.缩小,今天就实现这样的一种效果,比较简单,不喜勿喷.... var imageVi:UIImageView! = nil    var ...

  9. Spring之AOP模块

    AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的一个重要特征 Spring推荐使用接口编程 Spring提供三种拦截器:方法前拦截器.返回后拦截器.异 ...

  10. Mac 下安装node.js

    Node.js 简介 Node.js 是一个用Chrome's V8 JavaScript engine 构建的 JavaScript 运行时框架.Node.js 使用事件驱动.非阻塞式 I/O 模块 ...