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

Android OpenGLES2.0开发(九):图片滤镜

“当你改变想法的时候,记得也要改变你的世界。”——诺曼·文森特·皮尔

  • Android OpenGLES开发:EGL环境搭建
  • Android OpenGLES2.0开发(一):艰难的开始
  • Android OpenGLES2.0开发(二):环境搭建
  • Android OpenGLES2.0开发(三):绘制一个三角形
  • Android OpenGLES2.0开发(四):矩阵变换和相机投影
  • Android OpenGLES2.0开发(五):绘制正方形和圆形
  • Android OpenGLES2.0开发(六):着色器语言GLSL
  • Android OpenGLES2.0开发(七):纹理贴图之显示图片
  • Android OpenGLES2.0开发(八):Camera预览
  • Android OpenGLES2.0开发(九):图片滤镜

前言

还记得我们在Android OpenGLES2.0开发(一):艰难的开始这一篇中说到,OpenGL ES能做什么吗?其中一项就是对图片做处理,色调转换美颜等。专业的说法叫做滤镜,主要是用来实现图像的各种特殊效果。

学了这么久的OpenGL ES不知道大家有没有疑问?发现OpenGL ES没有必须要用它的场景,画三角形、正方形、圆形完全可以用Android自定义视图实现,Camera预览使用OpenGL ES绕了一大圈还是显示摄像头数据。之前学习的场景似乎使用普通模式都能够又快又好的实现,OpenGL ES貌似不是必需品。

如果你有这些疑问,那么你确实在思考了。其实开发就是这样,一个问题可以有多种方案实现,无非就是不同方案的优劣罢了。

灰度图的思考

我们知道图像都是由一个一个像素组成的,而每一个像素的颜色都由一个RBG值确定,由此组成了一副美丽的图像。

如果我们现在要将一副彩色的图像灰度化,就要对每一个像素点做处理。一般的处理方法是将图片颜色值的RGB三个通道值设为一样,这样原本的256*256*256种颜色就只有256种了,256种颜色值就丢失了图片的彩色信息,留下的只有亮度值,视觉上看上去就是灰色的图片。

灰度处理一般有三种算法:

  1. 最大值法:即新的颜色值R=G=B=Max(R,G,B),这种方法处理后的图片看起来亮度值偏高。
  2. 平均值法:即新的颜色值R=G=B=(R+G+B)/3,这样处理的图片十分柔和
  3. 加权平均值法:即新的颜色值R=G=B=(R * Wr+G*Wg+B*Wb),一般由于人眼对不同颜色的敏感度不一样,所以三种颜色值的权重不一样,一般来说绿色最高,红色其次,蓝色最低,最合理的取值分别为Wr = 30%,Wg = 59%,Wb = 11%

其实知道了上面的算法,图像灰度处理就变得简单了,无非就是循环遍历图像像素,一个一个修改就好了。一张1920*1080的图片,有2073600像素点,如果使用CPU进行遍历无异于大炮打苍蝇出力不讨好。这个时候GPU的并行处理能力就派上用场了,而OpenGL ES又是GPU的驱动,所以OpenGL ES什么时候用我想大家也就明白了。

加权平均值法

我们使用加权平均值法对图片进行灰度化处理,我们拷贝Image类修改为GrayFilter,修改片段着色器代码如下

// 灰度片段着色器代码
private final String grayFragmentShaderCode ="precision mediump float;\n" +"uniform sampler2D vTexture;\n" +"varying vec2 aTexCoordinate;\n" +"void main() {\n" +"  vec4 rgba = texture2D(vTexture, aTexCoordinate);\n" +"  float color = rgba.r * 0.3 + rgba.g * 0.59 + rgba.b * 0.11;\n" +"  gl_FragColor = vec4(color, color, color, 1.0);\n" +"}\n";

为了使GrayFilter更加通用我们不再内部传入Bitmap生成纹理,而是外部传入纹理进行渲染,修改onDraw方法如下:

public void onDraw(int textureId, float[] matrix)

我们使用这个灰度滤镜渲染一张1080*2041大图看下,哇哦看着效果确实不错。本人使用测试手机是2018年发布的一加6T,渲染时间不到1ms,可见OpenGL ES对图像强大的处理能力。

几种常用滤镜实现

1. 反相

反相是一种特殊的图像处理方式,其作用是将原图中的明暗颜色值反转,即明的地方变暗,暗的地方变亮。具体操作就是用255分别减去RGB三个值作为新的RGB颜色。直接上片段着色器代码:

private String fragmentShaderCode ="precision mediump float;\n" +"uniform sampler2D vTexture;\n" +"varying vec2 aTexCoordinate;\n" +"void main() {\n" +"  vec4 rgba = texture2D(vTexture, aTexCoordinate);\n" +"  gl_FragColor = vec4((1.0 - rgba.rgb), 1.0);\n" +"}\n";

应为GL中对色值进行的归一化为 [0-1.0] 所以代码中使用1.0减去对应的色值

2. 亮度

亮度是人对光的强度的主观感受,通常用堪德拉每平米(cd/m²)或尼特来表示。在PS中,亮度调整是通过改变图像中像素的光亮度来实现的,从而影响图像的明暗程度。提高亮度可以使图像变得更加明亮,减少阴影区域,增强图像的通透性;反之,降低亮度则会使图像变暗,增加画面的沉稳感或神秘氛围‌。

调整亮度只需要RGB色彩空间里面同时加上一个程度值。

private String fragmentShaderCode ="precision mediump float;\n" +"uniform sampler2D vTexture;\n" +"uniform lowp float brightness;\n" +"varying vec2 aTexCoordinate;\n" +"void main() {\n" +"  vec4 rgba = texture2D(vTexture, aTexCoordinate);\n" +"  gl_FragColor = vec4((rgba.rgb + brightness), 1.0);\n" +"}\n";

brightness为正表示提亮,为负表示变暗

3. 冷暖色调

暖色系调整,是加强R/G来完成

float[] changeColor = {0.1f, 0.1f, 0.0f};

冷色系调整,是增加B的分量

float[] changeColor = {0.0f, 0.0f, 0.1f};

着色器代码如下,我们通过外部传入色值来改变原图色调,并且最后限制色值范围为**[0-1]**

private String fragmentShaderCode ="precision mediump float;\n" +"uniform sampler2D vTexture;\n" +"uniform vec3 vChangeColor;\n" +"varying vec2 aTexCoordinate;\n" +// 控制颜色在[0-1]范围"vec4 clampColor(vec4 color) {\n" +"    return vec4(clamp(color.r, 0.0, 1.0), clamp(color.g, 0.0, 1.0), clamp(color.b, 0.0, 1.0), 1.0);\n" +"}\n" +"void main() {\n" +"  vec4 srcColor = texture2D(vTexture, aTexCoordinate);\n" +"  vec4 dstColor = srcColor + vec4(vChangeColor, 0.0);\n" +"  gl_FragColor = clampColor(dstColor);\n" +"}\n";

其实这套代码也适用于亮度变化,changeColor传入相同的值就是亮度变化了。

看下各种滤镜的效果:

最后

本章节我们学习了如何使用OpenGL ES对图片做各种滤镜效果,我们发现通过修改GLSL就能实现很多种效果,并且非常高效,我们找到了OpenGL ES的用武之地。

其实对图片的滤镜效果有几十种上百种,我们看下市面上的各种P图软件,如:醒图、美图秀秀等,他们提供了丰富的滤镜效果。但再多的滤镜也是使用OpenGL ES框架,编写GLSL来完成。而实现众多的滤镜效果,已经和OpenGL ES没什么关系了,而更多的是要熟悉图像处理的算法,只要有算法就能通过GLSL实现。

OpenGL ES系列:https://github.com/xiaozhi003/AndroidOpenGLDemo.git,如果对你有帮助可以star下,万分感谢^_^

相关文章:

Android OpenGLES2.0开发(九):图片滤镜

“当你改变想法的时候,记得也要改变你的世界。”——诺曼文森特皮尔 Android OpenGLES开发:EGL环境搭建Android OpenGLES2.0开发(一):艰难的开始Android OpenGLES2.0开发(二):环境搭…...

SQLite Update 语句

SQLite Update 语句 SQLite 的 UPDATE 语句用于更新数据库表中的现有记录。使用 UPDATE 语句,您可以修改一个或多个列的值。本教程将详细介绍如何使用 SQLite UPDATE 语句,包括语法、示例以及一些最佳实践。 语法 SQLite UPDATE 语句的基本语法如下&a…...

Metaploit-永恒之蓝漏洞利用

1:Metaploit介绍   本次测试主要是利用永恒之蓝漏洞对windows7进行控制利用,掌握Metaploit工具的使用,知道永恒之蓝的漏洞利用原理。永恒之蓝是在Windows的SMB服务处理SMB v1请求时发生的漏洞,这个漏洞导致攻击者在目标系统上可…...

机器学习预处理-表格数据的空值处理

机器学习预处理-表格数据的空值处理 机器学习预处理-表格数据的分析与可视化中详细介绍了表格数据的python可视化,可视化能够帮助我们了解数据的构成和分布,是我们进行机器学习的必备步骤。上文中也提及,原始的数据存在部分的缺失&#xff0…...

数据结构_平衡二叉树

结点类 构造函数分为有参和无参,相同点都是初始化树高为1 class Node { public:int data; // 用于输出int val; // 数据域,用于排序int height; // 树高Node* left;Node* right;Node();Node(int v, int d);static int max(int a, int b); };Node::N…...

C++对象的赋值与复制复制构造函数(指针数据成员)

一、对象的赋值 同类对象之间可以相互赋值,对象赋值的一般形式:对象名2 对象名1; 原理是,赋值运算符的重载。仅赋值,因此赋值前,需要先定义并初始化对象2。 对象的赋值针对指对象中所有数据成员的值; 对…...

Coding Caprice - monotonic stack2

42. 接雨水 class Solution { public:int trap(vector<int>& height) {stack<int> sh;int out 0;for(int i0; i<height.size(); i){while(!sh.empty() && height[sh.top()]<height[i]){int bo height[sh.top()];sh.pop();if(sh.empty()){brea…...

Spring Mvc面试题(常见)

1 Spring MVC的执行流程 用户发起请求,请求先被Servlet拦截以后,转发给SpringMVC框架SpringMVC 里面的DispatcherServlet(核心控制器) 接收到请求,并转发给HandlerMappingHandlerMapping负责解析请求,根据请求信息和配置信息找到匹配的Controller类(当这里有配置拦截器,会…...

opencv # Sobel算子、Laplacian算子、Canny边缘检测、findContours、drawContours绘制轮廓、外接矩形

一、Sobel算子 案例图片 cv2.Sobel(src, ddepth, dx, dy, ksize3, scale1, delta0, borderTypeNone) 功能&#xff1a;用于计算图像梯度&#xff08;gradient&#xff09;的函数 参数&#xff1a; src: 输入图像&#xff0c;它应该是灰度图像。 ddepth: 输出图像的所需深度&am…...

Neo4j插入数据逐级提升速度4倍又4倍

语雀版&#xff1a;https://www.yuque.com/xw76/back/dtukgqfkfwg1d6yo 目录 背景介绍初始方案Node()创建事务批量提交记录Node是否存在生成Cypher语句执行数据库参数优化切换成85k个三元组测试建索引&#xff08;很显著&#xff01;&#xff01;&#xff01;&#xff09;MATCH…...

C++特殊类设计(单例模式等)

目录 引言 1.请设计一个类&#xff0c;不能被拷贝 2. 请设计一个类&#xff0c;只能在堆上创建对象 为什么设置实例的方法为静态成员呢 3. 请设计一个类&#xff0c;只能在栈上创建对象 4. 请设计一个类&#xff0c;不能被继承 5. 请设计一个类&#xff0c;只能创建一个对…...

J8学习打卡笔记

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 Inception v1算法实战与解析 导入数据数据预处理划分数据集搭建模型训练模型正式训练结果可视化详细网络结构图个人总结 import os, PIL, random, pathlib imp…...

前端学习-操作元素内容(二十二)

目录 前言 目标 对象.innerText 属性 对象.innerHTML属性 案例 年会抽奖 需求 方法一 方法二 总结 前言 曾经沧海难为水&#xff0c;除却巫山不是云。 目标 能够修改元素的文本更换内容 DOM对象都是根据标签生成的,所以操作标签,本质上就是操作DOM对象&#xff0c;…...

【踩坑】pip离线+在线在虚拟环境中安装指定版本cudnn攻略

pip离线在线在虚拟环境中安装指定版本cudnn攻略 在线安装离线安装Windows环境&#xff1a;Linux环境&#xff1a; 清华源官方帮助文档 https://mirrors.tuna.tsinghua.edu.cn/help/pypi/ 标题的离线的意思是先下载whl文件再安装到虚拟环境&#xff0c;在线的意思是直接在当前虚…...

golang操作sqlite3加速本地结构化数据查询

目录 摘要Sqlite3SQLite 命令SQLite 语法SQLite 数据类型列亲和类型——优先选择机制 SQLite 创建数据库SQLite 附加数据库SQLite 分离数据库 SQLite 创建表SQLite 删除表 SQLite Insert 语句SQLite Select 语句SQLite 运算符SQLite 算术运算符SQLite 比较运算符SQLite 逻辑运算…...

vllm加速(以Qwen2.5-7B-instruction为例)与流式响应

1. vllm介绍 什么是vllm? vLLM 是一个高性能的大型语言模型推理引擎&#xff0c;采用创新的内存管理和执行架构&#xff0c;显著提升了大模型推理的速度和效率。它支持高度并发的请求处理&#xff0c;能够同时服务数千名用户&#xff0c;并且兼容多种深度学习框架&#xff0c;…...

WordPress弹窗公告插件-ts小陈

使用效果 使用后网站所有页面弹出窗口 插件特色功能 设置弹窗公告样式&#xff1a;这款插件可展示弹窗样式公告&#xff0c;用户点击完之后不再弹出&#xff0c;不会频繁打扰用户。可设置弹窗中间的logo图&#xff1a;这款插件针对公告图片进行独立设置&#xff0c;你可以在设…...

【ELK】容器化部署Elasticsearch1.14.3集群【亲测可用】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1. 部署1.1 单节点1.2 新节点加入集群1.3 docker-compose部署集群 1. 部署 按照官网流程进行部署 使用 Docker 安装 Elasticsearch |Elasticsearch 指南 [8.14] |…...

[SAP ABAP] ALV状态栏GUI STATUS的快速创建

使用事务码SE38进入到指定程序&#xff0c;点击"显示对象列表"按钮 鼠标右键&#xff0c;选择"GUI状态" 弹出【创建状态】窗口&#xff0c;填写状态以及短文本描述以后&#xff0c;点击按钮 点击"调整模板"&#xff0c;复制已有程序的状态栏 填…...

【Linux】NET9运行时移植到低版本GLIBC的Linux纯内核板卡上

背景介绍 自制了一块Linux板卡(基于全志T113i) 厂家给的SDK和根文件系统能够提供的GLIBC的版本比较低 V2.25/GCC 7.3.1 这个版本是无法运行dotnet以及dotnet生成的AOT应用的 我用另一块同Cortex-A7的板子运行dotnet的报错 版本不够&#xff0c;运行不了 而我的板子是根本就识…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

作为点的对象CenterNet论文阅读

摘要 检测器将图像中的物体表示为轴对齐的边界框。大多数成功的目标检测方法都会枚举几乎完整的潜在目标位置列表&#xff0c;并对每一个位置进行分类。这种做法既浪费又低效&#xff0c;并且需要额外的后处理。在本文中&#xff0c;我们采取了不同的方法。我们将物体建模为单…...

关于 ffmpeg设置摄像头报错“Could not set video options” 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/148515355 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…...

可下载旧版app屏蔽更新的app市场

软件介绍 手机用久了&#xff0c;app越来越臃肿&#xff0c;老手机卡顿成常态。这里给大家推荐个改善老手机使用体验的方法&#xff0c;还能帮我们卸载不需要的app。 手机现状 如今的app不断更新&#xff0c;看似在优化&#xff0c;实则内存占用越来越大&#xff0c;对手机性…...

盲盒一番赏小程序:引领盲盒新潮流

在盲盒市场日益火爆的今天&#xff0c;如何才能在众多盲盒产品中脱颖而出&#xff1f;盲盒一番赏小程序给出了答案&#xff0c;它以创新的玩法和优质的服务&#xff0c;引领着盲盒新潮流。 一番赏小程序的最大特色在于其独特的赏品分级制度。赏品分为多个等级&#xff0c;从普…...

c++算法学习3——深度优先搜索

一、深度优先搜索的核心概念 DFS算法是一种通过递归或栈实现的"一条路走到底"的搜索策略&#xff0c;其核心思想是&#xff1a; 深度优先&#xff1a;从起点出发&#xff0c;选择一个方向探索到底&#xff0c;直到无路可走 回溯机制&#xff1a;遇到死路时返回最近…...