在android camera2中将yuv420转换为jpeg

时间:2017-03-07 22:48:47

标签: java android image-processing jpeg yuv

我正在尝试将在android camera2中获得的yuv 420预览帧保存到jpeg。我发现这样做的唯一方法是将yuv420转换为nv21,构造一个yuvimage,然后使用compresstojpeg方法获取jpeg。为了从yuv420转换为jpeg,我使用下面的逻辑

            Image.Plane Y = img.getPlanes()[0];
            Image.Plane U = img.getPlanes()[2];
            Image.Plane V = img.getPlanes()[1];

            int Yb = Y.getBuffer().remaining();
            int Ub = U.getBuffer().remaining();
            int Vb = V.getBuffer().remaining();

            byte[] data = new byte[Yb + Ub + Vb];

            Y.getBuffer().get(data, 0, Yb);
            U.getBuffer().get(data, Yb, Ub);
            V.getBuffer().get(data, Yb + Ub, Vb);

            YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21,
                mPreviewSize.getWidth(), mPreviewSize.getHeight(), null);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            yuvImage.compressToJpeg(new Rect(0, 0,
                    mPreviewSize.getWidth(), mPreviewSize.getHeight()),
                100, out);

然而,这会导致获得某些分辨率为144 x 176,176x144,352x288,480x360,1280x960的绿色图像。转换为nv21的逻辑是否正确?我可以用什么方式从yuv420转换为jpeg。 这有没有Java / Android api?

1 个答案:

答案 0 :(得分:1)

不,这不正确 - 你没有注意到平面的行间距或像素跨度。

你必须解析那些,并确保你的输出缓冲区实际上符合YuvImage的NV21输入的输入预期,它假定行步幅=宽度和交错的V / U平面。

您所拥有的代码只有在输入的图像U / V平面实际交错时才会起作用(在这种情况下,您需要添加两倍所需的UV数据,但第一个副本恰好是正确的布局...),如果width == row stride。 width == row stride是否取决于分辨率;通常,由于硬件限制,步幅必须是16像素的倍数或类似的东西。因此,对于不是16的倍数的分辨率,您的代码将无效。

请解决两个问题 - 注意行和像素的步幅;否则你可能会意外地在你的设备上工作,并且仍然在具有不同步长参数的设备上打破它。

修改

可以在Android AOSP相机服务代码CallbackProcessor::convertFromFlexibleYuv中找到执行此类转换的一些示例C ++代码。

映射以供参考:

  • HAL_PIXEL_FORMAT_YCrCb_420_SP 的previewFormat是NV21。
  • src.data是plane 0
  • src.dataCb是1号飞机
  • src.dataCr是plane 2
  • src.stride是plane 0 rowStride
  • src.chromaStride是平面1和2 rowStride
  • src.chromaStep是平面1和2 pixelStride
  • 平面0像素步幅为1