美文网首页opengl
Android基于Shader的图像处理(6)-Camera开发

Android基于Shader的图像处理(6)-Camera开发

作者: andev009 | 来源:发表于2018-09-30 14:29 被阅读121次

    完整代码位置:AndroidShaderDemo

    Camera的预览方向和拍照方向
    YUV 数据格式完全解析
    MagicCamera

    这里讲下Shader在相机开发里怎么应用。
    相机预览的图像通过GLSurfaceView来展示,自定义一个CameraView来显示图像:

    public class CameraView extends GLSurfaceView implements GLSurfaceView.Renderer{
    

    Renderer接口的三个方法要在CameraView里实现。因为要使用Shader来处理预览图像,这里就需要先将预览图像保存在一个地方,然后把该图像作为纹理对象来处理。Android提供了SurfaceTexture来保存预览图像,SurfaceTexture和一个纹理对象绑定在一起。因为预览图像的格式是YUV,而不是普通RGB格式,所以生成这个纹理对象方式和一般不一样,生成纹理方法如下:

    public static int getExternalOESTextureID() {
            int[] texture = new int[1];
            GLES20.glGenTextures(1, texture, 0);
            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0]);//重点看这行
            GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                    GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
            GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                    GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                    GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
            GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                    GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
            return texture[0];
        }
    

    glBindTexture绑定的是GLES11Ext.GL_TEXTURE_EXTERNAL_OES,猜测是硬件来解析图像格式,生成纹理。
    有了纹理对象后,将该纹理和SurfaceTexture绑定在一起,代码如下:

    private void initSurfaceTexture(){
        if (textureId == TextureHelper.NO_TEXTURE) {
         textureId = TextureHelper.getExternalOESTextureID();//生成纹理
        if (textureId != TextureHelper.NO_TEXTURE) {
            surfaceTexture = new SurfaceTexture(textureId);//两个绑定
       surfaceTexture.setOnFrameAvailableListener(onFrameAvailableListener);
        }
       }
    }
    

    接收预览图像的地方有了,现在可以在onSurfaceChanged回调里打开相机了:

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        glViewport(0, 0, width, height);
        surfaceWidth = width;
        surfaceHeight = height;
        openCamera();
    }
    

    在openCamera()方法里,首先配置相机参数,然后将之前接收图像的SurfaceTexture传给相机,代码如下:

     camera.setPreviewTexture(surfaceTexture);
    

    现在相关配置都好了,开始接收图像到手机上显示。这里有两种显示方法,一种是直接显示相机预览图像,一种是对预览图像处理后再显示(就是相机滤镜,特效等)。先说下直接显示预览图像。
    在onDrawFrame里首先要先调用surfaceTexture.updateTexImage();更新预览的图像到纹理,然后调用surfaceTexture.getTransformMatrix(mtx);获得一个矩阵,用于之后的图像变换(图像翻转之类)。有了纹理,之后的渲染就和之前文章中的各种render方式一样了。所不同的还是因为图像是YUV格式,所以使用该纹理时要用 glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);这里封装成CameraInputFilter和SimpleTextureOESProgram来专门出来预览图像渲染。完整的onDrawFrame代码如下:

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    
        if(surfaceTexture == null)
            return;
        surfaceTexture.updateTexImage();
        float[] mtx = new float[16];
        surfaceTexture.getTransformMatrix(mtx);
        if(cameraInputFilter == null){
            cameraInputFilter = new CameraInputFilter(context, vertexArray);
            }
    
        cameraInputFilter.setTextureTransformMatrix(mtx);
    
        cameraInputFilter.onDrawFrame(textureId);
        }
    }
    

    程序跑下,发现图像显示不对,图像是反的,还记得前面提到的 surfaceTexture.getTransformMatrix(mtx);吗?将该 mtx传给Vertex Shader,将纹理坐标变换下,就可以得到正确的图像了。

    void main()
    {
        v_TextureCoordinates = (u_textureTransform * a_TextureCoordinates).xy;
        gl_Position = a_Position;
    }
    

    下面讲下将预览图像处理后再显示。
    有时候需要将预览图像当做纹理,然后再将普通纹理传给各种滤镜Shader。转化的方式就是使用帧缓存,过程就是不把SurfaceTexture的纹理显示到屏幕上,而是先显示在帧缓存里,然后再将帧缓存绑定的纹理传个各个Shader。
    生成帧缓存对象的方法,前面文章有提到,这里不再说。在CameraInputFilter里通过onDrawToTexture方法将预览图像渲染到帧缓存。然后将返回的纹理id交给各种Shader,Demo里用ImageFilter来接收这个纹理id,并使用GrayTextureProgram把预览图像变成灰度图显示出来。

    以上就是处理相机预览图像的流程。但是很多情况下,因为相机的翻转,纹理的坐标不匹配,导致显示图像不对。这里参考了MagicCamera项目,在openCamera里,根据相机的一些参数,先生成纹理坐标,代码如下:

    protected void adjustSize(int rotation, boolean flipHorizontal, boolean flipVertical){
        float[] textureCords = TextureRotationUtil.getRotation(Rotation.fromInt(rotation),
                    flipHorizontal, flipVertical);
        float[] cube = TextureRotationUtil.CUBE;
    
    
        float[] newCube = new float[]{
                    cube[0], cube[1], textureCords[0],textureCords[1],
                    cube[2], cube[3], textureCords[2],textureCords[3],
                    cube[4], cube[5], textureCords[4],textureCords[5],
                    cube[6], cube[7], textureCords[6],textureCords[7]
            };
    
        vertexArray = new VertexArray(newCube);
    }
    

    相关文章

      网友评论

        本文标题:Android基于Shader的图像处理(6)-Camera开发

        本文链接:https://www.haomeiwen.com/subject/hjjwoftx.html