Java Android Camera2 API YUV_420_888到JPEG

Java Android Camera2 API YUV_420_888到JPEG,java,android,android-camera2,camera2,Java,Android,Android Camera2,Camera2,我使用: 每次数据长度不同,但图像宽度和高度相同。 主要问题:数据。长度对于3264x2448这样的分辨率来说太小。 数据数组的大小应为3264*2448=7990272,而不是300000-600000。 怎么了 我不确定,但我认为您只使用YUV_420_888格式的一个平面(亮度部分) 在我的例子中,我通常用这种方式将图像转换为字节[] Image m_img; Log.v(LOG_TAG,"Format -> "+m_img.getF

我使用:

每次数据长度不同,但图像宽度和高度相同。
主要问题:
数据。长度对于3264x2448这样的分辨率来说太小。
数据数组的大小应为3264*2448=7990272,而不是300000-600000。
怎么了



我不确定,但我认为您只使用YUV_420_888格式的一个平面(亮度部分)

在我的例子中,我通常用这种方式将图像转换为字节[]

            Image m_img;
            Log.v(LOG_TAG,"Format -> "+m_img.getFormat());
            Image.Plane Y = m_img.getPlanes()[0];
            Image.Plane U = m_img.getPlanes()[1];
            Image.Plane V = m_img.getPlanes()[2];

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

            data = new byte[Yb + Ub + Vb];
            //your data length should be this byte array length.

            Y.getBuffer().get(data, 0, Yb);
            U.getBuffer().get(data, Yb, Ub);
            V.getBuffer().get(data, Yb+ Ub, Vb);
            final int width = m_img.getWidth();
            final int height = m_img.getHeight();
我用这个字节缓冲区转换成rgb

希望这有帮助

干杯。
Unai。

我通过使用图像格式并手动将其转换为图像格式来解决此问题。

imageReader = ImageReader.newInstance(MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT, 
                                      ImageFormat.YUV_420_888, 5);
imageReader.setOnImageAvailableListener(this, null);




您的代码正在请求压缩的JPEG格式图像。每一帧图像的大小都会发生变化,并且比未压缩的图像小得多。如果您除了保存JPEG图像之外什么都不想做,只需将byte[]数据中的内容保存到磁盘即可

例如,如果您想对JPEG进行实际处理,可以使用BitmapFactory.decodeByteArray()将其转换为位图,尽管这样效率很低


或者,您可以切换到YUV,这更有效,但您需要做更多的工作才能从中获得位图。

感谢您的回复!目前我使用的是
ImageFormat.JPEG
,因此
m_img.getPlanes().length
等于1。好的,如果我使用
ImageFormat.YUV_420_888
,如何将
rgb
缓冲区压缩到
JPEG
?太好了,不用客气,我不知道你用这个byterray的目的是什么。抱歉,我的回答延迟了。转换为位图并返回每个帧只是为了解码不是一个选项。您当前的解决方案似乎是将YUV数据重新排列为NV21,然后将其压缩为JPEG;现在还不清楚您想要YUV做什么,但是如果您只想保存数据,那么只要求JPEG肯定更便宜。您可以要求JPEG和YUV,这取决于分辨率,这可能是最有效的选择,尽管JPEG输出的帧速率可能低于30fps。我使用NV21进行QR识别/解码。JPEG用于向服务器发送帧。我使用了答案中的
ImageUtil
类将
YUV_420_888
转换为
JPEG
,但是我没有得到正确的输出。通过使用此类,可以将输出的
字节[]
保存到JPEG文件中,或者甚至可以将其显示在
SurfaceView
上?我正在尝试这样做,但是我得到了扭曲的图像。
data=NV21toJPEG(YUV_420_888; tonv21(image),image.getWidth(),image.getHeight())是否有失真?是的,我已按原样放置该类,并实现了与您的实现类似的编写文件,但使用了
FileOutputStream
,最终图像失真。我可以通过git repo向您展示完整的实现,我会稍微介绍一下。这个YUV_420_888; tonv21()不可靠,请参阅@AlexCohn谢谢,我同意您的看法。我来测试你的解决方案。
            Image m_img;
            Log.v(LOG_TAG,"Format -> "+m_img.getFormat());
            Image.Plane Y = m_img.getPlanes()[0];
            Image.Plane U = m_img.getPlanes()[1];
            Image.Plane V = m_img.getPlanes()[2];

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

            data = new byte[Yb + Ub + Vb];
            //your data length should be this byte array length.

            Y.getBuffer().get(data, 0, Yb);
            U.getBuffer().get(data, Yb, Ub);
            V.getBuffer().get(data, Yb+ Ub, Vb);
            final int width = m_img.getWidth();
            final int height = m_img.getHeight();
imageReader = ImageReader.newInstance(MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT, 
                                      ImageFormat.YUV_420_888, 5);
imageReader.setOnImageAvailableListener(this, null);
Surface imageSurface = imageReader.getSurface();
List<Surface> surfaceList = new ArrayList<>();
//...add other surfaces
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(imageSurface);
            surfaceList.add(imageSurface);
cameraDevice.createCaptureSession(surfaceList,
                    new CameraCaptureSession.StateCallback() {
//...implement onConfigured, onConfigureFailed for StateCallback
}, null);
@Override
public void onImageAvailable(ImageReader reader) {
    Image image = reader.acquireLatestImage();
    if (image != null) {
        //converting to JPEG
        byte[] jpegData = ImageUtils.imageToByteArray(image);
        //write to file (for example ..some_path/frame.jpg)
        FileManager.writeFrame(FILE_NAME, jpegData);
        image.close();
    }
}
public final class ImageUtil {

    public static byte[] imageToByteArray(Image image) {
        byte[] data = null;
        if (image.getFormat() == ImageFormat.JPEG) {
            Image.Plane[] planes = image.getPlanes();
            ByteBuffer buffer = planes[0].getBuffer();
            data = new byte[buffer.capacity()];
            buffer.get(data);
            return data;
        } else if (image.getFormat() == ImageFormat.YUV_420_888) {
            data = NV21toJPEG(
                    YUV_420_888toNV21(image),
                    image.getWidth(), image.getHeight());
        }
        return data;
    }

    private static byte[] YUV_420_888toNV21(Image image) {
        byte[] nv21;
        ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
        ByteBuffer vuBuffer = image.getPlanes()[2].getBuffer();

        int ySize = yBuffer.remaining();
        int vuSize = vuBuffer.remaining();

        nv21 = new byte[ySize + vuSize];

        yBuffer.get(nv21, 0, ySize);
        vuBuffer.get(nv21, ySize, vuSize);

        return nv21;
    }

    private static byte[] NV21toJPEG(byte[] nv21, int width, int height) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
        yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
        return out.toByteArray();
    }
}
public final class FileManager {
    public static void writeFrame(String fileName, byte[] data) {
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName));
            bos.write(data);
            bos.flush();
            bos.close();
//            Log.e(TAG, "" + data.length + " bytes have been written to " + filesDir + fileName + ".jpg");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}