当前位置: 首页 > 知识库问答 >
问题:

Android摄像头在GPU上将YUV格式预览为RGB格式

戚澄邈
2023-03-14

我已经复制粘贴了我在stackoverflow上找到的一些代码,将默认的相机预览YUV转换成RGB格式,然后上传到OpenGL进行处理。这很好,问题是大部分中央处理器都忙于将YUV图像转换成RGB格式,这变成了瓶颈。

我想将YUV图像上传到GPU,然后在片段着色器中将其转换为RGB。我使用了在CPU上运行的Java YUV to RGB函数,并试图使其在GPU上运行。

这变成了一场小小的噩梦,因为在Java和GPU上进行计算有几个不同之处。首先,预览图像在Java中是字节[],但是字节是有符号的,所以可能会有负值。

此外,片段着色器通常处理[0..1]的浮点值,而不是字节。

我确信这是可以解决的,我几乎解决了。但我花了几个小时试图找出我做错了什么,但还是没能成功。

总之,我要求有人编写这个着色器函数,最好测试一下。对我来说,这将是一个乏味的猴子工作,因为我真的不明白为什么这种转换的工作方式是这样的,我只是试图模仿GPU上相同的功能。

这是一个非常类似的功能,我用在Java:显示YUV图像在Android

我在CPU上做了一些工作,比如将1.5*wh字节YUV格式转换为wh*YUV格式,如下所示:

static public void decodeYUV420SP(int[] rgba, 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 = (int) yuv420sp[yp]+127;
            if ((i & 1) == 0) {
                v = (int)yuv420sp[uvp++]+127;
                u = (int)yuv420sp[uvp++]+127;
            }
            rgba[yp] = 0xFF000000+(y<<16) | (u<<8) | v;
        }
    }
}

添加了127,因为字节是有符号的。然后我将rgba加载到OpenGL纹理中,并尝试在GPU上完成其余的计算。

任何帮助都会被告知。。。

共有3个答案

徐经武
2023-03-14

2011年2月,Renderscript首次推出。自从Android3.0蜂巢(API 11)和Android4.2 JellyBean(API 17)加入ScriptIntrinsicYuvToRGB以来,最简单、最有效的解决方案就是使用renderscript进行YUV到RGB的转换。我最近推广了这个解决方案来处理设备旋转。

扶隐水
2023-03-14

在CPU上转换听起来很容易,但我相信问题是如何在GPU上做到这一点?

我最近在我的项目中做了这件事,我需要得到非常快的二维码检测,即使相机的角度是45度到打印代码的表面,它的工作性能很好:

(以下代码只是为了包含关键行而进行了裁剪,假设您对Java和OpenGLES都有很好的理解)

>

int[] txt = new int[1];
GLES20.glGenTextures(1,txt,0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,txt[0]);
GLES20.glTextParameterf(... set min filter to GL_LINEAR );
GLES20.glTextParameterf(... set mag filter to GL_LINEAR );
GLES20.glTextParameteri(... set wrap_s to GL_CLAMP_TO_EDGE );
GLES20.glTextParameteri(... set wrap_t to GL_CLAMP_TO_EDGE );

请注意,纹理类型不是GL_texture_2D。这一点很重要,因为SurfaceTexture对象只支持GL_TEXTURE_EXTERNAL_OES类型,这将在下一步中使用。

设置SurfaceTexture:

SurfaceTexture surfTex = new SurfaceTeture(txt[0]);
surfTex.setOnFrameAvailableListener(this); 

上面假设“this”是一个实现“onFrameAvailable”函数的对象。

    public void onFrameAvailable(SurfaceTexture st)
    {
            surfTexNeedUpdate = true;
            // this flag will be read in GL render pipeline
    }

设置相机:

Camera cam = Camera.open();
cam.setPreviewTexture(surfTex);

如果你的目标是Android5.0,那么这个摄像头API是不推荐的,所以如果你是,你必须使用新的摄像头API。

在渲染管道中,使用以下块检查摄影机是否有可用的帧,并使用它更新表面纹理。更新表面纹理时,将填充与其链接的GL纹理。

if( surfTexNeedUpdate )
{
        surfTex.updateTexImage();
        surfTexNeedUpdate = false;
}

绑定带有摄像头的GL纹理-

GLES20.glBindTexture(GLES20.GL_TEXTURE_EXTERNAL_OS, txt[0]);

不用说,您需要设置当前活动纹理。

在将在片段部分使用上述纹理的GL着色器程序中,必须有第一行:

#extension GL_OES_EGL_imiage_external : require

以上是必备。

纹理均匀性必须为Externaloes类型:

    uniform samplerExternalOES u_Texture0;

从中读取像素就像从GL_纹理_2D类型读取像素一样,UV坐标在相同的范围内(从0.0到1.0):

    vec4 px = texture2D(u_Texture0, v_UV);

一旦你有你的渲染管道准备渲染上面的纹理和着色器,只需启动相机:

cam.startPreview();

你应该可以在GL屏幕上看到四元摄像头的实时视频。现在,您只需抓取带有glReadPixels的图像:

GLES20.glReadPixels(0,0,width,height,GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, bytes);

上行假设您的FBO是RGBA,字节已经初始化为字节[]数组的适当大小,宽度和高度是FBO的大小。

瞧!您已经从相机中捕获了RGBA像素,而不是转换在onPreviewFrame回调中接收的YUV字节...

你也可以使用RGB帧缓冲对象,如果你不需要它,可以避免alpha。

需要注意的是,摄像机将在自己的线程中调用不是GL渲染管道线程的onFramePrime,因此您不应该在该函数中执行任何GL调用。

田巴英
2023-03-14

我使用维基百科的代码计算GPU上从YUV到RGB的转换:

private static int convertYUVtoRGB(int y, int u, int v) {
    int r,g,b;

    r = y + (int)1.402f*v;
    g = y - (int)(0.344f*u +0.714f*v);
    b = y + (int)1.772f*u;
    r = r>255? 255 : r<0 ? 0 : r;
    g = g>255? 255 : g<0 ? 0 : g;
    b = b>255? 255 : b<0 ? 0 : b;
    return 0xff000000 | (b<<16) | (g<<8) | r;
}

我把浮点数改成了0.0。。255.0,然后使用上述代码。CPU上的部分是将原始YUV像素重新排列成YUV矩阵(也在wikipdia中显示)。基本上我使用了维基百科的代码,做了最简单的浮点运算

 类似资料:
  • 我正在为我的Android应用程序使用此相机预览应用程序。 我想要全屏幕的相机预览。因此,我使用Android API中的示例尝试将预览设置为全屏。这就是我试图做到的: 我使用相对布局作为我的布局。我的布局设置如下: 我仍然无法在整个屏幕上预览我的相机。我想知道如何预览整个屏幕。

  • 我的意图是有一个方形相机预览。 我没有1:1比例的相机预览可用(我在Nexus 5模拟器中),所以我的最佳尺寸可用为640 x 480,所以为了适应正方形预览,我将SurfaceView尺寸设置为与纵横比相等,但将屏幕外的边距偏移一半,以在屏幕上只保留裁剪的正方形。表面视图本身的大小和偏移量是正确的,但是相机预览本身仍然在正方形内显示压缩后的预览,而不是以正确的纵横比部分地在屏幕外显示。 有什么想

  • 我试图捕捉一个YUV图像,但被拉伸了。我正在使用Android Camera2,访问https://github.com/googlearchive/android-camera2basic。Android SDK是28。 当我使用ImageReader从setOnImageAvailableListener()方法捕获YUV2048x1536中的相机帧时,我面临一个奇怪的行为。捕获是拉伸的: Y

  • 本文向大家介绍在Python中使用OpenCV将RGB格式的图像转换为HSV格式的图像,包括了在Python中使用OpenCV将RGB格式的图像转换为HSV格式的图像的使用技巧和注意事项,需要的朋友参考一下 的HSV是另一种类型的颜色空间,其中H代表色相,S表示饱和度和V代表值。 一个色调代表颜色。从0度到360度的角度。 角度 颜色 0-60 红 60-120 黄色 120-180 绿色 180

  • 我的问题是:我在Android系统中安装了一个摄像头,并使用onPreviewFrame侦听器接收预览数据,该侦听器会向我传递一个字节[]数组,其中包含默认AndroidYUV格式的图像数据(设备不支持R5G6B5格式)。每个像素由12位组成,这让事情有点棘手。现在我想做的是将YUV数据转换成ARGB数据,以便对其进行图像处理。为了保持高性能,这必须使用renderscript来完成。 我的想法是

  • 我已经和https://github.com/commonsguy/cwac-camera 创建了我的自定义CameraFragment和自定义CameraHost。 我已经重写了getPictureSize和getPreviewSize。通过这两种方法,我能够找出可以使用的大小。我的总体想法是拍摄一幅方形图像,并在拍摄前预览它。 我一直在测试,没有设备返回正方形格式的大小,所以我想最好的方法是选