Camera2 YUV_420_888のPixelフォーマットを取得する|Android開発

      2018/06/14

Camera2 APIImageReader.newInstanceformatImageFormat.YUV_420_888を指定した場合、端末によって取得されるPixelフォーマット下記のいずれかになります。

  • NV12
  • NV21
  • I420

NV21が一番多くの端末で使用されているようです。

下記はPixelフォーマットの判定方法サンプルになります。

サンプルコード

import android.media.Image;

import java.nio.ByteBuffer;

public class PixelFormatAnalyzer {
    /** YUV420 SemiPlanar */
    public static final int NV12 = 1;
    /** YVU420 SemiPlanar */
    public static final int NV21 = 2;
    /** YUV420 Planar */
    public static final int I420 = 3;

    private static long getByteBufferAddress(final ByteBuffer buffer) {
        try {
            final java.lang.reflect.Field field =
                java.nio.Buffer.class.getDeclaredField("effectiveDirectAddress");
            field.setAccessible(true);
            return field.getLong(buffer);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * SemiPlanar の Pixel フォーマットを取得する.
     * @param uvBuffer UV格納域の先頭アドレス.
     * @param vuBuffer VU格納域の先頭アドレス.
     * @return Pixel フォーマット.
     */
    private static int getPixelFormatNV(long uvBuffer, long vuBuffer) {
        if (uvBuffer < vuBuffer) {
            return NV12;
        }
        return NV21;
    }

    /**
     * Image image の Pixel フォーマットを取得する.
     * @param image 解析対象の Image (ImageFormat.YUV_420_888 指定).
     * @return Pixel フォーマット.
     */
    public static int getPixelFormat(Image image) {
        int stride = image.getPlanes()[1].getPixelStride();
        if (stride == 1) {
            return I420;
        }

        ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
        ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
        return getPixelFormatNV(getByteBufferAddress(uBuffer),
                                getByteBufferAddress(vBuffer));
    }
}

解説

Image.getPlanesで取得される配列は

  • [0]:Y
  • [1]:U(UV)
  • [2]:V(VU)

の情報となっています。
ここでUまたはVのPixelStrideが1であれば、Planar=I420になります。

PixelStrideが2の場合、SemiPlanarになります。
この場合、UVとVUのBufferは同じ領域を参照しています。但し、それぞれUまたはVから始まるアドレスになります。

アドレス格納イメージ

フォーマット UVアドレス VUアドレス
NV12 0x00001000 0x00001001
NV21 0x00001001 0x00001000

なので、どちらのアドレスが先になるかで、Pixelフォーマットの判定を行っています。

 - Android