您的位置:首页 > 移动开发 > Android开发

Android相机实时自动对焦的完美实现

2016-07-20 16:47 597 查看


Android相机实时自动对焦的完美实现

想写这篇文章很久了,去年十月份接触了相机这一块的知识,由于android碎片化严重,而且各大厂商极有可能去修改相关API的实现,其中遇到了不少坑,包括实时相机高斯模糊,自动对焦的兼容问题,以及一系列性能问题。换过很多搜索引擎,访问过很多网站,访问过很多网站,拜读过很多代码,没有发现对于相机实时自动对焦特别完美的实现方式。现对相机的自动对焦问题单独做一个记录,算是对这部分的一个总结。也希望后人在这部分能够快速地解决问题,不必浪费过多的时间。测试手机包括:MX4 pro,小米4,华为荣耀3C等等。

参考过@yanzi1225627 大神的一些做法,测试结果不是特别满意。


一,一些对焦方案的尝试


1,autoFocus()的尝试:

private Camera.AutoFocusCallback mAutoFocusCallback;
mAutoFocusCallback = new Camera.AutoFocusCallback() {

public void onAutoFocus(boolean success, Camera camera) {
// TODO Auto-generated method stub
if(success){
myCamera.setOneShotPreviewCallback(null);
Toast.makeText(TestPhotoActivity.this,
"自动聚焦成功" , Toast.LENGTH_SHORT).show();
}
}
};
myCamera.autoFocus(mAutoFocusCallback);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在一部分手机上,始终只对焦一次,也就是说根本不能实现。即使是在按下拍照的时候去调用一次对焦,等对焦成功后再进行拍照,实现的效果也不是很完美。 

还见部分博客把autoFocus()方法放在Camera预览SurfaceView的surfaceChanged()中的一些实现,发现也只对焦了一次。


2,设置对焦模式FOCUS_MODE_CONTINUOUS_PICTURE

/**
* Continuous auto focus mode intended for taking pictures. The camera
* continuously tries to focus. The speed of focus change is more
* aggressive than {@link #FOCUS_MODE_CONTINUOUS_VIDEO}. Auto focus
* starts when the parameter is set.
*
* <p>Applications can call {@link #autoFocus(AutoFocusCallback)} in
* this mode. If the autofocus is in the middle of scanning, the focus
* callback will return when it completes. If the autofocus is not
* scanning, the focus callback will immediately return with a boolean
* that indicates whether the focus is sharp or not. The apps can then
* decide if they want to take a picture immediately or to change the
* focus mode to auto, and run a full autofocus cycle. The focus
* position is locked after autoFocus call. If applications want to
* resume the continuous focus, cancelAutoFocus must be called.
* Restarting the preview will not resume the continuous autofocus. To
* stop continuous focus, applications should change the focus mode to
* other modes.
*
* @see #FOCUS_MODE_CONTINUOUS_VIDEO
*/
public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
1

在大部分手机上实现了自动对焦,而且效果还不错,而且不需要去额外调用方法。但是测试后发现小米4机型不会自动对焦。


3,设置对焦模式为FOCUS_MODE_CONTINUOUS_VIDEO

/**
* Continuous auto focus mode intended for video recording. The camera
* continuously tries to focus. This is the best choice for video
* recording because the focus changes smoothly . Applications still can
* call {@link #takePicture(Camera.ShutterCallback,
* Camera.PictureCallback, Camera.PictureCallback)} in this mode but the
* subject may not be in focus. Auto focus starts when the parameter is
* set.
*
* <p>Since API level 14, applications can call {@link
* #autoFocus(AutoFocusCallback)} in this mode. The focus callback will
* immediately return with a boolean that indicates whether the focus is
* sharp or not. The focus position is locked after autoFocus call. If
* applications want to resume the continuous focus, cancelAutoFocus
* must be called. Restarting the preview will not resume the continuous
* autofocus. To stop continuous focus, applications should change the
* focus mode to other modes.
*
* @see #FOCUS_MODE_CONTINUOUS_PICTURE
*/
public static final String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
1

经过测试,发现大部分手机可以连续对焦,但是在对焦过程中屏幕会连续闪烁,而且体验极其不好。魅族MX4不支持此种方式的对焦。也就是说第二,第三种方案都要放弃。


4,触摸对焦

本来在一番焦头烂额后准备妥协,先把触摸对焦实现吧。基本思路是支持定点对焦,就调用定点对焦,否则调用autoFocus()。
/**
* 手动聚焦
*
* @param point 触屏坐标
*/
protected boolean onFocus(Point point, Camera.AutoFocusCallback callback) {
if (mCamera == null) {
return false;
}

Camera.Parameters parameters = null;
try {
parameters = mCamera.getParameters();
} catch (Exception e) {
e.printStackTrace();
return false;
}
//不支持设置自定义聚焦,则使用自动聚焦,返回

if(Build.VERSION.SDK_INT >= 14) {

if (parameters.getMaxNumFocusAreas() <= 0) {
return focus(callback);
}

Log.i(TAG, "onCameraFocus:" + point.x + "," + point.y);

//定点对焦
List<Camera.Area> areas = new ArrayList<Camera.Area>();
int left = point.x - 300;
int top = point.y - 300;
int right = point.x + 300;
int bottom = point.y + 300;
left = left < -1000 ? -1000 : left;
top = top < -1000 ? -1000 : top;
right = right > 1000 ? 1000 : right;
bottom = bottom > 1000 ? 1000 : bottom;
areas.add(new Camera.Area(new Rect(left, top, right, bottom), 100));
parameters.setFocusAreas(areas);
try {
//本人使用的小米手机在设置聚焦区域的时候经常会出异常,看日志发现是框架层的字符串转int的时候出错了,
//目测是小米修改了框架层代码导致,在此try掉,对实际聚焦效果没影响
mCamera.setParameters(parameters);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return false;
}
}

return focus(callback);
}

private boolean focus(Camera.AutoFocusCallback callback) {
try {
mCamera.autoFocus(callback);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63


二,自动对焦方案

在实现无力的情况下,打开了其他已经实现自定义相机而且能够完美对焦的app,一番操作后,发现很多app都是在我移动手机或者有轻微晃动才进行了第二次对焦,等等,这不就是基于传感器实现的吗?

我们完全可以判断手机的运动状态啊,比如静止和移动。在移动一定时间后去对焦。 

核心代码:
package com.jerry.sweetcamera;

import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

import java.util.Calendar;

/**
* 加速度控制器  用来控制对焦
*
* @author jerry
* @date 2015-09-25
*/
public class SensorControler implements IActivityLifiCycle, SensorEventListener {
public static final String TAG = "SensorControler";
private SensorManager mSensorManager;
private Sensor mSensor;

private int mX, mY, mZ;
private long lastStaticStamp = 0;
Calendar mCalendar;

boolean isFocusing = false;
boolean canFocusIn = false;  //内部是否能够对焦控制机制
boolean canFocus = false;

public static final int DELEY_DURATION = 500;

public static final int STATUS_NONE = 0;
public static final int STATUS_STATIC = 1;
public static final int STATUS_MOVE = 2;
private int STATUE = STATUS_NONE;

private CameraFocusListener mCameraFocusListener;

private static SensorControler mInstance;

private int foucsing = 1;  //1 表示没有被锁定 0表示被锁定

private SensorControler() {
mSensorManager = (SensorManager) SweetApplication.CONTEXT.getSystemService(Activity.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// TYPE_GRAVITY
}

public static SensorControler getInstance() {
if (mInstance == null) {
mInstance = new SensorControler();
}
return mInstance;
}

public void setCameraFocusListener(CameraFocusListener mCameraFocusListener) {
this.mCameraFocusListener = mCameraFocusListener;
}

@Override
public void onStart() {
restParams();
canFocus = true;
mSensorManager.registerListener(this, mSensor,
SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
public void onStop() {
mSensorManager.unregisterListener(this, mSensor);
canFocus = false;
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}

@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor == null) {
return;
}

if (isFocusing) {
restParams();
return;
}

if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
int x = (int) event.values[0];
int y = (int) event.values[1];
int z = (int) event.values[2];
mCalendar = Calendar.getInstance();
long stamp = mCalendar.getTimeInMillis();// 1393844912

int second = mCalendar.get(Calendar.SECOND);// 53

if (STATUE != STATUS_NONE) {
int px = Math.abs(mX - x);
int py = Math.abs(mY - y);
int pz = Math.abs(mZ - z);
//                Log.d(TAG, "pX:" + px + "  pY:" + py + "  pZ:" + pz + "    stamp:"
//                        + stamp + "  second:" + second);
double value = Math.sqrt(px * px + py * py + pz * pz);
if (value > 1.4) {
//                    textviewF.setText("检测手机在移动..");
//                    Log.i(TAG,"mobile moving");
STATUE = STATUS_MOVE;
} else {
//                    textviewF.setText("检测手机静止..");
//                    Log.i(TAG,"mobile static");
//上一次状态是move,记录静态时间点
if (STATUE == STATUS_MOVE) {
lastStaticStamp = stamp;
canFocusIn = true;
}

if (canFocusIn) {
if (stamp - lastStaticStamp > DELEY_DURATION) {
//移动后静止一段时间,可以发生对焦行为
if (!isFocusing) {
canFocusIn = false;
//                                onCameraFocus();
if (mCameraFocusListener != null) {
mCameraFocusListener.onFocus();
}
//                                Log.i(TAG,"mobile focusing");
}
}
}

STATUE = STATUS_STATIC;
}
} else {
lastStaticStamp = stamp;
STATUE = STATUS_STATIC;
}

mX = x;
mY = y;
mZ = z;
}
}

private void restParams() {
STATUE = STATUS_NONE;
canFocusIn = false;
mX = 0;
mY = 0;
mZ = 0;
}

/**
* 对焦是否被锁定
*
* @return
*/
public boolean isFocusLocked() {
if(canFocus) {
return foucsing <= 0;
}
return false;
}

/**
* 锁定对焦
*/
public void lockFocus() {
isFocusing = true;
foucsing--;
Log.i(TAG, "lockFocus");
}

/**
* 解锁对焦
*/
public void unlockFocus() {
isFocusing = false;
foucsing++;
Log.i(TAG, "unlockFocus");
}

public void restFoucs() {
foucsing = 1;
}

public interface CameraFocusListener {
void onFocus();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

Start检测手机是否移动对焦几秒后yes

onSensorChanged()来判断手机的运动状态,自动去调用mCameraFocusListener.onFocus(); 

在mCameraFocusListener中可以调用触摸对焦的方法,这样基本上可以兼容大部分手机的自动对焦功能,而且可以比较好的控制对焦的显示以及对焦区域。foucsing用于对焦的计数,用来锁定对焦。

具体实现可以参考: 
https://github.com/huweigoodboy/SweetCamera
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: