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, у которого нет цветового оттенка или проблем с переворотом?