简体   繁体   English

JNI YUV_420_888到RGBA_8888的转换

[英]JNI YUV_420_888 to RGBA_8888 conversion

Currently I'm building an app to do real-time image processing and then display. 目前,我正在构建一个应用程序以进行实时图像处理然后显示。 The first step is to try displaying the original preview using Camera2 API and ANativeWindow API. 第一步是尝试使用Camera2 API和ANativeWindow API显示原始预览。 I pass the y, u, v channels through JNI separately and do YUV2RGB conversion following the Wikipedia article , but got wrong color output running on Google Pixel - 7.1.0 - API 25 - 1080x1920 on Genymotion : 我分别通过JNI传递了y,u,v通道,并在Wikipedia文章之后进行YUV2RGB转换,但是在Google Pixel-7.1.0-API 25-1080x1920Genymotion上运行了错误的颜色输出:

在此处输入图片说明

Implementation of ImageReader.OnImageAvailableListener : 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: 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++: 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);
}

The whole demo could be found here on Github: https://github.com/Fung-yuantao/android-camera2demo 整个演示可以在Github上找到: https : //github.com/Fung-yuantao/android-camera2demo

UPDATE : 更新

Added the following code after the line calling JNIUtils.RGBADisplay : 在调用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());

In Logcat: 在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

The image format should be planar according to the answer from alijandro. 根据alijandro的回答,图像格式应为平面。

The image output format for YUV_420_888 might be planar(I420, YV12) or semiplanar(NV12, NV21) format, from the documentation here . 用于图像输出格式YUV_420_888可能是平面的(I420,YV12)或semiplanar(NV12,NV21)格式,从文档这里

So how to know it's planar or semi-planar format? 那么如何知道它是平面还是半平面格式呢?

I guess you can find by image.getPlanes()[1].getPixelStride() . 我猜你可以通过image.getPlanes()[1].getPixelStride() If it's 2 , the image format is semi-planar format and has the following bit pattern. 如果为2 ,则图像格式为半平面格式,并具有以下位模式。

YYYYYYYY UVUVUVUV ...

In my test environment, the output image format from ImageReader is semi-planar. 在我的测试环境中, ImageReader的输出图像格式是半平面的。

For semi-planar, we only need to handle the first two planar. 对于半平面,我们只需要处理前两个平面。

Change your code like following. 如下更改代码。

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

For your planar output format, try use the YUV2RGB transform method below. 对于平面输出格式,请尝试使用下面的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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM