写在前面

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

实现

以OpenCV的JavaCameraView为例,首先需要定制自己的Camera,主要代码如下:
import java.util.ArrayList;
import java.util.List; import org.opencv.android.JavaCameraView; import android.R.integer;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Toast; public class MTCameraView extends JavaCameraView implements AutoFocusCallback { public MTCameraView(Context context, int attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} public List<Camera.Size> getResolutionList() {
return mCamera.getParameters().getSupportedPreviewSizes();
} public Camera.Size getResolution() {
Camera.Parameters params = mCamera.getParameters();
Camera.Size s = params.getPreviewSize();
return s;
} public void setResolution(Camera.Size resolution) {
disconnectCamera();
connectCamera((int)resolution.width, (int)resolution.height);
} public void focusOnTouch(MotionEvent event) {
Rect focusRect = calculateTapArea(event.getRawX(), event.getRawY(), 1f);
Rect meteringRect = calculateTapArea(event.getRawX(), event.getRawY(), 1.5f); Camera.Parameters parameters = mCamera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); if (parameters.getMaxNumFocusAreas() > 0) {
List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
focusAreas.add(new Camera.Area(focusRect, 1000)); parameters.setFocusAreas(focusAreas);
} if (parameters.getMaxNumMeteringAreas() > 0) {
List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
meteringAreas.add(new Camera.Area(meteringRect, 1000)); parameters.setMeteringAreas(meteringAreas);
} mCamera.setParameters(parameters);
mCamera.autoFocus(this);
} /**
* Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000.
*/
private Rect calculateTapArea(float x, float y, float coefficient) {
float focusAreaSize = 300;
int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue(); int centerX = (int) (x / getResolution().width * 2000 - 1000);
int centerY = (int) (y / getResolution().height * 2000 - 1000); int left = clamp(centerX - areaSize / 2, -1000, 1000);
int right = clamp(left + areaSize, -1000, 1000);
int top = clamp(centerY - areaSize / 2, -1000, 1000);
int bottom = clamp(top + areaSize, -1000, 1000); return new Rect(left, top, right, bottom);
} private int clamp(int x, int min, int max) {
if (x > max) {
return max;
}
if (x < min) {
return min;
}
return x;
} public void setFocusMode (Context item, int type){
Camera.Parameters params = mCamera.getParameters();
List<String> FocusModes = params.getSupportedFocusModes(); switch (type){
case 0:
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO))
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
else
Toast.makeText(item, "Auto Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 1:
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
else
Toast.makeText(item, "Continuous Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 2:
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_EDOF))
params.setFocusMode(Camera.Parameters.FOCUS_MODE_EDOF);
else
Toast.makeText(item, "EDOF Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 3:
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_FIXED))
params.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
else
Toast.makeText(item, "Fixed Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 4:
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_INFINITY))
params.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
else
Toast.makeText(item, "Infinity Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 5:
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_MACRO))
params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
else
Toast.makeText(item, "Macro Mode not supported", Toast.LENGTH_SHORT).show();
break;
} mCamera.setParameters(params);
} public void setFlashMode (Context item, int type){
Camera.Parameters params = mCamera.getParameters();
List<String> FlashModes = params.getSupportedFlashModes(); switch (type){
case 0:
if (FlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO))
params.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
else
Toast.makeText(item, "Auto Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 1:
if (FlashModes.contains(Camera.Parameters.FLASH_MODE_OFF))
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
else
Toast.makeText(item, "Off Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 2:
if (FlashModes.contains(Camera.Parameters.FLASH_MODE_ON))
params.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
else
Toast.makeText(item, "On Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 3:
if (FlashModes.contains(Camera.Parameters.FLASH_MODE_RED_EYE))
params.setFlashMode(Camera.Parameters.FLASH_MODE_RED_EYE);
else
Toast.makeText(item, "Red Eye Mode not supported", Toast.LENGTH_SHORT).show();
break;
case 4:
if (FlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH))
params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
else
Toast.makeText(item, "Torch Mode not supported", Toast.LENGTH_SHORT).show();
break;
} mCamera.setParameters(params);
} @Override
public void onAutoFocus(boolean arg0, Camera arg1) { }
}

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

	private MTCameraView mOpenCvCameraView;

	public void init() {
mOpenCvCameraView = new MTCameraView(this, -1);
mOpenCvCameraView.setCvCameraViewListener(this);
mOpenCvCameraView.setFocusable(true);
mOpenCvCameraView.setOnTouchListener(MainActivity.this);
mOpenCvCameraView.enableView(); FrameLayout frame = new FrameLayout(this);
frame.addView(mOpenCvCameraView); setContentView(frame);
} @Override
public boolean onTouch(View arg0, MotionEvent arg1) {
// TODO Auto-generated method stub
mOpenCvCameraView.focusOnTouch(arg1);
return true;
}

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中添加菜单栏的代码,以便进行选择。代码如下:
    private List<Camera.Size> mResolutionList;

    private MenuItem[] mResolutionMenuItems;
private MenuItem[] mFocusListItems;
private MenuItem[] mFlashListItems; private SubMenu mResolutionMenu;
private SubMenu mFocusMenu;
private SubMenu mFlashMenu; @Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "called onCreateOptionsMenu"); List<String> mFocusList = new LinkedList<String>();
int idx =0; mFocusMenu = menu.addSubMenu("Focus"); mFocusList.add("Auto");
mFocusList.add("Continuous Video");
mFocusList.add("EDOF");
mFocusList.add("Fixed");
mFocusList.add("Infinity");
mFocusList.add("Makro");
mFocusList.add("Continuous Picture"); mFocusListItems = new MenuItem[mFocusList.size()]; ListIterator<String> FocusItr = mFocusList.listIterator();
while(FocusItr.hasNext()){
// add the element to the mDetectorMenu submenu
String element = FocusItr.next();
mFocusListItems[idx] = mFocusMenu.add(2,idx,Menu.NONE,element);
idx++;
} List<String> mFlashList = new LinkedList<String>();
idx = 0; mFlashMenu = menu.addSubMenu("Flash"); mFlashList.add("Auto");
mFlashList.add("Off");
mFlashList.add("On");
mFlashList.add("Red-Eye");
mFlashList.add("Torch"); mFlashListItems = new MenuItem[mFlashList.size()]; ListIterator<String> FlashItr = mFlashList.listIterator();
while(FlashItr.hasNext()){
// add the element to the mDetectorMenu submenu
String element = FlashItr.next();
mFlashListItems[idx] = mFlashMenu.add(3,idx,Menu.NONE,element);
idx++;
} mResolutionMenu = menu.addSubMenu("Resolution");
mResolutionList = mOpenCvCameraView.getResolutionList();
mResolutionMenuItems = new MenuItem[mResolutionList.size()]; ListIterator<Camera.Size> resolutionItr = mResolutionList.listIterator();
idx = 0;
while(resolutionItr.hasNext()) {
Camera.Size element = resolutionItr.next();
mResolutionMenuItems[idx] = mResolutionMenu.add(1, idx, Menu.NONE,
Integer.valueOf((int) element.width).toString() + "x" + Integer.valueOf((int) element.height).toString());
idx++;
} return true;
} public boolean onOptionsItemSelected(MenuItem item) {
Log.i(TAG, "called onOptionsItemSelected; selected item: " + item); if (item.getGroupId() == 1)
{
int id = item.getItemId();
Camera.Size resolution = mResolutionList.get(id);
mOpenCvCameraView.setResolution(resolution);
resolution = mOpenCvCameraView.getResolution();
String caption = Integer.valueOf((int) resolution.width).toString() + "x" + Integer.valueOf((int) resolution.height).toString();
Toast.makeText(this, caption, Toast.LENGTH_SHORT).show();
}
else if (item.getGroupId()==2){ int focusType = item.getItemId(); mOpenCvCameraView.setFocusMode(this, focusType);
}
else if (item.getGroupId()==3){ int flashType = item.getItemId(); mOpenCvCameraView.setFlashMode(this, flashType);
} return true;
}

这样运行后,点击菜单就可以看见有三个菜篮列表: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. angular-cli学习笔记 快速创建代码模板

    组件: ng g component component/demo 服务: ng g service service/news 然后在app.module.ts里引入 ng g service ser ...

  2. LintCode题解之斐波纳契数列

    直接使用递归的方法会导致TLE,加个缓存就好了: public class Solution { private Integer[] buff = new Integer[1000]; /* * @p ...

  3. Docker容器如何互联

    容器的连接(linking)系统是除了端口映射外,另一种跟容器中应用交互的方式. 该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息. 自定义容器命名 连接系统依据容器的名称来执 ...

  4. Rails中rspec测试xxx_path调用失败的解决

    首先要想生成类似于home_path,about_path之类的方法,必须在路由文件中添加对应方法: match '/help',to:"static_pages#help",vi ...

  5. 个人在AS的一些安卓适配经验

    具体的安卓适配http://blog.csdn.net/qfanmingyiq/article/details/53219812 AS在屏幕适配方面做的比eclipse做得好得多. 以下AS中的一些具 ...

  6. Apache ActiveMQ实战(1)-基本安装配置与消息类型

    ActiveMQ简介 ActiveMQ是一种开源的,实现了JMS1.1规范的,面向消息(MOM)的中间件,为应用程序提供高效的.可扩展的.稳定的和安全的企业级消息通信.ActiveMQ使用Apache ...

  7. 关于 linux中TCP数据包(SKB)序列号的小笔记

    关于  SKB序列号的小笔记 为了修改TCP协议,现在遇到了要改动tcp分组的序列号,但是只是在tcp_sendmsg函数中找到了SKB的end_seq  一直没有找到seq 不清楚在那里初始化了,就 ...

  8. 如何获得Android手机的软件安装列表

    Android的PackageManager类用于检索目前安装在设备上的应用软件包的信息.你可以通过调用getpackagemanager()得到PackageManager类的一个实例.对查询和操作 ...

  9. ROS机器人程序设计(原书第2版)补充资料 (肆) 第四章 在ROS下使用传感器和执行器

    ROS机器人程序设计(原书第2版)补充资料 (肆) 第四章 在ROS使用传感器和执行器 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中使用. 第四 ...

  10. ubuntu cpu频率控制

    安装cpufrequtils:  sudo apt-get install cpufrequtils 查看cpu:                 sudo cpufreq-info 设置cpu模式: ...