gpt4 book ai didi

Android 使用渲染脚本将 ImageReader Image 转换为 YCbCr_420_SP (NV21) 字节数组?

转载 作者:IT老高 更新时间:2023-10-28 23:25:01 39 4
gpt4 key购买 nike

我目前正在使用 Javacv它利用了 public void onPreviewFrame(byte[] data, Camera camera) 相机功能。

由于不推荐使用相机,我一直在研究 camera2MediaProjection .这两个库都使用 ImageReader class .

目前我用以下代码实例化这样一个 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()));
}

我已尝试按照以下线程将 RGBA_8888 格式与 Javacv 一起使用:https://github.com/bytedeco/javacv/issues/298但这对我不起作用。

所以我考虑使用 Render script转换这些 Image s 到 NV21(YUV_420_SP) 格式(这是 onPreviewFrame 函数中相机的默认输出),因为它适用于 camera 库.

我也看过 this one 之类的帖子和 this website进行转换,但这些对我不起作用,我担心它们会太慢。此外,我对 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 格式。

我从 this answer 修改了代码产生以下功能:

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对象。

然而,这会产生类似于此图像的结果:

malformed image

这显然是错误的。似乎我的转换已关闭,但我无法弄清楚到底是什么。

最佳答案

@TargetApi(19)
public static byte[] yuvImageToByteArray(Image image) {

assert(image.getFormat() == ImageFormat.YUV_420_888);

int width = image.getWidth();
int height = image.getHeight();

Image.Plane[] planes = image.getPlanes();
byte[] result = new byte[width * height * 3 / 2];

int stride = planes[0].getRowStride();
assert (1 == planes[0].getPixelStride());
if (stride == width) {
planes[0].getBuffer().get(result, 0, width*height);
}
else {
for (int row = 0; row < height; row++) {
planes[0].getBuffer().position(row*stride);
planes[0].getBuffer().get(result, row*width, width);
}
}

stride = planes[1].getRowStride();
assert (stride == planes[2].getRowStride());
int pixelStride = planes[1].getPixelStride();
assert (pixelStride == planes[2].getPixelStride());
byte[] rowBytesCb = new byte[stride];
byte[] rowBytesCr = new byte[stride];

for (int row = 0; row < height/2; row++) {
int rowOffset = width*height + width/2 * row;
planes[1].getBuffer().position(row*stride);
planes[1].getBuffer().get(rowBytesCb);
planes[2].getBuffer().position(row*stride);
planes[2].getBuffer().get(rowBytesCr);

for (int col = 0; col < width/2; col++) {
result[rowOffset + col*2] = rowBytesCr[col*pixelStride];
result[rowOffset + col*2 + 1] = rowBytesCb[col*pixelStride];
}
}
return result;
}

我发表了another function有类似的要求。新实现试图利用这样一个事实,即 YUV_420_888 通常只是伪装的 NV21。

关于Android 使用渲染脚本将 ImageReader Image 转换为 YCbCr_420_SP (NV21) 字节数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39067828/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com