Java — преобразование JNI YUV_420_888 в RGBA_8888

В настоящее время я создаю приложение для обработки изображений в реальном времени, а затем для отображения. Первый шаг — попытаться отобразить исходный предварительный просмотр, используя Camera2 API и ANativeWindow API. Я передаю каналы y, u, v через JNI отдельно и выполняю преобразование YUV2RGB после Статья в википедии, но получился неправильный вывод цвета Google Pixel — 7.1.0 — API 25 — 1080×1920 на Genymotion:

введите описание изображения здесь

Реализация ImageReader.OnImageAvailableListener :

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

@Override
public void onImageAvailable(ImageReader reader) {
// get the newest frame
Image image = reader.acquireNextImage();

if (image == null) {
return;
}

Image.Plane Y_plane = image.getPlanes()[0];
int Y_rowStride = Y_plane.getRowStride();
Image.Plane U_plane = image.getPlanes()[1];
int U_rowStride = U_plane.getRowStride();
Image.Plane V_plane = image.getPlanes()[2];
int V_rowStride = V_plane.getRowStride();
JNIUtils.RGBADisplay(image.getWidth(), image.getHeight(), Y_rowStride, Y_plane.getBuffer(), U_rowStride, U_plane.getBuffer(), V_rowStride, V_plane.getBuffer(), surface);
image.close();
}
};

JNI:

public static native void RGBADisplay(int srcWidth, int srcHeight, int Y_rowStride, ByteBuffer Y_Buffer, int U_rowStride, ByteBuffer U_Buffer, int V_rowStride, ByteBuffer V_Buffer, Surface surface);

C ++:

const uint8_t NUM_128 = 128;
const uint8_t NUM_255 = 255;

JNIEXPORT void JNICALL Java_tau_camera2demo_JNIUtils_RGBADisplay(
JNIEnv *env,
jobject obj,
jint srcWidth,
jint srcHeight,
jint Y_rowStride,
jobject Y_Buffer,
jint U_rowStride,
jobject U_Buffer,
jint V_rowStride,
jobject V_Buffer,
jobject surface) {uint8_t *srcYPtr = reinterpret_cast<uint8_t *>(env->GetDirectBufferAddress(Y_Buffer));
uint8_t *srcUPtr = reinterpret_cast<uint8_t *>(env->GetDirectBufferAddress(U_Buffer));
uint8_t *srcVPtr = reinterpret_cast<uint8_t *>(env->GetDirectBufferAddress(V_Buffer));

ANativeWindow * window = ANativeWindow_fromSurface(env, surface);
ANativeWindow_acquire(window);
ANativeWindow_Buffer buffer;

//set output size and format
//only 3 formats are available:
//WINDOW_FORMAT_RGBA_8888(DEFAULT), WINDOW_FORMAT_RGBX_8888, WINDOW_FORMAT_RGB_565
ANativeWindow_setBuffersGeometry(window, 0, 0, WINDOW_FORMAT_RGBA_8888);
if (int32_t err = ANativeWindow_lock(window, &buffer, NULL)) {
LOGE("ANativeWindow_lock failed with error code: %d\n", err);
ANativeWindow_release(window);
}

//convert YUV_420_888 to RGBA_8888 and display
uint8_t * outPtr = reinterpret_cast<uint8_t *>(buffer.bits);
for (size_t y = 0; y < srcHeight; y++)
{
uint8_t * Y_rowPtr = srcYPtr + y * Y_rowStride;
uint8_t * U_rowPtr = srcUPtr + (y >> 1) * U_rowStride;
uint8_t * V_rowPtr = srcVPtr + (y >> 1) * V_rowStride;
for (size_t x = 0; x < srcWidth; x++)
{
//from Wikipedia article YUV:
//Integer operation of ITU-R standard for YCbCr(8 bits per channel) to RGB888
//Y-Y, U-Cb, V-Cr
//R = Y + V + (V >> 2) + (V >> 3) + (V >> 5);
//G = Y - ((U >> 2) + (U >> 4) + (U >> 5)) - ((V >> 1) + (V >> 3) + (V >> 4) + (V >> 5));
//B = Y + U + (U >> 1) + (U >> 2) + (U >> 6);
uint8_t Y = Y_rowPtr[x];
uint8_t U = U_rowPtr[(x >> 1)] - NUM_128;
uint8_t V = V_rowPtr[(x >> 1)] - NUM_128;
*(outPtr++) = Y + V + (V >> 2) + (V >> 3) + (V >> 5); //R
*(outPtr++) = Y - ((U >> 2) + (U >> 4) + (U >> 5)) - ((V >> 1) + (V >> 3) + (V >> 4) + (V >> 5)); //G
*(outPtr++) = Y + U + (U >> 1) + (U >> 2) + (U >> 6); //B
*(outPtr++) = NUM_255; // gamma for RGBA_8888
}
}

ANativeWindow_unlockAndPost(window);
ANativeWindow_release(window);
}

Вся демоверсия может быть найдена здесь, на Github: https://github.com/Fung-yuantao/android-camera2demo

ОБНОВИТЬ:

Добавлен следующий код после вызова линии JNIUtils.RGBADisplay:

        Log.d(TAG, "Y plane pixel stride: " + Y_plane.getPixelStride());
Log.d(TAG, "U plane pixel stride: " + U_plane.getPixelStride());
Log.d(TAG, "V plane pixel stride: " + V_plane.getPixelStride());

В Logcat:

09-07 06:40:02.576 5376-5392/tau.camera2demo D/Camera2Demo: Y plane pixel stride: 1
09-07 06:40:02.576 5376-5392/tau.camera2demo D/Camera2Demo: U plane pixel stride: 1
09-07 06:40:02.576 5376-5392/tau.camera2demo D/Camera2Demo: V plane pixel stride: 1

Формат изображения должен быть плоским в соответствии с ответом Алиандро.

0

Решение

Формат вывода изображения для YUV_420_888 может быть в плоском (I420, YV12) или полупланарном (NV12, NV21) формате из документации Вот.

Так как узнать, что это плоский или полуплоскостной формат?

Я думаю, вы можете найти по image.getPlanes()[1].getPixelStride(), Если это 2формат изображения является полуплоскостным форматом и имеет следующий битовый шаблон.

YYYYYYYY UVUVUVUV ...

В моей тестовой среде формат выходного изображения из ImageReader является полуплоскостным

Для полуплоскостей нам нужно обрабатывать только первые две плоскости.

Измените свой код следующим образом.

ANativeWindow_setBuffersGeometry(window, srcWidth, srcHeight, WINDOW_FORMAT_RGBA_8888);
if (int32_t err = ANativeWindow_lock(window, &buffer, NULL)) {
LOGE("ANativeWindow_lock failed with error code: %d\n", err);
ANativeWindow_release(window);
}

//convert YUV_420_888 to RGBA_888 and display
uint8_t * outPtr = reinterpret_cast<uint8_t *>(buffer.bits);
for (size_t y = 0; y < srcHeight; y++)
{
uint8_t * Y_rowPtr = srcYPtr + y * Y_rowStride;
uint8_t * UV_rowPtr = srcUPtr + (y >> 1) * Y_rowStride;
// uint8_t * V_rowPtr = srcVPtr + (y >> 1) * Y_rowStride / 4;
for (size_t x = 0; x < srcWidth; x++)
{
uint8_t Y = Y_rowPtr[x];
size_t uIndex = x & 0xfffffffe;
uint8_t U = UV_rowPtr[uIndex];
uint8_t V = UV_rowPtr[uIndex + 1];
double R = ((Y-16) * 1.164 + (V-128) * 1.596);
double G = ((Y-16) * 1.164 - (U-128) * 0.392 - (V-128) * 0.813);
double B = ((Y-16) * 1.164 + (U-128) * 2.017);
*(outPtr++) = (uint8_t) (R > 255 ? 255 : (R < 0 ? 0 : R));
*(outPtr++) = (uint8_t) (G > 255 ? 255 : (G < 0 ? 0 : G));
*(outPtr++) = (uint8_t) (B > 255 ? 255 : (B < 0 ? 0 : B));
*(outPtr++) = NUM_255; // gamma for RGBA_8888
}
}

Для вашего плоского выходного формата попробуйте использовать метод преобразования YUV2RGB ниже.

for (size_t y = 0; y < srcHeight; y++)
{
uint8_t * Y_rowPtr = srcYPtr + y * Y_rowStride;
uint8_t * U_rowPtr = srcUPtr + (y >> 1) * Y_rowStride / 2;
uint8_t * V_rowPtr = srcVPtr + (y >> 1) * Y_rowStride / 2;
for (size_t x = 0; x < srcWidth; x++)
{
uint8_t Y = Y_rowPtr[x];
uint8_t U = U_rowPtr[(x >> 1)];
uint8_t V = V_rowPtr[(x >> 1)];
double R = ((Y-16) * 1.164 + (V-128) * 1.596);
double G = ((Y-16) * 1.164 - (U-128) * 0.392 - (V-128) * 0.813);
double B = ((Y-16) * 1.164 + (U-128) * 2.017);
*(outPtr++) = (uint8_t) (R > 255 ? 255 : (R < 0 ? 0 : R));
*(outPtr++) = (uint8_t) (G > 255 ? 255 : (G < 0 ? 0 : G));
*(outPtr++) = (uint8_t) (B > 255 ? 255 : (B < 0 ? 0 : B));
*(outPtr++) = NUM_255; // gamma for RGBA_8888
}
}
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]