Camera2 フォーカス座標の補正について|Android開発

      2018/05/24

Camera2 APIではフォーカス位置をCameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZEで取得できる矩形内の座標で指定します。
Camera2フォーカス設定の方法|Android開発では、この座標の調整が必要なことを述べていましたが、ここでは実際にその処理を説明します。

解説

CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZEで取得できる矩形はセンサの物理サイズです。
例えば、このサイズが4000×3000だったとします。この時のセンサのアスペクト比は4:3です。
センサの物理サイズ
センサの物理サイズ

次にプレビューに使用しているサイズが1920×1080だったとします。この時のプレビューのアスペクト比は16:9です。
プレビューサイズ
プレビューサイズ

4:3のセンサに対して、16:9の領域を当てはめると、4:2.25(4000×2250)しか見えていないことになります。
重なっている部分がプレビューで見えている領域
使用領域
なので、プレビューサイズ1920×1080の場合に見えているセンサの物理サイズ(=指定可能なフォーカス座標)は、(0, 375)-(3999, 2624)となります。

サンプルソース

  • パラメータx,yはプレビュー領域の座標と思って下さい。
    上述の解説だと、(0, 0)-(1919, 1079)の範囲が指定されます。
  • PreviewSizeはプレビューサイズです。
    上述の解説だと1920×1080。
  • 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);
}

 - Android