Как я мог различить кодировку NV21 и YV12 в API камеры imageReader 2?

Я разрабатываю пользовательское приложение Camera API 2 и замечаю, что преобразование формата захвата на некоторых устройствах отличается, когда я использую обратный вызов ImageReader.

Например, в Nexus 4 не работает нормально, а в Nexus5X выглядит нормально, вот результат.

введите описание изображения здесь

Я инициализирую ImageReader в такой форме:

mImageReader = ImageReader.newInstance(320, 240, ImageFormat.YUV_420_888,2); 

И мой обратный вызов - это простой обратный вызов ImageReader Callback.

 mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {

    @Override
    public void onImageAvailable( ImageReader reader) {

       try {
             mBackgroundHandler.post(
                 new ImageController(reader.acquireNextImage())
             );
        }
        catch(Exception e)
        {
          //exception
        }
        }

};

А в случае Nexus 4: у меня была такая ошибка.

D/qdgralloc: gralloc_lock_ycbcr: Invalid format passed: 0x32315659

Когда я пытаюсь записать необработанный файл на обоих устройствах, у меня появляются разные изображения. Итак, я понимаю, что изображение Nexus 5X имеет кодировку NV21, а Nexus 4 - кодировку YV12. введите описание изображения здесь

Я нашел спецификацию формата изображения и пытаюсь получить формат в ImageReader. Есть варианты YV12 и NV21, но, очевидно, я получаю формат YUV_420_888, когда пытаюсь получить формат.

 int test=mImageReader.getImageFormat();

Итак, есть ли способ получить формат ввода камеры (NV21 или YV12), чтобы различать эти типы кодификации в классе камеры? КамераХарактеристики может быть?

Заранее спасибо.

Унаи. PD: Я использую OpenGL для отображения изображений RGB, и я использую Opencv для преобразования в YUV_420_888.


person uelordi    schedule 18.11.2016    source источник
comment
Image.getFormat() возвращает то же, что и формат ImageReader? и кажется, что вы не одиноки: http://stackoverflow.com/questions/34717969/imagereader-format-overridden-in-some-devices   -  person nandsito    schedule 18.11.2016
comment
@nandsito, спасибо за совет, но, к сожалению, они возвращают одинаковое значение 35 в обоих случаях, что и есть YUV_420_888.   -  person uelordi    schedule 18.11.2016
comment
Вы проверили Camera.Parameters.getSupportedPictureFormats(), какие форматы поддерживаются?   -  person Robert    schedule 18.11.2016
comment
Спасибо за ваш комментарий, но я думал, что класс Camera.Parameters предназначен только для API камеры 1. developer.android.com/reference/android/hardware/. он работает на API2 камеры?   -  person uelordi    schedule 21.11.2016
comment
Вы нашли решение для этого?   -  person PerracoLabs    schedule 03.07.2017
comment
Нет, я не нашел способа сделать это в чистом режиме. Эта проблема не возникает на устройствах Android 6 и 7 (у них есть N21). Думаю, проблема в устройствах Android 5 и 5.1.1.   -  person uelordi    schedule 04.07.2017


Ответы (2)


YUV_420_888 - это оболочка, которая может размещать (среди прочего) как изображения NV21, так и YV12. Если необходимо использовать плоскости и шаги для доступа к отдельным цветам:

ByteBuffer Y = image.getPlanes()[0];
ByteBuffer U = image.getPlanes()[1];
ByteBuffer V = image.getPlanes()[2];

Если базовые пиксели имеют формат NV21 (как на Nexus 4), pixelStride будет 2, а

int getU(image, col, row) {
    return getPixel(image.getPlanes()[1], col/2, row/2);
}

int getPixel(plane, col, row) {
    return plane.getBuffer().get(col*plane.getPixelStride() + row*plane.getRowStride());
}

Мы берем половину столбца и половину строки, потому что так хранятся плоскости U и V (цветности) в 420 изображениях.

Этот код приведен для иллюстрации, он очень неэффективен, вы, вероятно, захотите получить доступ к пикселям в большом количестве, используя _ 3_, либо через фрагментный шейдер, либо через функцию JNI GetDirectBufferAddress в собственном коде. Что вы не можете использовать, так это метод plane.array(), потому что плоскости гарантированно являются прямыми байтовыми буферами.

person Alex Cohn    schedule 26.11.2017

Вот полезный метод, который конвертирует из YV12 в NV21.

public static byte[] fromYV12toNV21(@NonNull final byte[] yv12,
                                    final int width,
                                    final int height) {
    byte[] nv21 = new byte[yv12.length];
    final int size = width * height;
    final int quarter = size / 4;
    final int vPosition = size; // This is where V starts
    final int uPosition = size + quarter; // This is where U starts

    System.arraycopy(yv12, 0, nv21, 0, size); // Y is same

    for (int i = 0; i < quarter; i++) {
        nv21[size + i * 2] = yv12[vPosition + i]; // For NV21, V first
        nv21[size + i * 2 + 1] = yv12[uPosition + i]; // For Nv21, U second
    }
    return nv21;
}
person kostyabakay    schedule 26.01.2021