2016-12-14 4 views
1

Я использую API камеры 2 для сохранения изображений JPEG на диске. В настоящее время у меня на Nexus 5X есть 3-4 кадра в секунду, я бы хотел улучшить его до 20-30. Является ли это возможным?Камера 2, увеличение FPS

Изменение формата изображения на YUV Мне удалось создать 30 кадров в секунду. Можно ли сохранить их при этой частоте кадров, или я должен сдаться и жить со своими 3-4 кадрами в секунду?

Очевидно, что я могу использовать код, если это необходимо, но если все согласны с тем, что это невозможно, я просто сдаюсь. Использование NDK (например, с libjpeg) является вариантом (но, очевидно, я бы предпочел избежать его ...).

Благодаря

EDIT: вот как я преобразовать YUV android.media.Image в один байт []:

private byte[] toByteArray(Image image, File destination) { 

    ByteBuffer buffer0 = image.getPlanes()[0].getBuffer(); 
    ByteBuffer buffer2 = image.getPlanes()[2].getBuffer(); 
    int buffer0_size = buffer0.remaining(); 
    int buffer2_size = buffer2.remaining(); 

    byte[] bytes = new byte[buffer0_size + buffer2_size]; 

    buffer0.get(bytes, 0, buffer0_size); 
    buffer2.get(bytes, buffer0_size, buffer2_size); 

    return bytes; 
} 

EDIT 2: еще один метод, который я нашел, чтобы преобразовать изображение в YUV байт []:

private byte[] toByteArray(Image image, File destination) { 

    Image.Plane yPlane = image.getPlanes()[0]; 
    Image.Plane uPlane = image.getPlanes()[1]; 
    Image.Plane vPlane = image.getPlanes()[2]; 

    int ySize = yPlane.getBuffer().remaining(); 

    // be aware that this size does not include the padding at the end, if there is any 
    // (e.g. if pixel stride is 2 the size is ySize/2 - 1) 
    int uSize = uPlane.getBuffer().remaining(); 
    int vSize = vPlane.getBuffer().remaining(); 

    byte[] data = new byte[ySize + (ySize/2)]; 

    yPlane.getBuffer().get(data, 0, ySize); 

    ByteBuffer ub = uPlane.getBuffer(); 
    ByteBuffer vb = vPlane.getBuffer(); 

    int uvPixelStride = uPlane.getPixelStride(); //stride guaranteed to be the same for u and v planes 

    if (uvPixelStride == 1) { 

     uPlane.getBuffer().get(data, ySize, uSize); 
     vPlane.getBuffer().get(data, ySize + uSize, vSize); 
    } 
    else { 

     // if pixel stride is 2 there is padding between each pixel 
     // converting it to NV21 by filling the gaps of the v plane with the u values 
     vb.get(data, ySize, vSize); 
     for (int i = 0; i < uSize; i += 2) { 
      data[ySize + i + 1] = ub.get(i); 
     } 
    } 

    return data; 
} 
+0

Вы использовали метод отслеживания или другие методы, чтобы точно определить, где ваше время тратится? Какое разрешение вы используете? Вы делаете свой диск ввода-вывода в отдельном потоке? – CommonsWare

+0

Да, время потрачено на преобразование YUV -> JPEG и на диск ввода/вывода. Я использую максимальное разрешение (4000 * 3000 или около того). И да, дисковый ввод-вывод имеет резьбу. Но если я многопоточно сохраняю изображение, и если это занимает больше времени, чем создание изображения, вероятно, вероятность заразится OOME (или без дискового пространства), верно? –

+0

«Время, потраченное на преобразование YUV -> JPEG» - что такое преобразование YUV-> JPEG? Почему Camera2 не дает вам JPEG напрямую, используя любое аппаратное устройство, предназначенное для этого преобразования? – CommonsWare

ответ

1

выделенные блоки кодировщик JPEG на мобильные телефоны являются эффективными, но как правило, не оптимизированы для пропускной способности. (Исторически, пользователи брали по одной фотографии каждую секунду или две). При полном разрешении контур камеры 5X не будет генерировать JPEG быстрее, чем несколько FPS.

Если вам нужны более высокие тарифы, вам необходимо захватить в несжатом YUV. Как уже упоминалось в CommonsWare, недостаточно полосы пропускания диска для потока несжатого YUV с высоким разрешением на диск, поэтому вы можете удерживать только некоторое количество кадров до того, как вы исчерпаете память.

Вы можете использовать libjpeg-turbo или какой-либо другой высокоэффективный JPEG-кодировщик и посмотреть, сколько кадров в секунду вы можете сжать самостоятельно - это может быть выше, чем аппаратный JPEG-блок. Самый простой способ максимизировать скорость - захватить YUV со скоростью 30 кадров в секунду и запустить некоторое количество потоков кодирования JPEG параллельно. Для максимальной скорости вы хотите вручную записать код, разговаривающий с кодировщиком JPEG, потому что ваши исходные данные - это YUV, а не RGB, которые, как правило, принимают большинство интерфейсов кодирования JPEG (хотя обычно цветовое пространство кодированного JPEG на самом деле YUV).

Всякий раз, когда поток кодировщика заканчивает предыдущий кадр, он может захватить следующий кадр, который поступает из камеры (вы можете сохранить небольшой круговой буфер последних изображений YUV, чтобы сделать это проще).

+0

Спасибо за подробный ответ! Знаете ли вы, что OpenCV может эффективно кодировать JPEG-файлы (у меня уже установлен OpenCV в моем проекте)? Изображение YUV появляется как файл android.media.Image, который хранит данные в трех плоскостях. Вы знаете, как передать эти данные OpenCV (или libjpeg)? Я обновил свой вопрос с помощью примера кода, чтобы преобразовать его как byte [], но он не работает со всеми разрешениями (1920 * 1080 работает, 2592 * 1944 - нет), и я не уверен, что он надежный. Есть ли у вас некоторый опыт в этом формате? –

+0

Ваш пример кода будет работать, только если базовые данные NV21; это вообще не гарантируется вообще (другие устройства Android могут иметь разные макеты, поэтому вам нужно обратить внимание на ряд строк и пикселов изображения и т. д.), хотя это относится к Nexus 5X. И ваш пример кода, вероятно, не работает, если rowStride! = Width, так как вы будете копировать значения мусора (хотя вы не укажете, что «не работает» означает конкретно). –

+0

Я понятия не имею, насколько быстро кодируется JPEG-кодировщик OpenCV, и я действительно не помню, какие форматы ввода YUV поддерживают OpenCV. Если вы загружаете OpenCV NV21, вам может потребоваться передать данные пикселя из планарного в полуплоскостное, если входное изображение имеет pixelStride = 1 или другое задание плоскости. –

Смежные вопросы