Как эффективно конвертировать YUV в RGB

«Я хочу использовать RGB в предварительном просмотре камеры. Я использовал JNI для преобразования YUV в RGB. Я изменил данные в RGB, затем я показываю RGB в предварительном просмотре с помощью drawBitmap. Улучши это

    public void onPreviewFrame(final byte[] data, Camera camera) {

    Thread showPic = new Thread(new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            Canvas c = mHolder.lockCanvas(null);
            try {

                synchronized (mHolder) {

                    // TODO Auto-generated method stub
                    int imageWidth = mCamera.getParameters()
                            .getPreviewSize().width;
                    int imageHeight = mCamera.getParameters()
                            .getPreviewSize().height;
                    int RGBData[] = new int[imageWidth * imageHeight];
                    int RGBDataa[] = new int[imageWidth * imageHeight];
                    int RGBDatab[] = new int[imageWidth * imageHeight];
                    int center = imageWidth * imageHeight / 2;

                    Jni.decodeYUV420SP(RGBData, data, imageWidth,
                            imageHeight); // decode
                    for (int i = 0; i < center; i++)
                        RGBDataa[i] = RGBData[i];
                    for (int i = center; i < imageWidth * imageHeight; i++)
                        RGBDatab[i - center] = RGBData[i];
                    for (int i = 0; i < center; i++)
                        RGBData[i] = RGBDatab[i];
                    for (int i = center; i < imageWidth * imageHeight; i++)
                        RGBData[i] = RGBDataa[i - center];

                    c.drawBitmap(RGBData, 0, imageWidth, 0, 0, imageWidth,
                            imageHeight, false, new Paint());

                    // Bitmap bm = Bitmap.createBitmap(RGBData, imageWidth,
                    // imageHeight, Config.ARGB_8888);

                }

            } finally {
                if (data != null)
                    mHolder.unlockCanvasAndPost(c);
            }
        }
    });
    showPic.run();

}

Следующий код Jni

    public class Jni {
public native static void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width,
        int height);

} Метод decodeYUV420SP завершается C.


person Elivis    schedule 18.07.2012    source источник
comment
эй, у меня стек, та же проблема, но я поместил это на снимок, сделанный, я хочу спросить, где разместить этот метод и как его вызвать   -  person Antwan    schedule 29.08.2015
comment
Если кто-то ищет ответ в эти дни, ваш лучший выбор будет libyuv с оптимизированной вручную сборкой NEON.   -  person Alex Cohn    schedule 03.10.2020


Ответы (2)


Я наткнулся на эту тему в поисках преобразования YUV420sp в RGB565. Использование decodeYUV420SP, предложенное выше, прекрасно работает, но я хочу добавить некоторые соображения во время выполнения.

Мой первый тест с использованием decodeYUV420SP в Java занял 74 секунды на декодирование и преобразование 10-секундного видео .mp4 FullHD (с использованием устройства Nexus 6 Android). Профилировщик сообщил, что 96% всего процесса было потрачено на декодирование YUV420SP.

Перенос decodeYUV420SP на JNI / C позволил сократить общее время, затраченное на выполнение, до 32 секунд. Включив оптимизацию кода GCC, оно сократилось до 12 секунд.

Хотя ОП уже предлагал это, я хотел подчеркнуть, что перенос процедуры на Java не будет улучшением. Хотя правильно, что изменение контекста между Java и C обходится дорого, это хороший пример: дорогостоящая операция, выполняемая в нативной функции, легко компенсирует эти накладные расходы. Заявление о накладных расходах справедливо для очень простых функций JNI и в случае, если требуется поиск методов и полей туда и обратно. Ничего из этого здесь не применимо.

Чтобы повысить производительность кода OP: избавьтесь от выделения памяти, сделанного для каждого кадра, и сохраните его глобально, избавьтесь от арифметики в RGBData * и переместите это тоже в JNI. К массивам можно получить доступ на уровне JNI бесплатно с помощью функций Critical.

person HarryS    schedule 07.02.2016
comment
В опубликованном коде есть еще вещи, которые нужно очистить: избавиться от mCamera.getParameters() и использовать предварительно рассчитанные ширину и высоту; использовать собственный доступ к данным растрового изображения (AndroidBitmap_lockPixels() и т. д.) для обновления растрового изображения вместо копирования пикселей; повторно использовать поток и избавиться от блокировок синхронизации... Можно использовать рендерскрипт, чтобы разгрузить преобразование в GPU, или переключиться на OpenGL и позволить фрагментному шейдеру выполнять тяжелую работу. Одно можно сказать наверняка: сегодня на Android запись преобразования YUV в RGB на C по сравнению с Java оправдана. - person Alex Cohn; 07.02.2016

Не рекомендуется использовать jni из-за медленной скорости компилятора для преобразования java-кода в C++. Я думаю, вы должны использовать прямой метод, подобный этому

public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
    final int frameSize = width * height;

    for (int j = 0, yp = 0; j < height; j++) {
        int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
        for (int i = 0; i < width; i++, yp++) {
            int y = (0xff & ((int) yuv420sp[yp])) - 16;
            if (y < 0) y = 0;
            if ((i & 1) == 0) {
                v = (0xff & yuv420sp[uvp++]) - 128;
                u = (0xff & yuv420sp[uvp++]) - 128;
            }

            int y1192 = 1192 * y;
            int r = (y1192 + 1634 * v);
            int g = (y1192 - 833 * v - 400 * u);
            int b = (y1192 + 2066 * u);

            if (r < 0) r = 0; else if (r > 262143) r = 262143;
            if (g < 0) g = 0; else if (g > 262143) g = 262143;
            if (b < 0) b = 0; else if (b > 262143) b = 262143;

            rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
        }
    }
}

ИЛИ просто используйте это

Camera.Parameters.setPreviewFormat(ImageFormat.RGB_565);

изменить формат вывода на rgb напрямую

person Kẻ Lười Biếng    schedule 26.09.2013
comment
Обратите внимание, что некоторые устройства могут не поддерживать ImageFormat.RGB_565. - person insomnium_; 12.02.2015