OpenGLES:glReadPixels()获取相机GLSurfaceView预览数据并保存

Android现行的Camera API2机制可以通过onImageAvailable(ImageReader reader)回调从底层获取到Jpeg、YuvRaw三种格式的Image,然后通过保存Image实现拍照功能,但是却并没有Api能直接在上层直接拿到实时预览的数据。

Android Camera预览的实现是上层下发SurfaceCameraHAL,由CameraHAL也就是android.hardware.camera.provider@2.4-service进程往Surface对应的Buffer中填充预览数据,然后再copySurfaceFling中由OpenGL进行渲染显示。

实际相机开发中,不仅仅只是要实现预览,还经常需要拿到预览数据做一些特效处理,那么问题来了,怎么在相机App获取到实时预览数据呢?

这跟上层Camera App用于显示SurfaceView控件有关:

  • 如果上层使用的是GLSurfaceView,可以直接通过OpenGLESglReadPixels()获取到copy到显存中的预览数据
  • 如果上层使用的不是GLSurfaceView,可以通过自己搭建EGL环境,然后在EGL环境中调用OpenGLESglReadPixels()获取到预览数据。

GLSurfaceView其实就是Android封装好的EGL+SufaceView控件,Android的所有渲染最终都是通过OpenGL来实现的,所以万变不离其宗,本质上上层Camera App都只能通过OpenGLESglReadPixels()实现预览数据的获取。

一个SurfaceAndroid EGL中对应一个FrameBuffer,学习过OpenGL的应该都知道,一个FrameBuffer会有多个附着(attachment),其中必须且只能有一个ColorBuffer附着,有一个或多个StencilBufferDepthBuffer附着

glReadPixels()仅限于读取ColorBuffer,无法读取DepthBufferStencilBuffer,它可以将图像内容从显存读取到内存中,将ColorBuffer中的像素值保存到预分配的内存缓冲区。

前面关于OpenGLES的博文中,有两篇是使用OpenGLES实现相机的相关功能,一篇是《OpenGLES:GLSurfaceView实现Android Camera预览》,一篇是《OpenGLES:相机实时滤镜四宫格、九宫格》,今天就在这两篇博文基础上实现相机预览数据的获取和保存。

相机实现部分在此不做过多讲解,有兴趣的可以参看前面两篇博文,有详细的讲解
本文主要展示glReadPixels()对相机预览数据获取的实现

代码实现其实很简单
GLSurfaceView.Renderer实现类的onDrawFrame(GL10 gl)函数中新增如下代码段:

if (shouldTakePic) {//预览尺寸int w = 1080;int h = 1440;//预览数据保存成照片的目录String savePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/MyCamera/";int[] iat = new int[w * h];IntBuffer ib = IntBuffer.allocate(w * h);//(0,580)距离屏幕左下角的距离,与glViewport(0, 580,...)保持一致glReadPixels(0, 580, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ib);int[] ia = ib.array();//glReadPixels 读取的内容是上下翻转的,要处理一下for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {iat[(h - i - 1) * w + j] = ia[i * w + j];}}Bitmap inBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);inBitmap.copyPixelsFromBuffer(IntBuffer.wrap(iat));ByteArrayOutputStream bos = new ByteArrayOutputStream();inBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bos);byte[] bitmapData = bos.toByteArray();File tempDir = new File(savePath);tempDir.mkdirs();String fileName = "temp_" + System.currentTimeMillis() + ".jpeg";File imgFile = new File(savePath, fileName);try {FileOutputStream output = new FileOutputStream(imgFile);output.write(bitmapData);output.flush();output.close();Log.v(TAG, "ImageReader X");} catch (Exception e) {e.printStackTrace();} finally {inBitmap.recycle();}
}

glReadPixels读取的内容上下翻转处理还有另外一种实现,
原理都是一样的,实现起来大同小异

if (shouldTakePic) {//预览尺寸int w = 1080;int h = 1440;//预览数据保存成照片的目录String savePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/MyCamera/";int b[] = new int[(int) (w * h)];int bt[] = new int[(int) (w * h)];IntBuffer buffer = IntBuffer.wrap(b);buffer.position(0);//(0,580)距离屏幕左下角的距离,与glViewport(0, 580,...)保持一致glReadPixels(0, 580, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {int pix = b[i * w + j];int pb = (pix >> 16) & 0xff;int pr = (pix << 16) & 0x00ff0000;int pix1 = (pix & 0xff00ff00) | pr | pb;bt[(h - i - 1) * w + j] = pix1;}}Bitmap inBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);inBitmap.copyPixelsFromBuffer(buffer);inBitmap = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);ByteArrayOutputStream bos = new ByteArrayOutputStream();inBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bos);byte[] bitmapData = bos.toByteArray();ByteArrayInputStream fis = new ByteArrayInputStream(bitmapData);String tempPicFile = "temp_" + System.currentTimeMillis() + ".jpeg";File tempDir = new File(savePath);tempDir.mkdirs();try {File tmpFile = new File(tempDir, tempPicFile);FileOutputStream fos = new FileOutputStream(tmpFile);byte[] buf = new byte[1024];int len;while ((len = fis.read(buf)) > 0) {fos.write(buf, 0, len);}fis.close();fos.close();inBitmap.recycle();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
}

验证下效果,抓两张预览照试试:

抓一张普通预览:

抓一张四宫格滤镜预览:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/264205.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cache教程1.LRU 缓存淘汰策略

这一节实现LRU算法&#xff0c;要理解明白其使用的数据结构。 FIFO/LFU/LRU 算法简介 Cache的缓存全部存储在内存中&#xff0c;内存是有限的&#xff0c;因此不可能无限制地添加数据。当占用内存超过了给定的内存大小时候&#xff0c;就需要从缓存中移除一条或多条数据了。我…

【机器学习】042_迁移学习

一、概述、定义 目的&#xff1a; 迁移学习的目的是将某个领域或任务上学习到的模式、知识应用到不同但相关的领域里&#xff0c;获取更多数据&#xff0c;而不必投入许多时间人力来进行数据的标注。 举例&#xff1a; 已经会下中国象棋&#xff0c;就可以类比着来学习国际…

shell脚本实战之mysql-物理备份实现周内备份策略

一.shell脚本要求 # 数据库备份脚本方式: 物理备份# 工具:xtrabackup过程# 需求1:不允许有任何的安在脚本中&#xff0c;# 需求2:在脚本中&#xff0c;不允许有任何的交互式代码容&#xff0c;使用日志记录# 需求3:在脚本中&#xff0c;不允许有任何输出# 需求4:每周为一个循环…

SSD数据在写入NAND之前为何要随机化?-part2

接part1介绍&#xff1a; 如何达到这个目的&#xff1f;业内常用的是对写入数据的数据进行随机化处理&#xff0c;这部分主要在SSD控制器中通过硬件实现。 上图b/c&#xff1a;在控制器芯片通过硬件方式实现随机化的读写流程&#xff0c;这个也是业内通常做法。随机化处理是在写…

JVM虚拟机:命令行查看JVM垃圾回收器的执行信息

在eclipse中打开命令行窗口 window->show view->Terminal 这样就打开了Terminal窗口&#xff0c;效果如下所示&#xff1a; java -XX:PrintCommandLineFlags -version 这个命令可以查看一些配置信息&#xff0c;其中最重要的配置信息就是&#xff0c;当前使用的G1回收器…

机器学习算法性能评估常用指标总结

考虑一个二分问题&#xff0c;即将实例分成正类&#xff08;positive&#xff09;或负类&#xff08;negative&#xff09;。对一个二分问题来说&#xff0c;会出现四种情况。如果一个实例是正类并且也被 预测成正类&#xff0c;即为真正类&#xff08;True positive&#xff0…

Java到底是什么?学了我们能做什么?

一、Java是什么&#xff1f; Java是一门面向对象编程语言&#xff0c;不仅吸收了C语言的各种优点&#xff0c;还摒弃了C里难以理解的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表&#xff0c;极好地实…

软件设计中如何画各类图之九活动图:系统行为与流程的可视化描述

目录 1 前言2 活动图的符号及说明2.1 开始和结束节点2.2 活动&#xff08;Action&#xff09;2.3 控制流&#xff08;Control Flow&#xff09;2.4 决策节点&#xff08;Decision Node&#xff09;2.5 合并节点&#xff08;Merge Node&#xff09; 3 画活动图的步骤3.1 确定活动…

Vue3拖拽功能【VueDraggablePlus】

一、介绍 由于 Sortablejs 的 vue3 组件一直没有更新&#xff0c;已经跟 vue3 严重脱节&#xff0c;所以诞生了这个项目&#xff0c;这个组件是基于 Sortablejs 的&#xff0c;所以如果你想了解更多关于 Sortablejs 的信息&#xff0c;可以查看Sortablejs 官网。VueDraggableP…

WIFI直连(Wi-Fi P2P)

一、概述 Wifi peer-to-peer&#xff08;也称Wifi-Direct&#xff09;是Wifi联盟推出的一项基于原来WIfi技术的可以让设备与设备间直接连接的技术&#xff0c;使用户不需要借助局域网或者AP&#xff08;Access Point&#xff09;就可以进行一对一或一对多通信。这种技术的应用…

微表情检测(三)----基于光流特征的微表情检测

Micro-expression spotting based on optical flow features 基于光流特征的微表情检测 Abstract 本文提出了一种高精度和可解释性的自动微表情检测方法。首先&#xff0c;我们设计了基于鼻尖位置的图像对齐方法&#xff0c;以消除由头部晃动引起的全局位移。其次&#xff0…