Android ImageReader получит формат NV21?Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Android ImageReader получит формат NV21?

Сообщение Anonymous »

У меня нет опыта работы с изображениями или графикой, поэтому, пожалуйста, потерпите :)

Я использую JavaCV в одном из своих проектов. В примерах создается Frame, имеющий буфер определенного размера.

При использовании функции public void onPreviewFrame(byte[] data, Camera camera) в Android копирование этого массива байтов данных не представляет проблем. если вы объявите Frame как новый Frame(frameWidth,frameHeight, Frame.DEPTH_UBYTE, 2); гдеframeWidth иframeHeight объявлены как

Код: Выделить всё

Camera.Size previewSize = cameraParam.getPreviewSize();
int frameWidth = previewSize.width;
int frameHeight = previewSize.height;
Недавно в Android добавлен метод захвата экрана. Естественно, мне хотелось захватить эти изображения и поместить их в рамки. Я изменил пример кода из Google, чтобы использовать ImageReader.

Этот ImageReader создан как ImageReader.newInstance(DISPLAY_WIDTH, DISPLAY_HEIGHT, PixelFormat.RGBA_8888, 2);. Поэтому в настоящее время он использует формат пикселей RGBA_8888. Я использую следующий код для копирования байтов в Frame, который создается как новый Frame(DISPLAY_WIDTH, DISPLAY_HEIGHT, Frame.DEPTH_UBYTE, 2);:

Код: Выделить всё

ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
mImage.close();
((ByteBuffer) frame.image[0].position(0)).put(bytes);
Но это дает мне исключение java.nio.BufferOverflowException. Я напечатал размеры обоих буферов, и размер буфера Frame составляет 691200, тогда как массив байтов выше имеет размер 1413056. Выяснить, как устроено это последнее число, не удалось, потому что я столкнулся с этим собственным вызовом. Очевидно, что это не сработает.

После долгих поисков я обнаружил, что формат изображения NV21 является «форматом по умолчанию для изображений предварительного просмотра камеры, когда иначе не устанавливается с помощью setPreviewFormat(int)", но класс ImageReader не поддерживает формат NV21 (см. параметр формата). Так что это невезение. В документации также написано, что
"Для API android.hardware.camera2 вместо этого для вывода YUV рекомендуется формат YUV_420_888".

Поэтому я попробовал создание ImageReader, такого как ImageReader.newInstance(DISPLAY_WIDTH, DISPLAY_HEIGHT, ImageFormat.YUV_420_888, 2);, но это дает мне java.lang.UnsupportedOperationException: формат выходного буфера производителя 0x1 не соответствует настроенному формату буфера ImageReader 0x23., так что это тоже не будет работать.

В крайнем случае, я попытался самостоятельно преобразовать RGBA_8888 в YUV, например. этот пост, но я не понимаю, как я могу получить int[] rgba согласно ответу.

Итак, TL;DR как Могу ли я получить данные изображения NV21, как вы получаете в публичной пустоте Android onPreviewFrame(byte[] data, Camera camera) функция камеры для создания экземпляра моего кадра и работать с ним с помощью Android ImageReader (и Media Проекция)?

Изменить (25-10-2016)

Я создал следующее возможность преобразования для перехода из формата RGBA в формат NV21:

Код: Выделить всё

private class updateImage implements Runnable {

private final Image mImage;

public updateImage(Image image) {
mImage = image;
}

@Override
public void run() {

int mWidth = mImage.getWidth();
int mHeight = mImage.getHeight();

// Four bytes per pixel: width * height * 4.
byte[] rgbaBytes = new byte[mWidth * mHeight * 4];
// put the data into the rgbaBytes array.
mImage.getPlanes()[0].getBuffer().get(rgbaBytes);

mImage.close(); // Access to the image is no longer needed, release it.

// Create a yuv byte array: width * height * 1.5 ().
byte[] yuv = new byte[mWidth * mHeight * 3 / 2];
RGBtoNV21(yuv, rgbaBytes, mWidth, mHeight);
((ByteBuffer) yuvImage.image[0].position(0)).put(yuv);
}

void RGBtoNV21(byte[] yuv420sp, byte[] argb, int width, int height) {
final int frameSize = width * height;

int yIndex = 0;
int uvIndex = frameSize;

int A, R, G, B, Y, U, V;
int index = 0;
int rgbIndex = 0;

for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {

R = argb[rgbIndex++];
G = argb[rgbIndex++];
B = argb[rgbIndex++];
A = argb[rgbIndex++]; // Ignored right now.

// RGB to YUV conversion according to
// https://en.wikipedia.org/wiki/YUV#Y.E2.80.B2UV444_to_RGB888_conversion
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor
// of 2 meaning for every 4 Y pixels there are 1 V and 1 U.
// Note the sampling is every other pixel AND every other scanline.
yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (i % 2 == 0 && index % 2 == 0) {
yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
}
index++;
}
}
}
}
Объект yuvImage инициализируется как yuvImage = new Frame(DISPLAY_WIDTH, DISPLAY_HEIGHT, Frame.DEPTH_UBYTE, 2);, DISPLAY_WIDTH и DISPLAY_HEIGHT — это всего лишь два целых числа, определяющих размер дисплея.
Это код, в котором фоновый обработчик обрабатывает onImageReady:

Код: Выделить всё

private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {

@Override
public void onImageAvailable(ImageReader reader) {
mBackgroundHandler.post(new updateImage(reader.acquireNextImage()));
}

};

...

mImageReader = ImageReader.newInstance(DISPLAY_WIDTH, DISPLAY_HEIGHT, PixelFormat.RGBA_8888, 2);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
Методы работают, и я, по крайней мере, не получаю никаких ошибок, но выходное изображение имеет неверный формат. Что происходит не так в моем преобразовании? Пример создаваемого изображения:

Изображение


Изменить (15-11-2016)

Я изменил функцию RGBtoNV21 следующим образом:

Код: Выделить всё

void RGBtoNV21(byte[] yuv420sp, int width, int height) {
try {
final int frameSize = width * height;

int yIndex = 0;
int uvIndex = frameSize;
int pixelStride = mImage.getPlanes()[0].getPixelStride();
int rowStride = mImage.getPlanes()[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();

Bitmap bitmap = Bitmap.createBitmap(getResources().getDisplayMetrics(), width, height, Bitmap.Config.ARGB_8888);

int A, R, G, B, Y, U, V;
int offset = 0;

for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {

// Useful link: https://stackoverflow.com/questions/26673127/android-imagereader-acquirelatestimage-returns-invalid-jpg

R = (buffer.get(offset) & 0xff) > 8) + 128;
//                        V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

Y = (int) Math.round(R *  .299000 + G *  .587000 + B *  .114000);
U = (int) Math.round(R * -.168736 + G * -.331264 + B *  .500000 + 128);
V = (int) Math.round(R *  .500000 + G * -.418688 + B * -.081312 + 128);

// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor
// of 2 meaning for every 4 Y pixels there are 1 V and 1 U.
// Note the sampling is every other pixel AND every other scanline.
yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (i % 2 == 0 && j % 2 == 0) {
yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
}
}
offset += rowPadding;
}

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath(), "/Awesomebitmap.png");
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
} catch (Exception e) {
Timber.e(e, "Converting image to NV21 went wrong.");
}
}
Теперь изображение больше не искажено, но цветность отключена.

Изображение

Правая часть — это растровое изображение, создаваемое в этом цикле, левая — это NV21, сохраненный в виде изображения. Таким образом, пиксели RGB обрабатываются правильно. Очевидно, что цветность отключена, но преобразование RGB в YUV должно быть таким же, как показано в Википедии. Что здесь может быть не так?

Подробнее здесь: https://stackoverflow.com/questions/357 ... v21-format
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»