相机拍照加入滤镜效果

前言:

  • 在前面的文章中给小伙伴们介绍了进行Camera预览及实时美颜效果的实现,如果你还没有看过的话,建议先去看上一篇文章《Android 短视频开发之摄像头预览(二》Android 短视频编辑开发之摄像头预览实时美颜(三)
  • 本篇文章会介绍如何实现摄像头捕捉的画面进行拍照留存

原理:

    拍照其实就很简单了,主要是在执行拍照动作后把摄像头回调的数据进行处理后保存到本地就可以了

实现思路:

前面的预览及美颜过程就不罗嗦了,不懂可以看前面的文章。

这里我采用两种方式实现拍照

  1. 在onDrawFrame(GL10 gl)回调方法中获取当前EGL中渲染过的图像数据(由于这里每帧都会回调,要注意性能问题)
  2. 拿到摄像头捕获的原始数据放到GLSurfaceView的渲染队列中进行OpengGL渲染后(涉及到FrameBuffer的使用),再从EGL环境下获取渲染后的图片数据进行保存

具体实现

代码实现方式一:

@Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);
        if(surfaceTexture == null)
            return;
        surfaceTexture.updateTexImage();  //获取最新的数据
       
        float[] mtx = new float[16];
        surfaceTexture.getTransformMatrix(mtx);
        cameraInputFilter.setTextureTransformMatrix(mtx);
        int id = textureId;
        //进行OpenGL渲染
        if(filter == null){
            cameraInputFilter.onDrawFrame(textureId, gLCubeBuffer, gLTextureBuffer);
        }else{
            id = cameraInputFilter.onDrawToTexture(textureId);
            filter.onDrawFrame(id, gLCubeBuffer, gLTextureBuffer);
        }
      
        if(isTakePicture){
            
            CameraInfo info = CameraEngine.getCameraInfo();
            int width = getWidth();
            int height = getHeight();
            GLES20.glViewport(0,0,width,height);
            IntBuffer ib = IntBuffer.allocate(width * height);
            //获取EGL环境的图像数据
            GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib); 
            Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            result.copyPixelsFromBuffer(IntBuffer.wrap(ib.array()));
            //            Bitmap result = createBitmapFromGLSurface(0,0,getWidth(),getHeight(), gl);
            //异步保存图片
            new SavePictureTask(getOutputMediaFile(),null).execute(result);
            isTakePicture = false;
        }
    }

这里有很多优化空间,我这里只提供一种实现方式具体优化,可以根据情况自行处理。

方式二实现 

    1.先获取摄像头原始数据的bitmap图像

CameraEngine.takePicture(null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                CameraEngine.stopPreview();
                //摄像头原始图像数据
                final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                //要利用OpenGl 绘图暂时停止摄像头预览,并且渲染操作要放到EGLContenxt环境下
                queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        //把摄像头获取的bitmap图片经过Opengl滤镜处理后返回保存
                        final Bitmap photo = drawPhoto(bitmap,CameraEngine.getCameraInfo().isFront);
                        GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight);
                        if (photo != null)
                            savePictureTask.execute(photo);
                    }
                });
                CameraEngine.startPreview();
            }
        });

   2.通过drawPhoto方法使用OpenGL 处理源图像数据

private Bitmap drawPhoto(Bitmap bitmap,boolean isRotated){
        //设置最终生成的图片的宽高
        CameraInfo info = CameraEngine.getCameraInfo();
        int width = info.previewHeight;
        int height = info.previewWidth;

        if(beautyFilter == null)
            beautyFilter = new MagicBeautyFilter();
        beautyFilter.init();
        beautyFilter.onDisplaySizeChanged(width, height);
        beautyFilter.onInputSizeChanged(width, height);

        if(filter != null) {
            filter.onInputSizeChanged(width, height);
            filter.onDisplaySizeChanged(width, height);
        }


        //创建并使用FBO //使用帧缓冲绘制渲染到离屏的buffer中
        int[] mFrameBuffers = new int[1];
        int[] mFrameBufferTextures = new int[1];
        GLES20.glGenFramebuffers(1, mFrameBuffers, 0);
        GLES20.glGenTextures(1, mFrameBufferTextures, 0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFrameBufferTextures[0]);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
                GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);
        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
                GLES20.GL_TEXTURE_2D, mFrameBufferTextures[0], 0);

        //设置渲染窗口大小
        GLES20.glViewport(0, 0, width, height);
        //加载原始图片到纹理对象中
        int textureId = OpenGlUtils.loadTexture(bitmap, OpenGlUtils.NO_TEXTURE, true);

        FloatBuffer gLCubeBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.CUBE.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        FloatBuffer gLTextureBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.TEXTURE_NO_ROTATION.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        gLCubeBuffer.put(TextureRotationUtil.CUBE).position(0);
        //这是纹理是否要旋转,因为前置摄像头拍照时反转了的
        if(isRotated)
            gLTextureBuffer.put(TextureRotationUtil.getRotation(Rotation.NORMAL, false, false)).position(0);
        else
            gLTextureBuffer.put(TextureRotationUtil.getRotation(Rotation.NORMAL, false, true)).position(0);


        if(filter == null){
            //只要美颜滤镜直接渲染
            beautyFilter.onDrawFrame(textureId, gLCubeBuffer, gLTextureBuffer);
        }else{
            //双滤镜叠加,首先美颜渲染到原始图片纹理上
            beautyFilter.onDrawFrame(textureId);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
            //然后颜色滤镜渲染到帧缓存FBO上
            filter.onDrawFrame(mFrameBufferTextures[0], gLCubeBuffer, gLTextureBuffer);
        }

        //从EGL环境下获取图片数据
        IntBuffer ib = IntBuffer.allocate(width * height);
        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib);
        Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        result.copyPixelsFromBuffer(ib);

        //释放资源
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        GLES20.glDeleteTextures(1, new int[]{textureId}, 0);
        GLES20.glDeleteFramebuffers(mFrameBuffers.length, mFrameBuffers, 0);
        GLES20.glDeleteTextures(mFrameBufferTextures.length, mFrameBufferTextures, 0);

        beautyFilter.destroy();
        beautyFilter = null;
        if(filter != null) {
            filter.onDisplaySizeChanged(surfaceWidth, surfaceHeight);
            filter.onInputSizeChanged(imageWidth, imageHeight);
        }
        return result;
    }

 

这样就能把处理后的图片给保存下来了。是不是很简单?

如有不当之处还请指正。