2016-08-18 9 views
1

У меня возникают проблемы при использовании ScriptIntrinsicYuvToRGB из библиотеки поддержки для преобразования изображений из формата NV21 в Bitmaps (ARGB_8888). Приведенный ниже код может проиллюстрировать проблему.Поврежденные изображения при использовании ScriptIntrinsicYuvToRGB (поддержка lib) для NV21 в Bitmap

Пусть у меня есть следующие 50х50 изображения (ниже один скриншот из устройства, а не на самом деле 50х50):

enter image description here

Тогда, если я конвертировать указанное изображение в Bitmap через YuvImage#compressToJpeg + BitmapFactory.decodeByteArray:

YuvImage yuvImage = new YuvImage(example, android.graphics.ImageFormat.NV21, width, height, null); 
ByteArrayOutputStream os = new ByteArrayOutputStream(); 
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, os); 
byte[] jpegByteArray = os.toByteArray(); 
return BitmapFactory.decodeByteArray(jpegByteArray, 0, jpegByteArray.length); 

Я получаю ожидаемое изображение. Но если преобразовать его через ScriptIntrinsicYuvToRGB следующим образом:

RenderScript rs = RenderScript.create(context); 

Type.Builder tb = new Type.Builder(rs, Element.createPixel(rs, 
     Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_YUV)); 
tb.setX(width); 
tb.setY(height); 
tb.setYuvFormat(android.graphics.ImageFormat.NV21); 
Allocation yuvAllocation = Allocation.createTyped(rs, tb.create(), Allocation.USAGE_SCRIPT); 
yuvAllocation.copyFrom(example); 

Type rgbType = Type.createXY(rs, Element.RGBA_8888(rs), width, height); 
Allocation rgbAllocation = Allocation.createTyped(rs, rgbType); 

ScriptIntrinsicYuvToRGB yuvToRgbScript = ScriptIntrinsicYuvToRGB.create(rs, Element.RGBA_8888(rs)); 
yuvToRgbScript.setInput(yuvAllocation); 
yuvToRgbScript.forEach(rgbAllocation); 

Bitmap convertedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 
rgbAllocation.copyTo(convertedBitmap); 

Я получаю следующее поврежденное изображение:

enter image description here

Я заметил, что это не происходит с изображениями квадратных размеров и никогда с силами 2 (например, 64x64, 128x128 и т. д.). Если бы я не пробовал квадратные размеры, я бы не заметил эту проблему, так как некоторые размеры изображения, такие как 2592x1728, работают нормально. Что мне не хватает?

Update: ввод кода, который сгенерировал исходное изображение в соответствии с просьбой:

int width = 50; 
int height = 50; 
int size = width * height; 

byte[] example = new byte[size + size/2]; 
for (int x = 0; x < width; x++) { 
    for (int y = 0; y < height; y++) { 
     example[y * width + x] = (byte) ((x*y/(float)size) * 255); 
    } 
} 
for (int i = 0; i < size/2; i++) { 
    example[size + i] = (byte) (127); 
} 
+0

Вы можете разместить файл ввода, который может быть использован для тестирования кода? В противном случае немного сложно воспроизвести ошибку. :) – cmaster11

+0

@ cmaster11 Я сделал это лучше, я просто добавил код, который сгенерировал входное изображение в конце вопроса :) – silvaren

ответ

2

Следующий код ведет себя неправильно:

Type.Builder tb = new Type.Builder(rs, Element.createPixel(rs, 
        Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_YUV)); 
tb.setX(width); 
tb.setY(height); 
tb.setYuvFormat(android.graphics.ImageFormat.NV21); 
yuvAllocation = Allocation.createTyped(rs, tb.create(), Allocation.USAGE_SCRIPT); 

Если вы замените его с помощью «сырой» способ от создания распределения, будет выполнено преобразование:

int expectedBytes = width * height * 
       ImageFormat.getBitsPerPixel(ImageFormat.NV21)/8; 

Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.U8(rs)) 
                .setX(expectedBytes); 
Type yuvType = yuvTypeBuilder.create(); 
yuvAllocation = Allocation.createTyped(rs, yuvType, Allocation.USAGE_SCRIPT); 

Похоже, что если вы используете PIXEL_YUV, существует проблема с размером для не-множественных размеров. Все еще расследуя это.

+0

Насколько я понимаю, любые четные (как в нечетных/четных) разрешениях размеры изображения приемлемы для NV21 (см. http://i.stack.imgur.com/NqqJD.png для справки по формату. Вы увидите, что даже пример - 6x4, который не делится на 16. Таким образом, это изображение размером 50x50 NV21- действительный, но я мог бы что-то делать неправильно или есть ошибка в ScriptIntrinsicYuvToRgb. – silvaren

+0

Это изображение относится не к NV21, это просто пример, показывающий вам, что такое распределение памяти планарной системы YUV. – cmaster11

+0

Что не так Это?Я использовал эту ссылку на несколько раз для прямого манипулирования байтами в формате. – silvaren

0

ИМХО лучший способ преобразовать NV21 (или NV12) для ARGB на Android является использование нативного (C++) LibYUV библиотека https://chromium.googlesource.com/libyuv/libyuv/

Основное преимущество на ARM v7 (и новейших v8) на базе Android устройств NEON оптимизация, которая делает преобразование чрезвычайно быстрым.

Вы можете сделать собственную сборку LibYUV (Build Instruction) или использовать любой prebuild из GitHub: https://github.com/search?q=libyuv+Android