在Flutter中将图像转换为灰度:ColorFiltered组件

背景

在最近的项目中,我遇到了一个需求:当满足特定条件时,需要将图像转换为灰度。例如,用户个人资料页面有一组用来显示个人社交信息,并展示相关社交平台图标,如果用设置了社交信息,则该社交平台的图标应处理点亮状态,如果没有设置,则应处于禁用状态,也就是是说,我需要把对应图标的颜色改为灰度模式。这看起来很简单,特别是对于那些单一颜色的图标,对吧?但实际上,有一些图标是彩色的而且色彩丰富,比如说instagram。

BlendMode

我首先研究了Flutter中的混合模式(BlendMode),结果发现它们确实效果很好 ------ 但这仅适用于没有透明度的JPEGPNG 图像。以下是我用于将彩色透明PNG图像显示为灰度图像的代码。

dart 复制代码
ColorFiltered(
        colorFilter: enabled ? ColorFilter.mode(
          Colors.transparent,
          BlendMode.multiply,
        ) : ColorFilter.mode(
          Colors.grey,
          BlendMode.saturation,
        ),
        child: Image.asset("assets/roundabout.png",),
      )

上面用的图片是一个带有透明的PNG图片,让我们看看效果:

再放一个没有经过处理的demo用的原图:

很显然,这样的效果很糟糕。混合模式(BlendMode)也会影响图像中的透明部分,并将它们也转换为灰色,但我们需要让这些透明区域也不受影响。

颜色矩阵

经过一番研究,我在Flutter文档的深处发现了这个内容。文档中提到了另一个构造函数,它可以用来定义一个矩阵,从而更精细地控制混合模式的应用方式。

于是,我用ColorFilter.matrix替换了ColorFilter.mode构造函数,并传入了这些神奇的数字。

dart 复制代码
ColorFilter.matrix(<double>[
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0,0,0,1,0,
])

没错,这正是我一直在寻找的解决方案。文档中还提到了更多示例。大家可以随意尝试这些示例,并对这些数值进行调整。

** 反色(Inversion)** 效果的颜色矩阵:

dart 复制代码
const ColorFilter invert = ColorFilter.matrix(<double>[
   -1, 0, 0, 0, 255,
   0, -1, 0, 0, 255,
   0, 0, -1, 0, 255,
   0, 0, 0, 1, 0, ]);

** 复古棕褐色(Sepia Tone)** 效果的颜色矩阵

dart 复制代码
const ColorFilter sepia = ColorFilter.matrix(<double>[ 
   0.393, 0.769, 0.189, 0, 0, 
   0.349, 0.686, 0.168, 0, 0,
   0.272, 0.534, 0.131, 0, 0, 
   0, 0, 0, 1, 0, ]);

个人感觉这个效果适用于复古风格 UI、老照片效果、特定主题界面

灰度滤镜(Grayscale Color Filter) 效果矩阵

标准灰度滤镜矩阵(最常用)

dart 复制代码
const ColorFilter greyscale = ColorFilter.matrix(<double>[ 
  0.2126, 0.7152, 0.0722, 0, 0, // 红色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 绿色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 蓝色通道转换
  0, 0, 0, 1, 0, //透明度保持不变
  ]);

上面的的灰度算法是一个标准的灰度算法,还两种算法可供参考:

简化版灰度矩阵(平均法)

dart 复制代码
ColorFilter.matrix([ 
0.333, 0.333, 0.333, 0, 0, // 红色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 绿色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 蓝色通道取平均值 
0, 0, 0, 1, 0, // 透明度保持不变 
])

高对比度灰度矩阵

dart 复制代码
ColorFilter.matrix([ 
0.299, 0.587, 0.114, 0, 0, // 红色通道(电视标准算法) 
0.299, 0.587, 0.114, 0, 0, // 绿色通道 
0.299, 0.587, 0.114, 0, 0, // 蓝色通道 
0, 0, 0, 1, 0, // 透明度保持不变
])

矩阵原理

  • 人眼对红、绿、蓝三色的敏感度不同,公式 亮度 = 0.2126*R + 0.7152*G + 0.0722*B 可模拟真实灰度效果。
  • 矩阵中每行对应一个颜色通道(红、绿、蓝、透明度),每列对应输入颜色的分量(R、G、B、A、偏移量)。

让我们再回头看看BlendMode相关属性:

关键属性解析

属性 说明
colorFilter 必需参数,指定颜色滤镜。可通过 ColorFilter.modeColorFilter.matrix 创建。
BlendMode 混合模式(如 colorBurnsrcIn 等),影响颜色过滤的最终效果。
child 待处理的子组件(通常为 ImageIcon 等可视化组件)。

注意事项

  1. 性能影响

    • 复杂的 ColorFilter.matrix 可能对性能产生轻微影响,建议对静态图像提前处理为灰度图。
    • 动态切换时,避免在列表或高频率刷新场景中大量使用。
  2. 混合模式选择

    • BlendMode.color 可保留亮度,仅修改色相和饱和度,适合部分灰度效果。
    • BlendMode.luminosity 直接提取亮度,是最纯净的灰度转换模式。

最后

通过 ColorFiltered 组件,Flutter 提供了简洁高效的图像灰度处理方案。无论是静态效果还是动态交互,合理选择混合模式或矩阵运算,均可灵活实现所需视觉效果。

也希望大家关注我的微信公众号OpenFlutter

相关推荐
恋猫de小郭39 分钟前
Redis 作者反驳「中国模型之所以强,是因为通过 API 蒸馏了美国模型」
前端·人工智能·ai编程
Darling噜啦啦43 分钟前
Canvas 游戏开发与数据可视化实战:从飞机大战到 ECharts 报表
前端·echarts·canvas
OpenTiny社区1 小时前
这次更新太良心!GenUI SDK v1.2.0 轻量化 + 稳流式 + 超强 Playground
前端·vue.js·ai编程
梨子同志1 小时前
WebGL test
前端
程序员黑豆1 小时前
AI全栈开发系列开篇:从Java全栈到AI应用实战
前端·ai编程·全栈
yangyj1 小时前
从 PDR 到落地:用 Codex 完成一次 Rspack 升级
前端
程序员鱼皮1 小时前
提示词工程已死,Loop Engineering 称王!保姆级教程 + 项目实战
前端·后端·ai编程
小爷毛毛_卓寿杰1 小时前
给 Embedding 模型也加一块“游乐场“—— Xinference 是怎么把 vector 变成肉眼可见的体验的
前端
忆江南1 小时前
iOS 性能优化全面详解
前端
lichenyang4531 小时前
HAP / HAR / HSP 到底啥区别?顺带把「导入」那点疑惑讲清楚
前端