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.mzph.cn/news/212337.shtml

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

相关文章

Java学习笔记——instanceof关键字

instanceof关键字&#xff1a; 作用&#xff1a;保证对象向下转型的安全性在对象向下转型前判断某一对象实例是否属于某个类 判断时&#xff0c;如果对象是null&#xff0c;则 instanceof 判断结果为 false

Tr0ll

信息收集 探测主机存活信息&#xff1a; nmap -sn --min-rate 10000 192.168.182.0/24Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-14 15:45 CST Nmap scan report for 192.168.182.1 Host is up (0.00026s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap…

Linux环境下socket本地通信

最近项目有用到了socket本地通信&#xff0c;故复习一下。之前都是基于本地虚拟机的ip地址通信的&#xff0c;现在项目&#xff0c;Linux单板上面有2个进程需要通信&#xff0c;故用到了本地socket通信&#xff0c;主要其实就是用了sockfd,文件描述符&#xff0c;也叫句柄。 服…

java接入gpt开发

前情提要 本次文章使用编译器为IDEA2020 使用GPT模型为百度旗下的千帆大模型 如果是个人用或者不流传出去&#xff0c;可以无脑入&#xff0c;因为会免费送20块钱&#xff08;够用上万次&#xff09; 代金卷查看 正式教程&#xff1a; 百度智能云控制台 (baidu.com) 按照步…

JMS(Java Message Service)使用指南

介绍 JMS即Java消息服务&#xff08;Java Message Service&#xff09;应用程序接口&#xff0c;是一个Java平台中关于面向消息中间件&#xff08;MOM&#xff09;的API&#xff0c;用于在两个应用程序之间&#xff0c;或分布式系统中发送消息&#xff0c;进行异步通信。它是一…

基于单片机智能病床呼叫系统设计

**单片机设计介绍&#xff0c;基于单片机智能病床呼叫系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能病床呼叫系统是一种利用单片机技术设计的医疗设备&#xff0c;它能够帮助病人在住院期间快速、方便…

国内大厂机器人赛道产品

大疆 大疆无人机自然不必说&#xff0c;除此之外大疆搞机甲大师&#xff0c;教育机器人。 字节 当前字节在机器人领域只是初步探索阶段&#xff0c;目前尚未发布相关产品&#xff08;截止至23.12&#xff09;。 管理层想法&#xff1a; 跟已有业务做结合&#xff0c;服务好…

Java设计模式分类

java的设计模式大体上分为三大类&#xff1a; 创建型模式&#xff08;5种&#xff09;&#xff1a;工厂方法模式&#xff0c;抽象工厂模式&#xff0c;单例模式&#xff0c;建造者模式&#xff0c;原型模式。 结构型模式&#xff08;7种&#xff09;&#xff1a;适配器模式&am…

传感器(一) :IMU / 陀螺仪模块

IMU / 陀螺仪模块 一、概述二、注意参数2.1 陀螺仪芯片标准&#xff08;MPU6050)2.2 参数说明 三、IMU模式使用注意事项3.1 IMU模块安装注意事项3.2 为什么IMU要安装在机器中心位置 四、常见陀螺仪芯片品牌 一、概述 IMU全称为惯性测量单元&#xff0c;可以通过测量物体在三维空…

Linux实用操作

一、各类小技巧&#xff08;快捷键&#xff09; 1.1 ctrl c 强制停止 Linux某些程序的运行&#xff0c;如果想要强制停止它&#xff0c;可以使用快捷键ctrl c 命令输入错误&#xff0c;也可以通过快捷键ctrl c&#xff0c;退出当前输入&#xff0c;重新输入 1.2 ctrl d…

ReLU(Rectified Linear Unit)和Sigmoid激活函数

ReLU&#xff08;Rectified Linear Unit&#xff09;和Sigmoid都是神经网络中常用的激活函数。 特点&#xff1a; ReLU是一种简单而有效的激活函数。它对于正数部分直接返回输入&#xff0c;对于负数部分返回零。这种非线性转换有助于网络学习更复杂的表示。ReLU在许多深度学习…

自治调优!人大金仓解放DBA双手

数据库系统的性能是确保整个应用系统高效运转的关键因素&#xff0c;因此数据库性能调优工作至关重要。KingbaseES通过将人工调优过程内化为数据库内核&#xff0c;成功实现了自治调优。这种创新的调优方案为DBA提供了更高效且准确的性能调优途径&#xff0c;同时也显著降低了数…

构建VREP和MATLAB联合仿真实验平台,控制机械臂末端按照固定轨迹移动

构建VREP和MATLAB联合仿真实验平台&#xff0c;控制机械臂末端按照固定轨迹移动。主要工作如下&#xff1a; &#xff08;1&#xff09;solidworks构建机械臂模型&#xff1b; &#xff08;2&#xff09;将solidworks中构建的模型导入VREP中建立机械臂的多体动力学模型&#xf…

【Unity动画】什么是任意状态(Any state)

&#xff08;Any state&#xff09;可以从某个状态A直接切换到另一个状态 B\C\D\E\F 比如A到C的过渡&#xff0c;直接设置从Any state 到C的过渡线触发参数即可。而不需要让A到C直接在连接&#xff0c;同样&#xff0c;B到C之间也无需直接链接。 这样设计是在每一个动画之间都…

【论文笔记】Gemini: A Family of Highly Capable Multimodal Models——细看Gemini

Gemini 【一句话总结&#xff0c;对标GPT4&#xff0c;模型还是transformer的docoder部分&#xff0c;提出三个不同版本的Gemini模型&#xff0c;Ultra的最牛逼&#xff0c;Nano的可以用在手机上。】 谷歌提出了一个新系列多模态模型——Gemini家族模型&#xff0c;包括Ultra…

Java 何时会触发一个类的初始化

Java 何时会触发一个类的初始化&#xff1f; 使用new关键字创建对象访问类的静态成员变量 或 对类的静态成员变量进行赋值调用类的静态方法反射调用类时&#xff0c;如 Class.forName()初始化子类时&#xff0c;会先初始化其父类&#xff08;如果父类还没有进行过初始化的话&a…

找不到WMVCORE.dll怎么办?一键解决WMVCORE.dll缺失的详细方法分享

当打开软件时提示wmvcore.dll丢失&#xff0c;这可能是由于以下几个原因导致的&#xff1a; 系统文件损坏&#xff1a;wmvcore.dll是系统文件&#xff0c;可能会因为各种原因&#xff08;如病毒感染、系统错误、软件卸载等&#xff09;而损坏。 软件依赖问题&#xff1a;某些…

用 Python 自动创建 Markdown 表格

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Markdown表格是文档中整理和展示数据的重要方式之一。然而&#xff0c;手动编写大型表格可能会费时且容易出错。本文将介绍如何使用Python自动创建Markdown表格&#xff0c;通过示例代码详细展示各种场景下的创建…

Linux基础指令详解(1)

操作系统的概念 百度百科 操作系统&#xff08;英语&#xff1a;Operating System&#xff0c;缩写&#xff1a;OS&#xff09;是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。根据运行的环境&#xff0c;操作系…

【Python网络爬虫入门教程1】成为“Spider Man”的第一课:HTML、Request库、Beautiful Soup库

Python 网络爬虫入门&#xff1a;Spider man的第一课 写在最前面背景知识介绍蛛丝发射器——Request库智能眼镜——Beautiful Soup库 第一课总结 写在最前面 有位粉丝希望学习网络爬虫的实战技巧&#xff0c;想尝试搭建自己的爬虫环境&#xff0c;从网上抓取数据。 前面有写一…