Camera2 YUV_420_888のPixelフォーマットを取得する|Android開発
Camera2 APIでImageReader.newInstanceのformat
にImageFormat.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フォーマットの判定を行っています。