当前位置: 首页 > 知识库问答 >
问题:

camera2捕获的图片-从YUV_420_888到NV21的转换

公冶森
2023-03-14
private static byte[] YUV_420_888toNV21(Image image) {
    byte[] nv21;
    ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
    ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
    ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();

    int ySize = yBuffer.remaining();
    int uSize = uBuffer.remaining();
    int vSize = vBuffer.remaining();

    nv21 = new byte[ySize + uSize + vSize];

    //U and V are swapped
    yBuffer.get(nv21, 0, ySize);
    vBuffer.get(nv21, ySize, vSize);
    uBuffer.get(nv21, ySize + vSize, uSize);

    return nv21;
}

为什么两个函数调用的结果是不同的,而请求的类型是相同的?

更新:正如注释中提到的,我得到这种行为是因为不同的图像大小(捕获请求的尺寸要大得多)。但是我们在JNI端的进一步处理操作对于两个请求是相同的,并且不依赖于图像尺寸(只依赖于纵横比,这在两种情况下是相同的)。

共有1个答案

满玉泽
2023-03-14

如果根本没有填充,并且U和V平面图重叠,实际上表示交错的VU值,代码将只返回正确的NV21。这种情况在预览中经常发生,但在这种情况下,您需要为数组分配额外的W*h/4字节(这可能不是问题)。也许对于捕获的图像,你需要一个更健壮的隐写,例如。

private static byte[] YUV_420_888toNV21(Image image) {

    int width = image.getWidth();
    int height = image.getHeight(); 
    int ySize = width*height;
    int uvSize = width*height/4;

    byte[] nv21 = new byte[ySize + uvSize*2];

    ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); // Y
    ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); // U
    ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); // V

    int rowStride = image.getPlanes()[0].getRowStride();
    assert(image.getPlanes()[0].getPixelStride() == 1);

    int pos = 0;

    if (rowStride == width) { // likely
        yBuffer.get(nv21, 0, ySize);
        pos += ySize;
    }
    else {
        long yBufferPos = -rowStride; // not an actual position
        for (; pos<ySize; pos+=width) {
            yBufferPos += rowStride;
            yBuffer.position(yBufferPos);
            yBuffer.get(nv21, pos, width);
        }
    }

    rowStride = image.getPlanes()[2].getRowStride();
    int pixelStride = image.getPlanes()[2].getPixelStride();

    assert(rowStride == image.getPlanes()[1].getRowStride());
    assert(pixelStride == image.getPlanes()[1].getPixelStride());
    
    if (pixelStride == 2 && rowStride == width && uBuffer.get(0) == vBuffer.get(1)) {
        // maybe V an U planes overlap as per NV21, which means vBuffer[1] is alias of uBuffer[0]
        byte savePixel = vBuffer.get(1);
        try {
            vBuffer.put(1, (byte)~savePixel);
            if (uBuffer.get(0) == (byte)~savePixel) {
                vBuffer.put(1, savePixel);
                vBuffer.position(0);
                uBuffer.position(0);
                vBuffer.get(nv21, ySize, 1);
                uBuffer.get(nv21, ySize + 1, uBuffer.remaining());

                return nv21; // shortcut
            }
        }
        catch (ReadOnlyBufferException ex) {
            // unfortunately, we cannot check if vBuffer and uBuffer overlap
        }

        // unfortunately, the check failed. We must save U and V pixel by pixel
        vBuffer.put(1, savePixel);
    }

    // other optimizations could check if (pixelStride == 1) or (pixelStride == 2), 
    // but performance gain would be less significant

    for (int row=0; row<height/2; row++) {
        for (int col=0; col<width/2; col++) {
            int vuPos = col*pixelStride + row*rowStride;
            nv21[pos++] = vBuffer.get(vuPos);
            nv21[pos++] = uBuffer.get(vuPos);
        }
    }

    return nv21;
}

如果您打算将结果数组传递给C++,您可以利用以下事实

返回的缓冲区将始终具有isDirect返回true,因此底层数据可以映射为JNI中的指针,而无需使用GetDirectBufferAddress进行任何复制。

 类似资料:
  • 我试图将YUV_420_888图像转换为位图,来自camera2预览。但是输出图像的颜色不正确。 请让我知道如果有人能发现转换的问题:

  • 问题内容: 我正在使用OnImageAvailableListener获得预览帧: 每次数据长度不同,但图像的宽度和高度相同。 主要问题:对于3264x2448之类的分辨率而言太小。 数据数组的大小应为3264 * 2448 = 7,990,272,而不是300,000-600,000。 怎么了? 问题答案: 我通过使用 YUV_420_888 图像格式并将其手动转换为 JPEG 图像格式解决了此

  • 但是为了全屏显示相机预览,我将TextureView更改为match_parent。这样做,输出就改变了。现在预览的相机是不同的图像捕获。 请检查这里附上的图像。 1、相机预览:-相机预览截图 我也尝试在camera preview builder和image reader中设置CaptureRequest.Scaler_Crop_Region,但它没有像预期的那样工作。

  • 我编写了一个从YUV_420_888到位图的转换,考虑了以下逻辑(根据我的理解): 总结一下这个方法:内核的坐标x和y与y平面(2D-分配)的非填充部分的x和y以及输出位图的x和y是一致的。然而,U平面和V平面具有不同于Y平面的结构,因为它们使用1字节来覆盖4个像素,此外,它们可能具有超过一个像素步幅,此外,它们还可能具有与Y平面不同的填充。因此,为了让内核有效地访问U和V,我将它们放入一维分配中

  • 问题内容: 我正在尝试从相机预览中捕获图像并在其上进行一些绘制。问题是,我只有大约3-4 fps的绘图,并且一半的帧处理时间是从摄像机预览接收和解码NV21图像并将其转换为 位图 。我有一个执行此任务的代码,是在另一个堆栈问题上找到的。它似乎并不快,但我不知道如何优化它。在Samsung Note 3(图像尺寸1920x1080)上大约需要100-150毫秒。我怎样才能使其更快地工作? 代码: 图

  • 问题内容: 我正在尝试修改android-Camera2Basic代码以捕获一连串的图片。但是,在运行L 5.0.1的Nexus 5上,图片之间的延迟不会超过200-300ms。 我已经尝试了很多东西,但这是最基本的。这是我修改过的Camera2Basic代码的唯一部分。我的预览TextureView只有50x50dp,但这没关系,对吧? 就其价值而言,此代码在我的Nexus 6(带有L 5.1)