Metal每日分享,颜色转换滤镜效果

news2025/1/11 14:52:31

本案例的目的是理解如何用Metal实现像素颜色转换滤镜,通过对像素颜色的不同读取方式获取到相应像素颜色,灰度图移除场景中除了黑白灰以外所有的颜色,让整个图像灰度化;


Demo

  • HarbethDemo地址

实操代码

// 转成灰度图滤镜
let filter = C7ColorConvert(with: .gray)

// 方案1:
let dest = BoxxIO.init(element: originImage, filter: filter)
ImageView.image = try? dest.output()

dest.filters.forEach {
    NSLog("%@", "\($0.parameterDescription)")
}

// 方案2:
ImageView.image = try? originImage.make(filter: filter)

// 方案3:
ImageView.image = originImage ->> filter

实现原理

  • 过滤器

这款滤镜采用并行计算编码器设计.compute(kernel: type.rawValue)

/// 颜色通道`RGB`位置转换
public struct C7ColorConvert: C7FilterProtocol {
    
    public enum ColorType: String, CaseIterable {
        case invert = "C7ColorInvert"
        case gray = "C7Color2Gray"
        case bgra = "C7Color2BGRA"
        case brga = "C7Color2BRGA"
        case gbra = "C7Color2GBRA"
        case grba = "C7Color2GRBA"
        case rbga = "C7Color2RBGA"
    }
    
    private let type: ColorType
    
    public var modifier: Modifier {
        return .compute(kernel: type.rawValue)
    }
    
    public init(with type: ColorType) {
        self.type = type
    }
}
  • 着色器

取出像素rgb值,然后根据对应像素颜色;灰度图则是取所有的颜色分量,将它们加权或平均;

// 颜色反转,1 - rgb
kernel void C7ColorInvert(texture2d<half, access::write> outputTexture [[texture(0)]],
                          texture2d<half, access::read> inputTexture [[texture(1)]],
                          uint2 grid [[thread_position_in_grid]]) {
    const half4 inColor = inputTexture.read(grid);
    
    const half4 outColor(1.0h - inColor.rgb, inColor.a);
    
    outputTexture.write(outColor, grid);
}

// 转灰度图
kernel void C7Color2Gray(texture2d<half, access::write> outputTexture [[texture(0)]],
                         texture2d<half, access::read> inputTexture [[texture(1)]],
                         uint2 grid [[thread_position_in_grid]]) {
    const half4 inColor = inputTexture.read(grid);
    
    const half3 kRec709Luma = half3(0.2126, 0.7152, 0.0722);
    const half gray = dot(inColor.rgb, kRec709Luma);
    const half4 outColor = half4(half3(gray), 1.0h);
    
    outputTexture.write(outColor, grid);
}

kernel void C7Color2BGRA(texture2d<half, access::write> outputTexture [[texture(0)]],
                         texture2d<half, access::read> inputTexture [[texture(1)]],
                         uint2 grid [[thread_position_in_grid]]) {
    const half4 inColor = inputTexture.read(grid);
    
    const half4 outColor(inColor.bgr, inColor.a);
    
    outputTexture.write(outColor, grid);
}

kernel void C7Color2BRGA(texture2d<half, access::write> outputTexture [[texture(0)]],
                         texture2d<half, access::read> inputTexture [[texture(1)]],
                         uint2 grid [[thread_position_in_grid]]) {
    const half4 inColor = inputTexture.read(grid);
    
    const half4 outColor(inColor.brg, inColor.a);
    
    outputTexture.write(outColor, grid);
}

kernel void C7Color2GBRA(texture2d<half, access::write> outputTexture [[texture(0)]],
                         texture2d<half, access::read> inputTexture [[texture(1)]],
                         uint2 grid [[thread_position_in_grid]]) {
    const half4 inColor = inputTexture.read(grid);
    
    const half4 outColor(inColor.gbr, inColor.a);
    
    outputTexture.write(outColor, grid);
}

kernel void C7Color2GRBA(texture2d<half, access::write> outputTexture [[texture(0)]],
                         texture2d<half, access::read> inputTexture [[texture(1)]],
                         uint2 grid [[thread_position_in_grid]]) {
    const half4 inColor = inputTexture.read(grid);
    
    const half4 outColor(inColor.grb, inColor.a);
    
    outputTexture.write(outColor, grid);
}

kernel void C7Color2RBGA(texture2d<half, access::write> outputTexture [[texture(0)]],
                         texture2d<half, access::read> inputTexture [[texture(1)]],
                         uint2 grid [[thread_position_in_grid]]) {
    const half4 inColor = inputTexture.read(grid);
    
    const half4 outColor(inColor.rbg, inColor.a);
    
    outputTexture.write(outColor, grid);
}
  • 权值法
const half3 kRec709Luma = half3(0.2126, 0.7152, 0.0722);
const half gray = dot(inColor.rgb, kRec709Luma);
const half4 outColor = half4(half3(gray), 1.0h);
  • 平均值法
const float color = (inColor.r + inColor.g + inColor.b) / 3.0;
const half4 outColor = half4(color, color, color, 1.0h);

总结:
一般由于人眼对不同颜色的敏感度不一样,所以三种颜色值的权重不一样,一般来说绿色最高,红色其次,蓝色最低,最合理的取值分别为Wr = 30%,Wg = 59%,Wb = 11%,所以权值法相对效果更好一点。

对照图

invertbgrabrga
WX20221125-165057.pngWX20221125-164833.pngWX20221125-165018.png
gbragrbarbga
WX20221125-165138.pngWX20221125-165151.pngWX20221125-165213.png

Harbeth功能清单

  • 支持ios系统和macOS系统
  • 支持运算符函数式操作
  • 支持多种模式数据源 UIImage, CIImage, CGImage, CMSampleBuffer, CVPixelBuffer.
  • 支持快速设计滤镜
  • 支持合并多种滤镜效果
  • 支持输出源的快速扩展
  • 支持相机采集特效
  • 支持视频添加滤镜特效
  • 支持矩阵卷积
  • 支持使用系统 MetalPerformanceShaders.
  • 支持兼容 CoreImage.
  • 滤镜部分大致分为以下几个模块:
    • Blend:图像融合技术
    • Blur:模糊效果
    • Pixel:图像的基本像素颜色处理
    • Effect:效果处理
    • Lookup:查找表过滤器
    • Matrix: 矩阵卷积滤波器
    • Shape:图像形状大小相关
    • Visual: 视觉动态特效
    • MPS: 系统 MetalPerformanceShaders.

最后

  • 关于颜色转换滤镜介绍与设计到此为止吧。
  • 慢慢再补充其他相关滤镜,喜欢就给我点个星🌟吧。
  • 滤镜Demo地址,目前包含100+种滤镜,同时也支持CoreImage混合使用。
  • 再附上一个开发加速库KJCategoriesDemo地址
  • 再附上一个网络基础库RxNetworksDemo地址
  • 喜欢的老板们可以点个星🌟,谢谢各位老板!!!

✌️.

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

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

相关文章

js深拷贝浅拷贝与lodash

title: js深拷贝浅拷贝与lodash date: 2022/9/27 14:46:25 categories: lodashjs入门 深拷贝和浅拷贝 ref&#xff1a;JS传递参数时的内部存储逻辑 JS 變數傳遞探討&#xff1a;pass by value 、 pass by reference 還是 pass by sharing&#xff1f; 在这个问题前&#xff…

LeetCode HOT 100 —— 169.多数元素

题目 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 思路 方法一&#xff1a;哈希表 遍历整个数组&#xff0c;记录每个数值出…

JAVA IO详解

目录 一、流的概念与作用 二、Java IO的用途和特征 三、流的使用详解 一、流的概念与作用 流(Stream)&#xff1a; 在Java IO中&#xff0c;流是一个核心的概念。流从概念上来说是一个连续的数据传输过程。人们根据数据传输特性将流抽象为各种类&#xff0c;方便更直观的进…

【服务器数据恢复】服务器raid5崩溃导致上层应用不可用的数据恢复案例

服务器数据故障&#xff1a; 某公司服务器8块硬盘组成raid5磁盘阵列&#xff0c;其中有2块硬盘故障指示灯报警&#xff0c;其他硬盘指示灯正常&#xff0c;上层应用不可用。 服务器数据恢复过程&#xff1a; 1、服务器数据恢复工程师拿到故障服务器所有硬盘后对出现物理故障的…

ADI Blackfin DSP处理器-BF533的开发详解18:用触摸屏的例程来理解中断(含源码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 硬件实现原理 ADSP-EDU-BF533 开发板上的中断资源连接到了 CPLD&#xff0c;并通过 CPLD 将中断信号连接到 PF0 触发&#xff0c;通过 CPLD映射的…

ubuntu交叉编译(armv7_32位)onnx源码_cpu版本

1 下载onnx git clone https://github.com/microsoft/onnxruntime cd onnxruntime git submodule update --init --recursive2 编译 由于是交叉编译&#xff0c;所以需要设置一下编译工具&#xff0c;在网上搜索看到了这个 chineseocr_lite/build-onnxruntime-android.sh a…

Jmeter(一):jmeter概述与工作原理,安装与基本配置介绍

Jmeter(1)&#xff1a;jmeter概述与工作原理 jmeter概述与工作原理 JMeter 是 Apache 基金会 Jakarta 上的一个纯 Java 开源项目&#xff0c;起初用于基 于 Web 的压力测试&#xff08;pressure test&#xff09;&#xff0c;后来其应用范围逐渐扩展到对文件传输 FTP, 大型数据…

基于JavaWeb+JSP的校园二手交易平台(源码+数据库+说明文档)

目录 一、前后端功能模块 1.用户web前端页面功能模块 2.后台信息管理模块 二、开发环境 三、开发技术 四、页面设计 1.登录注册界面 2.网页主页界面 3.商品列表界面 4.商品详情界面 5.支付页面 6.支付成功后页面 7.我的订单页面 ​8.个人已发布与待处理订单界面…

google外链重要性高吗?谷歌外链作用大不大

google外链重要性高吗&#xff1f; 答案是&#xff1a;非常重要&#xff0c;而且要注重建设付费GPB外链。 要相信有价值的外链一般都比较难获取&#xff0c;那种高流量的外链一般要靠自己去outreach, 但是成功率比较低&#xff0c;我们需要用金钱和优质外链资源去交换 做高质…

程序员的浪费,Python一对一还原《点燃我,温暖你》里面比较火的那个爱心代码 | 附源码

前言 包子们&#xff0c;上午好 最近有个剧挺火的 就是那个程序员的剧&#xff0c;叫《点燃我&#xff0c;温暖你》 最近听说很火呀&#xff0c;那作为程序员&#xff0c;Python中的战斗机的小编&#xff0c;能不给大家安排一波&#xff01; 怎么说呢&#xff0c;用这个表白也…

我凭借这 1000 道 java 真题,顺利拿下京东、饿了么、阿里大厂 offer

今天这篇文章也算是一次面试总结了吧&#xff01; 毕竟金九银十过去了&#xff0c;总得给大家来点东西交代交代&#xff01; 所以今天&#xff0c;这篇文章就应运而生了&#xff0c;给大家来点正正经经的干货教学&#xff0c;让大家体验一下干货的魅力&#xff01; 小编今天这里…

【C语言数据结构(基础篇)】第一站:时间复杂度与空间复杂度

目录 一、什么是时间复杂度和空间复杂度 1.算法效率 2.时间复杂度的概念 3.空间复杂度的概念 二、如何计算常见的时间复杂度 1.大O的渐进表示法 2.一些时间复杂度的例子 &#xff08;1&#xff09;例1 &#xff08;2&#xff09;例2 (3)例3 (4)例4 &#xff08;5&a…

【计算机视觉+自动驾驶】二、多任务深度学习网络并联式、级联式构建详细讲解(图像解释 超详细必看)

觉得有帮助麻烦点赞关注收藏~~~ 一、多任务网络的主要分类 目前建立的多任务网络可以分为两种方法&#xff0c;一种为并联多任务网络结构&#xff0c;另一种为级联多任务网络结构&#xff0c;两种网络构建方式分别如下图所示 并联式 级联式 并联网络结构大多为共享基础网络而…

ADI Blackfin DSP处理器-BF533的开发详解14:LED跑马灯(含源代码)

接口讲完了&#xff0c;下面写点应用程序&#xff0c;GPIO最典型的应用&#xff0c;LED跑马灯。 硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 *硬件实现原理 ADSP-EDU-BF533开发板上共设计了…

2005-2020年全国31省劳动力市场分割指数

2005-2020年全国31省劳动力市场分割指数 1、时间&#xff1a;2005-2020年 2、范围&#xff1a;包括全国31省&#xff0c; 3、数据内容&#xff1a;数据存在缺失&#xff0c;下载链接界面有数据预览&#xff0c;具体缺失情况参看链接内数据预览&#xff0c; 内含原始数据、A…

把废旧监控改无人机遥控车红外远程摄像头

像我们这些精打细算的业余玩家&#xff0c;淘个新宝贝都要掂量掂量。很羡慕能买到专用红外摄像头配无人机。可是手头不宽裕&#xff0c;只有一些旧零件。这都是废物再利用&#xff0c;所以说不要太追求性能了&#xff0c;自然让他工作就好&#xff0c;测试这条路线的可行性。 …

blneder 蜡笔

文章目录简介.打开蜡笔.基本操作.自由线.图形工具.图层.遮罩.画布.画布原点.![在这里插入图片描述](https://img-blog.csdnimg.cn/46cb7019e8ff41e6b391e056c616ce32.png)画布旋转.辅助.圆形.径向.平行.栅格.等距.编辑模式.顶部工具栏.选择.曲线编辑.左侧工具栏.快捷键.画笔深度…

值得一看的Linux内核—中断下半部之软中断

软中断 软中断&#xff08;softirq&#xff09;是中断处理程序在开启中断的情况下执行的部分&#xff0c;可以被硬中断抢占。 内核定义了一张软中断向量表&#xff0c;每种软中断有一个唯一的编号&#xff0c;对应一个softirq_action实例&#xff0c;softirq_action实例的成员…

b站黑马JavaScript的Node.js案例代码——考试成绩整理案例

目录 目标效果&#xff1a; 重点原理&#xff1a; 1.js中split方法——转换字符串为数组 2.js中forEach方法——遍历数组中每个对象 3.js数组操作中push方法——添加1/多个元素去数组的末尾 4.js数组操作中replace方法——在字符串中用一些字符替换另一些字符 5.js数组操…

ATtiny13与Proteus仿真-8位通用定时器/计数器与PWM仿真

8位通用定时器/计数器与PWM 1、8位通用定时器介绍 ATtiny13的8位通用定时器/计数器有两个独立的输出比较单元,并支持PWM。这意味着,可以通过8位通用定时器/计数器生产PWM信号。关于PWM的介绍,在这里就展开介绍,请参考相关资料。 ATtiny13的8位通用定时器/计数器具有如下…