• 【转】[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(支持的分辨率)。对焦模式和视频模式中提供了几种常见的模式供选择,代码会判断当前设备是否支持该模式。而分辨率菜单栏会显示出当前设备支持的所有分辨率种类。
     
     
     

    参考

     
  • 相关阅读:
    浅出Java Socket 编程
    WPF指南之一(WPF的结构)
    使用URL访问网络资源
    WPF指南之三(XAML的名字空间)
    多线程并发思考文件加锁
    关于kindeditor上传图片出现"服务器发生故障"的解决办法
    isset function of PHP
    JSON字符串传到后台PHP处理的问题
    isset function of PHP
    (转)Linux利器 strace
  • 原文地址:https://www.cnblogs.com/exmyth/p/5125223.html
Copyright © 2020-2023  润新知