我目前正在使用Javacv,它使用public void onPreviewFrame(byte[] data, Camera camera)
相机函数。
由于相机已被弃用,我一直在研究camera2和MediaProjection。这两个库都使用了ImageReader类。
目前,我使用以下代码实例化ImageReader
:
ImageReader.newInstance(DISPLAY_WIDTH, DISPLAY_HEIGHT, PixelFormat.RGBA_8888, 2);
并像这样附加一个OnImageAvailableListener
:
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
mBackgroundHandler.post(new processImage(reader.acquireNextImage()));
}
我尝试使用Javacv中的
RGBA_8888
格式,如此线程所述:https://github.com/bytedeco/javacv/issues/298,但对我无效。
因此,我考虑使用RenderScript将这些Image转换为默认输出格式为onPreviewFrame
函数中的NV21(YUV_420_SP)
格式(摄像机的默认输出格式),因为我在camera
库中使用了这种方法。我还阅读了一些帖子,例如这个和这个网站来进行转换,但这些对我无效,而且我担心它们太慢。 此外,我的C语言知识非常有限。 基本上看起来我需要反向操作https://developer.android.com/reference/android/renderscript/ScriptIntrinsicYuvToRGB.html 那么,如何将
Image
转换为与onPreviewFrame
函数输出匹配的字节数组,即NV21(YUV_420_SP)
格式?最好使用Renderscript,因为它更快。
编辑 1:
我尝试使用ImageFormat.YUV_420_888
,但无济于事。我一直收到像“生成器输出缓冲区格式0x1与ImageReader配置的缓冲区格式不匹配”这样的错误。我切换回PixelFormat.RGBA_8888
并发现ImageObject
中只有一个平面。该平面的字节缓冲区大小为width*height*4(分别为R、G、B、A一字节)。因此,我尝试将其转换为NV21
格式。我修改了这个答案的代码,以产生以下函数:
void RGBtoNV21(byte[] yuv420sp, byte[] argb, int width, int height) {
final int frameSize = width * height;
int yIndex = 0;
int uvIndex = frameSize;
int A, R, G, B, Y, U, V;
int index = 0;
int rgbIndex = 0;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
R = argb[rgbIndex++];
G = argb[rgbIndex++];
B = argb[rgbIndex++];
A = argb[rgbIndex++];
// RGB to YUV conversion according to
// https://en.wikipedia.org/wiki/YUV#Y.E2.80.B2UV444_to_RGB888_conversion
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor
// of 2 meaning for every 4 Y pixels there are 1 V and 1 U.
// Note the sampling is every other pixel AND every other scanline.
yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (i % 2 == 0 && index % 2 == 0) {
yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
}
index++;
}
}
}
并使用以下方式调用它:
int mWidth = mImage.getWidth();
int mHeight = mImage.getHeight();
byte[] rgbaBytes = new byte[mWidth * mHeight * 4];
mImage.getPlanes()[0].getBuffer().get(rgbaBytes);
mImage.close();
byte[] yuv = new byte[mWidth * mHeight * 3 / 2];
RGBtoNV21(yuv, rgbaBytes, mWidth, mHeight);
这里的mImage
是由我的ImageReader
创建的Image
对象。
然而,这会产生类似于以下图像的结果:
显然格式错误。看起来我的转换有问题,但我无法确定具体问题出在哪里。