Camera2 フォーカス座標の補正について|Android開発
Camera2 APIではフォーカス位置をCameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE
で取得できる矩形内の座標で指定します。
Camera2フォーカス設定の方法|Android開発では、この座標の調整が必要なことを述べていましたが、ここでは実際にその処理を説明します。
解説
CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE
で取得できる矩形はセンサの物理サイズです。
例えば、このサイズが4000x3000だったとします。この時のセンサのアスペクト比は4:3です。
センサの物理サイズ
次にプレビューに使用しているサイズが1920x1080だったとします。この時のプレビューのアスペクト比は16:9です。
プレビューサイズ
4:3のセンサに対して、16:9の領域を当てはめると、4:2.25(4000x2250)しか見えていないことになります。
重なっている部分がプレビューで見えている領域
なので、プレビューサイズ1920x1080の場合に見えているセンサの物理サイズ(=指定可能なフォーカス座標)は、(0, 375)-(3999, 2624)となります。
サンプルソース
- パラメータ
x
,y
はプレビュー領域の座標と思ってください。
上述の解説だと、(0, 0)-(1919, 1079)の範囲が指定されます。 PreviewSize
はプレビューサイズです。
上述の解説だと1920x1080。previewArraySize
が見えている物理センサの領域になります。
上述の解説だと(0, 375)-(3999, 2624)。mOpenCameraId
はオープン中のカメラIDです。
public boolean calculateFocusPoint(float x, float y, Point position) {
Rect activeArraySize;
try {
CameraManager cameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(mOpenCameraId);
activeArraySize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
} catch (CameraAccessException e) {
e.printStackTrace();
return false;
}
// センサー最大領域に対するプレビュー可視領域の縮小率を算出する.
PointF ratios = new PointF();
calculateReducingRatios(activeArraySize.width(), activeArraySize.height(), PreviewSize.x, PreviewSize.y, ratios);
// センサーに対するプレビューの可視領域を設定する.
int previewArrayWidth = (int)(activeArraySize.width() * ratios.x);
int previewArrayHeight = (int)(activeArraySize.height() * ratios.y);
int left = (activeArraySize.width() - previewArrayWidth) / 2;
int top = (activeArraySize.height() - previewArrayHeight) / 2;
int right = left + previewArrayWidth;
int bottom = top + previewArrayHeight;
Rect previewArraySize = new Rect(left, top, right, bottom);
// 座標をプレビュー領域の割合(0.0~1.0)にする.
float x_per = x / PreviewSize.x;
float y_per = y / PreviewSize.y;
// フォーカス指定する座標
position.set(previewArraySize.left + (int)(previewArraySize.width() * x_per),
previewArraySize.top + (int)(previewArraySize.height() * y_per));
return true;
}
/**
* base 領域に対する矩形の割合(縮小率)を算出する.(アスペクト比を考慮した計算)
* @param baseWidth 基準となる矩形の幅.
* @param baseHeight 基準となる矩形の高さ.
* @param width 算出対象の幅.
* @param height 算出対象の高さ.
* @param ratios 縮小率
*/
private static void calculateReducingRatios(int baseWidth, int baseHeight, int width, int height, PointF ratios) {
BigDecimal baseAspect = new BigDecimal((double) baseHeight / baseWidth);
BigDecimal targetAspect = new BigDecimal((double) height / width);
if (baseAspect.equals(targetAspect)) {
ratios.set(1.0f, 1.0f);
return;
}
// widthLcm:幅の最小公倍数.
int widthLcm = getLCM(baseWidth, width);
int baseRatio = widthLcm / baseWidth;
int outputRatio = widthLcm / width;
if (targetAspect.compareTo(baseAspect) < 0) {
ratios.set(1.0f, (float) (height * outputRatio) / (float) (baseHeight * baseRatio));
} else {
ratios.set((float) (baseHeight * baseRatio) / (float) (height * outputRatio), 1.0f);
}
}
/**
* 最大公約数を求める.
* @param a 値1.
* @param b 値2.
* @return a と b の最大公約数.
*/
private static int getGCD(int a, int b) {
// swap.
if (a > b) {
int temp = a;
a = b;
b = temp;
}
while (a != 0) {
int temp = a;
a = b % a;
b = temp;
}
return b;
}
/**
* 最小公倍数を求める.
* @param a 値1.
* @param b 値2.
* @return a と b の最小公倍数.
*/
private static int getLCM(int a, int b) {
return (a * b) / getGCD(a, b);
}