Android ImageReader получить формат NV21?

у меня нет фона в изображении или графике, поэтому, пожалуйста, несите со мной:)

я использую JavaCV в одном из моих проектов. В примеры, a Frame построено которое имеет буфер определенного размера.

при использовании public void onPreviewFrame(byte[] data, Camera camera) функция в Android, копирование этого data массив байтов не проблема, если вы объявите Frame as new Frame(frameWidth, frameHeight, Frame.DEPTH_UBYTE, 2); здесь frameWidth и frameHeight объявлены

Camera.Size previewSize = cameraParam.getPreviewSize();
int frameWidth = previewSize.width;
int frameHeight = previewSize.height;

недавно Android добавил метод для захвата экрана. Естественно, я хотел захватить эти изображения, а также скрыть их Frames. Я изменил пример кода из Google использовать ImageReader.

этой ImageReader построен в ImageReader.newInstance(DISPLAY_WIDTH, DISPLAY_HEIGHT, PixelFormat.RGBA_8888, 2);. Поэтому в настоящее время он использует формат пикселей RGBA_8888. Я использую следующий код для копирования байтов в Frame, который создается как new Frame(DISPLAY_WIDTH, DISPLAY_HEIGHT, Frame.DEPTH_UBYTE, 2);:

ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
mImage.close();
((ByteBuffer) frame.image[0].position(0)).put(bytes);

но это дает мне java.nio.BufferOverflowException. Я напечатал размеры обоих буферов, а размер буфера кадра-691200, тогда как bytes массив выше имеет размер 1413056. Выяснить, как это последнее число построено, не удалось, потому что я столкнулся с этим родным вызовом. Ясно, что из этого ничего не выйдет.

после довольно много копать я узнал, что изображения NV21 формате это "формат по умолчанию для предварительного просмотра камеры изображения, если не установлено иначе с setPreviewFormat (int)", но класс ImageReader не поддерживает формат NV21 (см. параметр format). Так что не повезло. В документации также говорится, что - Для андроида.аппаратура.Camera2 API, формат YUV_420_888 рекомендуется для вывода YUV вместо этого."

поэтому я попытался создать ImageReader, как это ImageReader.newInstance(DISPLAY_WIDTH, DISPLAY_HEIGHT, ImageFormat.YUV_420_888, 2);, но это дает мне java.lang.UnsupportedOperationException: The producer output buffer format 0x1 doesn't match the ImageReader's configured buffer format 0x23. так что это тоже не сработает.

в крайнем случае, я пытался преобразовать RGBA_8888 в YUV сам, используя, например этот пост, но я не понимаю, как я могу получить int[] rgba согласно ответу.

Итак, TL; DR как я могу получить данные изображения NV21, как вы получаете в Android public void onPreviewFrame(byte[] data, Camera camera) функция камеры для создания экземпляра my Frame и работать с ним с помощью ImageReader Android (и медиа-проекции)?

Edit (25-10-2016)

я создал следующее преобразование runnable to go от RGBA до nv21 формат:

private class updateImage implements Runnable {

    private final Image mImage;

    public updateImage(Image image) {
        mImage = image;
    }

    @Override
    public void run() {

        int mWidth = mImage.getWidth();
        int mHeight = mImage.getHeight();

        // Four bytes per pixel: width * height * 4.
        byte[] rgbaBytes = new byte[mWidth * mHeight * 4];
        // put the data into the rgbaBytes array.
        mImage.getPlanes()[0].getBuffer().get(rgbaBytes);

        mImage.close(); // Access to the image is no longer needed, release it.

        // Create a yuv byte array: width * height * 1.5 ().
        byte[] yuv = new byte[mWidth * mHeight * 3 / 2];
        RGBtoNV21(yuv, rgbaBytes, mWidth, mHeight);
        ((ByteBuffer) yuvImage.image[0].position(0)).put(yuv);
    }

    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++]; // Ignored right now.

                // 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++;
            }
        }
    }
}

на

1 ответов


вообще говоря, точка ImageReader-предоставить вам необработанный доступ к пикселям, отправленным на поверхность с минимальными накладными расходами, поэтому попытка выполнить преобразование цветов не имеет смысла.

для камеры вы можете выбрать один из двух выходных форматов (NV21 или YV12), поэтому выберите YV12. Это ваши сырые данные YUV. Для захвата экрана выход всегда будет RGB, поэтому вам нужно выбрать RGBA_8888 (формат 0x1) для вашего ImageReader, а не YUV_420_888 (0x23 формат). Если вам нужен YUV для этого, вам придется сделать преобразование самостоятельно. ImageReader дает вам серию Plane объектов, а не byte[], поэтому вам нужно будет адаптироваться к этому.