我如何区分 imageReader camera API 2 中的 NV21 和 YV12 编码?
How could I distinguish between NV21 and YV12 codification in imageReader camera API 2?
我正在开发自定义相机 API 2 应用程序,我注意到当我使用 ImageReader 回调时,捕获格式转换在某些设备上是不同的。
例如,在 Nexus 4 中运行不正常,在 Nexus5X 中看起来正常,这是输出。
我以这种形式初始化 ImageReader:
mImageReader = ImageReader.newInstance(320, 240, ImageFormat.YUV_420_888,2);
我的回调是简单的回调 ImageReader 回调。
mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable( ImageReader reader) {
try {
mBackgroundHandler.post(
new ImageController(reader.acquireNextImage())
);
}
catch(Exception e)
{
//exception
}
}
};
对于 Nexus 4:我遇到了这个错误。
D/qdgralloc: gralloc_lock_ycbcr: Invalid format passed: 0x32315659
当我尝试在两台设备上写入原始文件时,我得到了这些不同的图像。所以我了解到 Nexus 5X 图像具有 NV21 编码,而 Nexus 4 具有 YV12 编码。
我找到了 image format 的规范,我尝试在 ImageReader 中获取格式。
有 YV12 和 NV21 选项,但显然,当我尝试获取格式时,我得到了 YUV_420_888 格式。
int test=mImageReader.getImageFormat();
那么有什么方法可以获取相机输入格式(NV21或YV12)来区分相机中的这种编码类型class?也许是 CameraCharacteristics?
提前致谢。
乌奈。
PD:我使用 OpenGL 显示 RGB 图像,并使用 Opencv 将其转换为 YUV_420_888.
YUV_420_888 是一个可以托管(以及其他)NV21 和 YV12 图像的包装器。您必须使用平面和步幅来访问各个颜色:
ByteBuffer Y = image.getPlanes()[0];
ByteBuffer U = image.getPlanes()[1];
ByteBuffer V = image.getPlanes()[2];
如果底层像素采用 NV21 格式(如 Nexus 4),则 pixelStride 将为 2,并且
int getU(image, col, row) {
return getPixel(image.getPlanes()[1], col/2, row/2);
}
int getPixel(plane, col, row) {
return plane.getBuffer().get(col*plane.getPixelStride() + row*plane.getRowStride());
}
我们采用半列半行,因为这是 U 和 V(色度)平面在 420 图像中的存储方式。
此代码仅供说明,效率非常低,您可能希望使用 get(byte[], int, int)
或通过片段着色器或通过本机代码中的 JNI 函数 GetDirectBufferAddress 批量访问像素。你不能使用的是方法 plane.array()
,因为这些平面保证是直接字节缓冲区。
这是从 YV12 转换为 NV21 的有用方法。
public static byte[] fromYV12toNV21(@NonNull final byte[] yv12,
final int width,
final int height) {
byte[] nv21 = new byte[yv12.length];
final int size = width * height;
final int quarter = size / 4;
final int vPosition = size; // This is where V starts
final int uPosition = size + quarter; // This is where U starts
System.arraycopy(yv12, 0, nv21, 0, size); // Y is same
for (int i = 0; i < quarter; i++) {
nv21[size + i * 2] = yv12[vPosition + i]; // For NV21, V first
nv21[size + i * 2 + 1] = yv12[uPosition + i]; // For Nv21, U second
}
return nv21;
}
我正在开发自定义相机 API 2 应用程序,我注意到当我使用 ImageReader 回调时,捕获格式转换在某些设备上是不同的。
例如,在 Nexus 4 中运行不正常,在 Nexus5X 中看起来正常,这是输出。
我以这种形式初始化 ImageReader:
mImageReader = ImageReader.newInstance(320, 240, ImageFormat.YUV_420_888,2);
我的回调是简单的回调 ImageReader 回调。
mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable( ImageReader reader) {
try {
mBackgroundHandler.post(
new ImageController(reader.acquireNextImage())
);
}
catch(Exception e)
{
//exception
}
}
};
对于 Nexus 4:我遇到了这个错误。
D/qdgralloc: gralloc_lock_ycbcr: Invalid format passed: 0x32315659
当我尝试在两台设备上写入原始文件时,我得到了这些不同的图像。所以我了解到 Nexus 5X 图像具有 NV21 编码,而 Nexus 4 具有 YV12 编码。
我找到了 image format 的规范,我尝试在 ImageReader 中获取格式。 有 YV12 和 NV21 选项,但显然,当我尝试获取格式时,我得到了 YUV_420_888 格式。
int test=mImageReader.getImageFormat();
那么有什么方法可以获取相机输入格式(NV21或YV12)来区分相机中的这种编码类型class?也许是 CameraCharacteristics?
提前致谢。
乌奈。 PD:我使用 OpenGL 显示 RGB 图像,并使用 Opencv 将其转换为 YUV_420_888.
YUV_420_888 是一个可以托管(以及其他)NV21 和 YV12 图像的包装器。您必须使用平面和步幅来访问各个颜色:
ByteBuffer Y = image.getPlanes()[0];
ByteBuffer U = image.getPlanes()[1];
ByteBuffer V = image.getPlanes()[2];
如果底层像素采用 NV21 格式(如 Nexus 4),则 pixelStride 将为 2,并且
int getU(image, col, row) {
return getPixel(image.getPlanes()[1], col/2, row/2);
}
int getPixel(plane, col, row) {
return plane.getBuffer().get(col*plane.getPixelStride() + row*plane.getRowStride());
}
我们采用半列半行,因为这是 U 和 V(色度)平面在 420 图像中的存储方式。
此代码仅供说明,效率非常低,您可能希望使用 get(byte[], int, int)
或通过片段着色器或通过本机代码中的 JNI 函数 GetDirectBufferAddress 批量访问像素。你不能使用的是方法 ,因为这些平面保证是直接字节缓冲区。plane.array()
这是从 YV12 转换为 NV21 的有用方法。
public static byte[] fromYV12toNV21(@NonNull final byte[] yv12,
final int width,
final int height) {
byte[] nv21 = new byte[yv12.length];
final int size = width * height;
final int quarter = size / 4;
final int vPosition = size; // This is where V starts
final int uPosition = size + quarter; // This is where U starts
System.arraycopy(yv12, 0, nv21, 0, size); // Y is same
for (int i = 0; i < quarter; i++) {
nv21[size + i * 2] = yv12[vPosition + i]; // For NV21, V first
nv21[size + i * 2 + 1] = yv12[uPosition + i]; // For Nv21, U second
}
return nv21;
}