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

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

 

http://blog.csdn.net/candycat1992/article/details/21617741

 

 

 

 

写在前面

 
最近在从零开始写一个移动端的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(支持的分辨率)。对焦模式和视频模式中提供了几种常见的模式供选择,代码会判断当前设备是否支持该模式。而分辨率菜单栏会显示出当前设备支持的所有分辨率种类。
 
 
 

参考

 
posted @ 2016-01-12 18:14  牧之丨  阅读(788)  评论(0编辑  收藏  举报