我正在尝试将在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?
答案 0 :(得分:1)
不,这不正确 - 你没有注意到平面的行间距或像素跨度。
你必须解析那些,并确保你的输出缓冲区实际上符合YuvImage的NV21输入的输入预期,它假定行步幅=宽度和交错的V / U平面。
您所拥有的代码只有在输入的图像U / V平面实际交错时才会起作用(在这种情况下,您需要添加两倍所需的UV数据,但第一个副本恰好是正确的布局...),如果width == row stride。 width == row stride是否取决于分辨率;通常,由于硬件限制,步幅必须是16像素的倍数或类似的东西。因此,对于不是16的倍数的分辨率,您的代码将无效。
请解决两个问题 - 注意行和像素的步幅;否则你可能会意外地在你的设备上工作,并且仍然在具有不同步长参数的设备上打破它。
修改强>
可以在Android AOSP相机服务代码CallbackProcessor::convertFromFlexibleYuv中找到执行此类转换的一些示例C ++代码。
映射以供参考: