当前位置: 首页 > news >正文

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();}
}

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

抓一张普通预览:

抓一张四宫格滤镜预览:

 

相关文章:

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

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

小红书蒲公英平台开通后,有哪些注意的地方,以及如何进行报价?

今天来给大家聊聊当小红书账号过1000粉后&#xff0c;开通蒲公英需要注意的事项。 蒲公英平台是小红书APP中的一个专为内容创作者设计的平台。它为品牌和创作者提供了一个完整的服务流程&#xff0c;包括内容的创作、推广、互动以及转换等多个方面。 2.蒲公英平台的主要功能 &…...

持续集成交付CICD:Jenkins配置Nexus制品上传流水线

目录 一、实验 1.Jenkins配置制品上传流水线 二、问题 1.上传制品显示名称有误 一、实验 1.Jenkins配置制品上传流水线 (1) 新建流水线项目 &#xff08;2&#xff09;描述 &#xff08;3&#xff09;添加参数 &#xff08;4&#xff09;查看构建首页 &#xff08;5&…...

C语言笔试例题_指针专练30题(附答案解析)

C语言笔试例题_指针专练30题(附答案解析) 指针一直是C语言的灵魂所在&#xff0c;是掌握C语言的必经之路&#xff0c;收集30道C语言指针题目分享给大家&#xff0c;测试环境位64位ubuntu18.04环境&#xff0c;如有错误&#xff0c;恳请指出&#xff0c;文明讨论&#xff01;&am…...

【Vue+Python】—— 基于Vue与Python的图书管理系统

文章目录 &#x1f356; 前言&#x1f3b6;一、项目描述✨二、项目展示&#x1f3c6;三、撒花 &#x1f356; 前言 【VuePython】—— 基于Vue与Python的图书管理系统 &#x1f3b6;一、项目描述 描述&#xff1a; 本项目为《基于Vue与Python的图书管理系统》&#xff0c;项目…...

智能成绩表 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题目描述 小明来到某学校当老师&#xff0c;需要将学生按考试总分或单科分数进行排名&#xff0c;你能帮帮他吗? 输入描述 第1行输入两个整数&#xff0c;学生人数n和科目数量m。0<n<100,0<m<10…...

【基于ESP32无线蓝牙上传电脑Excel透传数据】

【基于ESP32无线蓝牙上传电脑透传数据】 1. 引言2. 环境搭建2.1 硬件准备:2.2 软件准备:2.3. 配置Excel端口接收功能3. 测试代码4. 连接电脑和 ESP324.1 烧录程序4.2 启动蓝牙服务4.3 测试数据透传5. 总结1. 引言 随着物联网技术的发展,越来越多的设备开始支持无线通信,其…...

Qt篇——QChartView实现鼠标滚轮缩放、鼠标拖拽平移、鼠标双击重置缩放平移、曲线点击显示坐标

话不多说。 第一步&#xff1a;自定义QChartView&#xff0c;直接搬 FirtCurveChartView.h #ifndef FITCURVECHARTVIEW_H #define FITCURVECHARTVIEW_H #include <QtCharts>class FitCurveChartView : public QChartView {Q_OBJECTpublic:FitCurveChartView(QWidget *…...

掌握VUE中localStorage的使用

文章目录 &#x1f341;localStorage的使用&#x1f33f;设置数据&#x1f33f;获取数据&#x1f33f;更新数据&#x1f33f;删除数据 &#x1f341;代码示例&#x1f341;使用场景&#x1f341;总结 localStorage是一种Web浏览器提供的本地存储机制&#xff0c;允许开发者在用…...

所有行业的最终归宿-我有才打造知识付费平台

随着科技的不断进步和全球化的加速发展&#xff0c;我们生活在一个信息爆炸的时代。各行各业都在努力适应这一变化&#xff0c;寻找新的商业模式和增长机会。在这个过程中&#xff0c;一个趋势逐渐凸显出来&#xff0c;那就是知识付费。可以说&#xff0c;知识付费正在成为所有…...

图的深度和广度优先遍历

题目描述 以邻接矩阵给出一张以整数编号为顶点的图&#xff0c;其中0表示不相连&#xff0c;1表示相连。按深度和广度优先进行遍历&#xff0c;输出全部结果。要求&#xff0c;遍历时优先较小的顶点。如&#xff0c;若顶点0与顶点2&#xff0c;顶点3&#xff0c;顶点4相连&…...

计算机毕业设计JAVA+SSM+springboot养老院管理系统

设计了养老院管理系统&#xff0c;该系统包括管理员&#xff0c;医护人员和老人三部分。同时还能为用户提供一个方便实用的养老院管理系统&#xff0c;管理员在使用本系统时&#xff0c;可以通过系统管理员界面管理用户的信息&#xff0c;也可以进行个人中心&#xff0c;医护等…...

Flutter路由的几种用法

Flutter路由跳转 基本路由跳转 ElevatedButton(onPressed: () {//基本路由跳转Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {return const SearchPage();}),);},child: const Text("基本路由跳转"), ), search.dart页面 impo…...

力扣119双周赛

第 119 场双周赛 文章目录 第 119 场双周赛找到两个数组中的公共元素消除相邻近似相等字符最多 K 个重复元素的最长子数组找到最大非递减数组的长度 找到两个数组中的公共元素 模拟 class Solution { public:vector<int> findIntersectionValues(vector<int>&…...

Redux,react-redux,dva,RTK

1.redux的介绍 Redux – 李立超 | lilichao.com 2.react-redux 1&#xff09;react-Redux将所有组件分成两大类 UI组件 只负责 UI 的呈现&#xff0c;不带有任何业务逻辑通过props接收数据(一般数据和函数)不使用任何 Redux 的 API一般保存在components文件夹下容器组件 …...

基于Java SSM框架实现高校信息资源共享平台系统【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现高校信息资源共享平台系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们…...

SpringMvc入坑系列(一)----maven插件启动tomcat

springboot傻瓜式教程用久了&#xff0c;回过来研究下SSM的工作流程&#xff0c;当然从Spring MVC开始&#xff0c;从傻瓜式入门处理请求和页面交互&#xff0c;再到后面深入源码分析。 本人写了一年多的后端和半年多的前端了。用的都是springbioot和vue&#xff0c;源码一直来…...

Leetcode—337.打家劫舍III【中等】

2023每日刷题&#xff08;五十二&#xff09; Leetcode—337.打家劫舍III 算法思想 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(null…...

列表标签的介绍与使用

列表的作用&#xff1a; 整齐、整洁、有序&#xff0c;它作为布局会更加自由和方便。 根据使用情景不同&#xff0c;列表可以分为三大类&#xff1a;无序列表、有序列表和自定义列表 无序列表 <ul> 标签表示 HTML 页面中项目的无序列表&#xff0c;一般会以项目符号呈…...

浅谈什么是语音芯片的白噪音支持功能:打造舒适家居与优质音频体验

随着科技的不断进步和人们对生活质量要求的提升&#xff0c;语音芯片已经成为了现代电子产品中不可或缺的一部分。而在这些语音芯片中&#xff0c;支持白噪音的功能逐渐受到人们的关注。本文将围绕语音芯片中的白噪音支持功能展开讨论&#xff0c;带您领略其带来的舒适家居与优…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

前端高频面试题2:浏览器/计算机网络

本专栏相关链接 前端高频面试题1&#xff1a;HTML/CSS 前端高频面试题2&#xff1a;浏览器/计算机网络 前端高频面试题3&#xff1a;JavaScript 1.什么是强缓存、协商缓存&#xff1f; 强缓存&#xff1a; 当浏览器请求资源时&#xff0c;首先检查本地缓存是否命中。如果命…...

二维FDTD算法仿真

二维FDTD算法仿真&#xff0c;并带完全匹配层&#xff0c;输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”&#xff0c;于是尝试整合图像识别与网页自动化技术&#xff0c;完成了这套模拟登录流程。核心思路是&#xff1a;截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...