Преобразование YUV (NV21) в BGR на мобильных устройствах (собственный код)

Я разрабатываю мобильное приложение, которое работает на Android и IOS. Он способен обрабатывать видеопоток в реальном времени. На Android я получаю предварительный просмотр видеопотока камеры через android.hardware.Camera.PreviewCallback.onPreviewFrame. Я решил использовать формат NV21, поскольку он должен поддерживаться всеми Android-устройствами, а RGB — нет (или только RGB565).

Для моих алгоритмов, которые в основном предназначены для распознавания образов, мне нужны изображения в градациях серого, а также информация о цвете. Оттенки серого не проблема, но преобразование цвета из NV21 в BGR занимает слишком много времени.

Как описано, я использую следующий метод для захвата изображений;

В приложении я переопределяю onPreviewFrame-Handler камеры. Это делается в CameraPreviewFrameHandler.java:

 @Override
      public void onPreviewFrame(byte[] data, Camera camera) {
      {
          try {
              AvCore.getInstance().onFrame(data, _prevWidth, _prevHeight, AvStreamEncoding.NV21);
          } catch (NativeException e) 
          {
              e.printStackTrace();
          } 
      }

Затем onFrame-Function вызывает собственную функцию, которая извлекает данные из Java-объектов в виде локальных ссылок. Затем он преобразуется в поток байтов char* без знака и вызывает следующую функцию c++, которая использует OpenCV для преобразования из NV21 в BGR:

void CoreManager::api_onFrame(unsigned char* rImageData, avStreamEncoding_t vImageFormat, int vWidth, int vHeight)
    {
    // rImageData is a local JNI-reference to the java-byte-array "data" from onPreviewFrame
    Mat bgrMat; // Holds the converted image
    Mat origImg;    // Holds the original image (OpenCV-Wrapping around rImageData)

    double ts; // for profiling
    switch(vImageFormat)
    {
           // other formats
        case NV21:
            origImg = Mat(vHeight + vHeight/2, vWidth, CV_8UC1, rImageData); // fast, only creates header around rImageData
            bgrMat = Mat(vHeight, vWidth, CV_8UC3); // Prepare Mat for target image
            ts = avUtils::gettime();                // PROFILING START
            cvtColor(origImg, bgrMat, CV_YUV2BGRA_NV21);
            _onFrameBGRConversion.push_back(avUtils::gettime()-ts); // PROFILING END
            break;
    }

    [...APPLICATION LOGIC...]
}

Как можно заключить из комментариев в коде, я уже профилировал конвертацию, и оказалось, что на моем Nexus 4 она занимает ~30 мс, что неприемлемо долго для такого «тривиального» шага предварительной обработки. (Мои методы профилирования проверены дважды и работают правильно для измерения в реальном времени)

Теперь я отчаянно пытаюсь найти более быструю реализацию этого преобразования цвета из NV21 в BGR. Это то, что я уже сделал;

  1. Принят код «convertYUV420_NV21toRGB8888» в C++, предоставленный в этой теме (кратное времени конверсии)
  2. Изменен код с 1, чтобы использовать только целочисленные операции (удвоенное время преобразования openCV-Solution)
  3. Просмотрел пару других реализаций, все с аналогичным временем конверсии.
  4. Проверено OpenCV-Implementation, они используют много битового сдвига для повышения производительности. Думаю, я не могу сделать лучше сам

У вас есть предложения/знаете хорошие реализации или даже есть совершенно другой способ обойти эту проблему? Мне как-то нужно захватить RGB/BGR-кадры с Android-камеры, и это должно работать на как можно большем количестве Android-устройств.

Спасибо за ваши ответы!


person Thomas Bergmueller    schedule 12.09.2013    source источник
comment
Я не совсем уверен, согласен ли я с тем, что программное преобразование цвета является тривиальным этапом предварительной обработки, может быть математически тривиальным, но с точки зрения вычислений ... вам нужно пройти через все пиксели.   -  person Rui Marques    schedule 13.09.2013


Ответы (1)


Вы пробовали либьюв? Я использовал его в прошлом, и если вы скомпилируете его с поддержкой NEON, он использует ассемблерный код, оптимизированный для процессоров ARM, вы можете начать с него для дальнейшей оптимизации для вашей конкретной ситуации.

person Dragos Iordache    schedule 12.09.2013
comment
Спасибо за подсказку, я еще не пробовал и не уверен, стоит ли; Мы стараемся писать код, который работает одинаково на каждой платформе без адаптации, поэтому мы больше отдаем предпочтение алгоритмической оптимизации. Это может учитывать оптимизацию работы ЦП, например, такие вещи, как таблицы поиска для минимизации остановок конвейера, хорошо подходят, потому что большинство архитектур имеют аналогичные алгоритмы прогнозирования перехода, соответственно, прогнозирование перехода не требуется. Но если мы не найдем кросс-платформенный способ оптимизации, я с радостью вернусь к вашему предложению, спасибо! - person Thomas Bergmueller; 12.09.2013