在解決項目中相機(jī)某些機(jī)型無法自動對焦的問題時,在網(wǎng)上找到了一些資料蚯涮,寫下解決問題過程坯临,以備查看。
Android相機(jī)實時自動對焦的完美實現(xiàn)
Android圖像濾鏡框架GPUImage從配置到應(yīng)用
GPUImage for Android
加速度控制器
當(dāng)設(shè)備移動時恋昼,認(rèn)定需要對焦看靠,然后調(diào)用CameraFocusListener 接口的onFocus()方法。
/**
* 加速度控制器 用來控制對焦
* @author zuo
* @date 2018/5/9 14:34
*/
public class SensorController implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mSensor;
private static SensorController mInstance;
private CameraFocusListener mCameraFocusListener;
public static final int STATUS_NONE = 0;
public static final int STATUS_STATIC = 1;
public static final int STATUS_MOVE = 2;
private int mX, mY, mZ;
private int STATUE = STATUS_NONE;
boolean canFocus = false;
boolean canFocusIn = false;
boolean isFocusing = false;
Calendar mCalendar;
private final double moveIs = 1.4;
private long lastStaticStamp = 0;
public static final int DELAY_DURATION = 500;
private SensorController(Context context) {
mSensorManager = (SensorManager) context.getSystemService(Activity.SENSOR_SERVICE);
if (mSensorManager!=null){
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
start();
}
public static SensorController getInstance(Context context) {
if (mInstance == null) {
mInstance = new SensorController(context);
}
return mInstance;
}
public void setCameraFocusListener(CameraFocusListener mCameraFocusListener) {
this.mCameraFocusListener = mCameraFocusListener;
}
public void start() {
restParams();
canFocus = true;
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
public void stop() {
mSensorManager.unregisterListener(this, mSensor);
canFocus = false;
}
@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();
int second = mCalendar.get(Calendar.SECOND);
if (STATUE != STATUS_NONE) {
int px = Math.abs(mX - x);
int py = Math.abs(mY - y);
int pz = Math.abs(mZ - z);
double value = Math.sqrt(px * px + py * py + pz * pz);
if (value > moveIs) {
STATUE = STATUS_MOVE;
} else {
if (STATUE == STATUS_MOVE) {
lastStaticStamp = stamp;
canFocusIn = true;
}
if (canFocusIn) {
if (stamp - lastStaticStamp > DELAY_DURATION) {
//移動后靜止一段時間液肌,可以發(fā)生對焦行為
if (!isFocusing) {
canFocusIn = false;
// onCameraFocus();
if (mCameraFocusListener != null) {
mCameraFocusListener.onFocus();
}
}
}
}
STATUE = STATUS_STATIC;
}
} else {
lastStaticStamp = stamp;
STATUE = STATUS_STATIC;
}
mX = x;
mY = y;
mZ = z;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private void restParams() {
STATUE = STATUS_NONE;
canFocusIn = false;
mX = 0;
mY = 0;
mZ = 0;
}
/**
* 對焦是否被鎖定
* @return
*/
public boolean isFocusLocked() {
return canFocus && isFocusing;
}
/**
* 鎖定對焦
*/
public void lockFocus() {
isFocusing = true;
}
/**
* 解鎖對焦
*/
public void unlockFocus() {
isFocusing = false;
}
public void restFocus() {
isFocusing = false;
}
public interface CameraFocusListener {
/**
* 相機(jī)對焦中
*/
void onFocus();
}
}
自定義相機(jī)
1挟炬、初始化相機(jī)時,進(jìn)行加速度監(jiān)聽
public Camera1(Activity activity, Callback callback, PreviewImpl preview) {
super(callback, preview);
this.mActivity = activity;
preview.setCallback(new PreviewImpl.Callback() {
@Override
public void onSurfaceChanged() {
if (mCamera != null) {
setUpPreview();
adjustCameraParameters();
}
}
});
sensorController = SensorController.getInstance(mActivity);
sensorController.setCameraFocusListener(new SensorController.CameraFocusListener() {
@Override
public void onFocus() {
if (mCamera != null) {
DisplayMetrics mDisplayMetrics = mActivity.getApplicationContext().getResources()
.getDisplayMetrics();
int mScreenWidth = mDisplayMetrics.widthPixels;
if (!sensorController.isFocusLocked()) {
if (newFocus(mScreenWidth / 2, mScreenWidth / 2)) {
sensorController.lockFocus();
}
}
}
}
});
}
sensorController.start();
2嗦哆、自動對焦代碼
private boolean isFocusing;
private boolean newFocus(int x, int y) {
//正在對焦時返回
if (mCamera == null || isFocusing) {
return false;
}
isFocusing = true;
setMeteringRect(x, y);
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.cancelAutoFocus(); // 先要取消掉進(jìn)程中所有的聚焦功能
try {
mCamera.setParameters(mCameraParameters);
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
設(shè)置感光區(qū)域谤祖,將手機(jī)屏幕點擊點對應(yīng)的感光矩形范圍映射到相機(jī)的感光矩形坐標(biāo)系
/**
* 設(shè)置感光區(qū)域
* 需要將屏幕坐標(biāo)映射到Rect對象對應(yīng)的單元格矩形
*
* @param x
* @param y
*/
private void setMeteringRect(int x, int y) {
if (mCameraParameters.getMaxNumMeteringAreas() > 0) {
List<Camera.Area> areas = new ArrayList<Camera.Area>();
Rect rect = new Rect(x - 100, y - 100, x + 100, y + 100);
int left = rect.left * 2000 / CameraUtil.screenWidth - 1000;
int top = rect.top * 2000 / CameraUtil.screenHeight - 1000;
int right = rect.right * 2000 / CameraUtil.screenWidth - 1000;
int bottom = rect.bottom * 2000 / CameraUtil.screenHeight - 1000;
// 如果超出了(-1000,1000)到(1000, 1000)的范圍,則會導(dǎo)致相機(jī)崩潰
left = left < -1000 ? -1000 : left;
top = top < -1000 ? -1000 : top;
right = right > 1000 ? 1000 : right;
bottom = bottom > 1000 ? 1000 : bottom;
Rect area1 = new Rect(left, top, right, bottom);
//只有一個感光區(qū)老速,直接設(shè)置權(quán)重為1000了
areas.add(new Camera.Area(area1, 1000));
mCameraParameters.setMeteringAreas(areas);
}
}
3粥喜、自動對焦回調(diào)事件
private Handler mHandler = new Handler();
private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//一秒之后才能再次對焦
isFocusing = false;
sensorController.unlockFocus();
}
}, 1000);
}
};
4、關(guān)閉監(jiān)聽事件
/**
* 關(guān)閉攝像頭,關(guān)掉加速度監(jiān)聽
*
*/
@Override
public void stop(boolean stopAll) {
sensorController.stop();
stopPreview();
releaseCamera();
}
測光和調(diào)焦
在某些攝像情景中橘券,自動調(diào)焦和測光可能不能達(dá)到設(shè)計結(jié)果额湘。從Android4.0(API Level 14)開始,你的Camera應(yīng)用程序能夠提供另外的控制允許應(yīng)用程序或用戶指定圖像中特定區(qū)域用于進(jìn)行調(diào)焦或光線級別的設(shè)置旁舰,并且把這些值傳遞給Camera硬件用于采集圖片或視頻锋华。
測光和調(diào)焦區(qū)域的工作與其他Camera功能非常類似,你可以通過Camera.Parameters對象中的方法來控制它們箭窜。
1毯焕、給Camera設(shè)置兩個測光區(qū)域
Camera.Area對象,包含兩個參數(shù):
- Rect對象磺樱,它用于指定Camera預(yù)覽窗口一塊矩形區(qū)域(測光區(qū)域)
- 一個權(quán)重值(weight)纳猫,它告訴Camera這塊指定區(qū)域應(yīng)該給予的測光或調(diào)焦計算的重要性等級,權(quán)重大的優(yōu)先級高竹捉,權(quán)重最高為1000
//獲取相機(jī)實例
Camera.Parameters params = mCamera.getParameters();
//檢查是否支持測光區(qū)域
if (params.getMaxNumMeteringAreas() > 0){
List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
//在圖像的中心指定一個測光區(qū)域
Rect areaRect1 = new Rect(-100, -100, 100, 100);
//設(shè)置權(quán)重為600芜辕,最高1000
meteringAreas.add(new Camera.Area(areaRect1, 600));
//圖像右上方的測光區(qū)域
Rect areaRect2 = new Rect(800, -1000, 1000, -800);
//設(shè)置權(quán)重為400
meteringAreas.add(new Camera.Area(areaRect2, 400));
//將測光區(qū)域設(shè)置給相機(jī)屬性
params.setMeteringAreas(meteringAreas);
}
mCamera.setParameters(params);
Rect對象,代表了一個2000x2000的單元格矩形活孩,它的坐標(biāo)對應(yīng)Camera圖像的位置關(guān)系可以參考下圖物遇,坐標(biāo)(-1000,-1000)代表Camera圖像的左上角憾儒,(1000,1000)代表Camera圖像的右下角询兴。
2、感光區(qū)的計算
在上面設(shè)置感光區(qū)的setMeteringRect()方法中起趾,為什么從手機(jī)屏幕坐標(biāo)系映射到相機(jī)的感光矩形坐標(biāo)系需要這樣計算诗舰?
Rect rect = new Rect(x - 100, y - 100, x + 100, y + 100);
int left = rect.left * 2000 / CameraUtil.screenWidth - 1000;
int top = rect.top * 2000 / CameraUtil.screenHeight - 1000;
int right = rect.right * 2000 / CameraUtil.screenWidth - 1000;
int bottom = rect.bottom * 2000 / CameraUtil.screenHeight - 1000;
-
首先,我們把手機(jī)的屏幕坐標(biāo)和Camera的感光矩陣坐標(biāo)對應(yīng)起來
如圖训裆,我用黑色線條繪制了手機(jī)的屏幕坐標(biāo)系眶根,用紅色線條繪制了Camera的感光矩陣坐標(biāo)系,Camera的感光矩陣坐標(biāo)系是一個2000x2000的單元格矩形边琉,(0,0)單元格在中心属百,(-1000,-1000)代表Camera圖像的左上角变姨,(1000,1000)代表Camera圖像的右下角族扰。
現(xiàn)在,我們點擊了手機(jī)屏幕上的(x,y)這個點定欧,并取這個點上下左右各100個單位的矩形作為要映射到Camera感光矩陣坐標(biāo)系上的感光(對焦)區(qū)域渔呵,就是上面代碼中的Rect rect = new Rect(x - 100, y - 100, x + 100, y + 100);
,好了砍鸠,我們要開始計算了扩氢。
-
計算,將手機(jī)屏幕坐標(biāo)系上的矩形映射到Camera感光矩陣坐標(biāo)系上
矩形Rect對象入?yún)⒌亩x:Rect(int left, int top, int right, int bottom)
爷辱,下面我們就用rect.left录豺、rect.top、rect.right饭弓、rect.bottom
來表示該矩形在手機(jī)屏幕坐標(biāo)上的數(shù)據(jù)巩检,用△left 、△top 示启、△right 兢哭、△bottom
表示該矩形在Camera感光矩陣坐標(biāo)上的距離(長度),用left 夫嗓、top 迟螺、right 、bottom
表示該矩形在Camera感光矩陣坐標(biāo)上的坐標(biāo)舍咖,進(jìn)行計算矩父,
//1、建立等價式
rect.left / width = △left / 2000 排霉,距離在兩個坐標(biāo)系上的長度比相同
left = △left - 1000 窍株,不管在第幾象限-1000之后的數(shù)據(jù)都是他的坐標(biāo)值
計算可得
△left = rect.left * 2000 / width
left = △left - 1000 = rect.left * 2000 / width -1000
也就是該矩形在Camera感光矩陣坐標(biāo)系上的 left 數(shù)值就等于 rect.left * 2000 / width -1000 ,width 就是手機(jī)屏幕的寬度,也就是上面代碼中的 int left = rect.left * 2000 / CameraUtil.screenWidth - 1000;
同理:
int top = rect.top * 2000 / CameraUtil.screenHeight - 1000;
int right = rect.right * 2000 / CameraUtil.screenWidth - 1000;
int bottom = rect.bottom * 2000 / CameraUtil.screenHeight - 1000;
好了球订,感光區(qū)的計算就是這樣了后裸!