NV21 to Bitmap на Android, очень темное изображение, оттенки серого или желтый оттенок?

Я смотрел на преобразование байта NV21 [], который я получаю из onPreviewFrame(). Я искал форумы и Google для различных решений. Я пробовал RenderScripts и некоторые другие примеры кода. Некоторые из них дают мне изображение с желтым оттенком, некоторые дают мне изображение с перевернутым красным и синим цветом (после того, как я переворачиваю его обратно в код, я получаю желтый оттенок), некоторые дают мне странные цветовые особенности по всему изображению (почти как негатив), некоторые дают мне изображение в градациях серого, другие дают мне изображение, настолько темное, что вы не сможете ничего разобрать.

Так как я пишу вопрос, я понимаю, что должен быть идиотом в комнате, поэтому мы начнем с этого поста. Это конкретное решение дает мне очень темное изображение, но я еще не настолько крут, чтобы иметь возможность комментировать. Кто-нибудь пробовал это решение или у него есть изображение такого же качества, как в оригинальном формате NV21?

Мне нужен либо действительный байт ARGB [], либо действительное растровое изображение, я могу изменить свой проект, чтобы справиться с любым из них. Просто для справки я попробовал это (и несколько других, которые на самом деле являются просто точными копиями этих):

Одно решение я попробовал
Другое решение, которое я попробовал

3 ответа

Решение

Если вы пытаетесь конвертировать YUV с камеры в растровое изображение, вот что вы можете попробовать:

// import android.renderscript.*
// RenderScript mRS;
// ScriptIntrinsicYuvToRGB mYuvToRGB;
// Allocation yuvPreviewAlloc;
// Allocation rgbOutputAlloc;

// Create RenderScript context, ScriptIntrinsicYuvToRGB and Allocations and keep reusing them.
if (NotInitialized) {
    mRS = RenderScript.create(this).
    mYuvToRGB = ScriptIntrinsicYuvToRGB.create(mRS, Element.YUV(mRS));    

    // Create a RS Allocation to hold NV21 data.
    Type.Builder tYuv = new Type.Builder(mRS, Element.YUV(mRS));
    tYuv.setX(width).setY(height).setYuvFormat(android.graphics.ImageFormat.NV21);
    yuvPreviewAlloc = Allocation.createTyped(mRS, tYuv.create(), Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT);

    // Create a RS Allocation to hold RGBA data.
    Type.Builder tRgb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
    tRgb.setX(width).tRgb(height);
    rgbOutputAlloc = Allocation.createTyped(mRS, tRgb.create(), Allocation.USAGE_SCRIPT);

    // Set input of ScriptIntrinsicYuvToRGB
    mYuvToRGB.setInput(yuvPreviewAlloc);
}

// Use rsPreviewSurface as one of the output surface from Camera API.
// You can refer to https://github.com/googlesamples/android-HdrViewfinder/blob/master/Application/src/main/java/com/example/android/hdrviewfinder/HdrViewfinderActivity.java#L504
Surface rsPreviewSurface = yuvPreviewAlloc.getSurface();
...

// Whenever a new frame is available
// Update the yuv Allocation with a new Camera buffer without any copy.
// You can refer to https://github.com/googlesamples/android-HdrViewfinder/blob/master/Application/src/main/java/com/example/android/hdrviewfinder/ViewfinderProcessor.java#L109
yuvPreviewAlloc.ioReceive();
// The actual Yuv to Rgb conversion.
mYuvToRGB.forEach(rgbOutputAlloc);

// Copy the rgb Allocation to a Bitmap.
rgbOutputAlloc.copyTo(mBitmap);

// continue processing mBitmap.
...

При использовании ScriptIntrinsics я настоятельно рекомендую обновиться до версии JellyBean версии 4.3 или выше (API18). Вещи намного проще в использовании, чем в JB 4.2 (API 17).

ScriptIntrinsicYuvToRGB не так сложен, как кажется. Особенно вам не нужны объекты Type.Builder. Формат предварительного просмотра камеры должен быть NV21!

в методе onCreate()... создайте объект RenderScript и Intrinsic:

mRS = RenderScript.create(this);
mYuvToRGB = ScriptIntrinsicYuvToRGB.create(mRS, Element.U8_4(mRS));    

С помощью cameraPreviewWidth и cameraPreviewHeight вычислите длину байтового массива данных камеры:

int yuvDatalength = cameraPreviewWidth*cameraPreviewHeight*3/2 ;  // this is 12 bit per pixel

Вам нужно растровое изображение для вывода:

mBitmap = Bitmap.createBitmap(cameraPreviewWidth, cameraPreviewHeight, Bitmap.Config.ARGB_8888);

Затем вы создаете входные и выходные распределения (вот изменения в API18+)

yuvPreviewAlloc = Allocation.createSized(mRS, Element.U8(mRS),  yuvDatalength);

rgbOutputAlloc =  Allocation.createFromBitmap(mRS, mBitmap);  // this simple !

и установите скрипт-ввод для распределения ввода

mYuvToRGB.setInput(yuvPreviewAlloc);  // this has to be done only once !

В цикле камеры (когда доступен новый кадр), скопируйте байтовый массив NV21 (data[]) в yuvPreviewAlloc, выполните сценарий и скопируйте результат в растровое изображение:

yuvPreviewAlloc.copyFrom(data);  // or yuvPreviewAlloc.copyFromUnchecked(data);

mYuvToRGB.forEach(rgbOutputAlloc);

rgbOutputAlloc.copyTo(mBitmap);

Например: на Nexus 7 (2013, JellyBean 4.3) преобразование предварительного просмотра камеры в формате Full HD (1920x1080) занимает около 7 мс.

Я смог заставить работать другой метод (ранее связанный) с помощью приведенного здесь кода. Но это дало красный / синий цвет перевернуть. Итак, я просто переставил линии U и V, и все было в порядке. Это не так быстро, как RenderScript, хотя. Было бы хорошо иметь RenderScript, который функционировал должным образом. Вот код:

static 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) {
                u = (0xff & yuv420sp[uvp++]) - 128; //Just changed the order
                v = (0xff & yuv420sp[uvp++]) - 128; //It was originally v then u
            }

            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);
        }
    }
}

У кого-нибудь есть RenderScript, у которого нет цветового оттенка или проблем с переворотом?

Другие вопросы по тегам