实战:用 effectKit 做一个"复古胶片"滤镜

今天我们来做点实际的——用 effectKit 的 Filter 链组合出一个复古胶片风格的滤镜。

复古胶片长什么样?泛黄的色调、稍微降低的对比度、一点点颗粒感、暗角。我们不需要全部做到(颗粒感需要像素级操作,effectKit 不直接支持),但色调和亮度调整完全可以。

复古滤镜处理流程

下面是复古胶片滤镜的完整处理流程:

加载图片资源

创建ImageSource

转换为PixelMap

创建滤镜效果对象

添加brightness提亮

添加setColorMatrix复古色调

添加blur柔焦效果

应用滤镜链

获取处理后的PixelMap

显示复古效果图片

复古滤镜的思路

复古胶片的核心特征:

  1. 色调偏暖——整体偏黄/偏橙
  2. 对比度降低——暗部提亮,亮部压暗
  3. 饱和度降低——颜色没那么鲜艳
  4. 轻微模糊——模拟老镜头的柔焦感

用 effectKit 怎么实现?

第一步:创建颜色矩阵

颜色矩阵是一个 5x4 的矩阵,可以同时控制色调、对比度、饱和度。复古风格的矩阵大概是这样的:

// 复古胶片矩阵
let vintageMatrix: Array<number> = [
  0.6, 0.3, 0.1, 0, 20,    // R: 偏暖,提亮
  0.2, 0.7, 0.1, 0, 10,    // G: 稍微偏绿
  0.1, 0.2, 0.5, 0, 5,     // B: 压低蓝色
  0, 0, 0, 1, 0             // A: 不变
];

这个矩阵在说什么?每一行对应一个输出通道(R、G、B、A),前四列是输入通道的权重,最后一列是常量偏移。

以第一行为例:0.6, 0.3, 0.1, 0, 20 意味着:

  • 输出的红色 = 输入红色×0.6 + 输入绿色×0.3 + 输入蓝色×0.1 + 20

为什么要这样设?复古胶片偏暖(偏黄/橙),所以我们压低蓝色(B 通道权重只有 0.5),同时给红色通道加了 20 的常量偏移,让暗部也带点暖色。

第二步:创建滤镜链

import { image } from '@kit.ImageKit';
import { effectKit } from '@kit.ArkGraphics2D';

先导入需要的模块。image 用来处理图片,effectKit 提供滤镜功能。

let imageSource = image.createImageSource(source);
let pixelMap = await imageSource.createPixelMap();

把图片数据转换成 PixelMap——这是 HarmonyOS 处理图片的标准格式,所有图像操作都要基于它。

let headFilter = effectKit.createEffect(pixelMap);

createEffect 创建一个滤镜效果对象,传入 PixelMap。返回的 headFilter 是滤镜链的"头节点",后续所有滤镜都挂在这个链上。

第三步:添加滤镜

if (headFilter != null) {
  // 第一步:轻微提亮
  headFilter.brightness(0.1);

brightness(0.1) 把整体亮度提高 10%。复古胶片不会太暗,但也不能太亮,0.1 是个保守的值。

  // 第二步:复古色调矩阵
  headFilter.setColorMatrix(vintageMatrix);

setColorMatrix 应用我们刚才定义的颜色矩阵。这一步是复古效果的核心——它改变了整个画面的色调。

  // 第三步:轻微柔焦
  headFilter.blur(1);
}

blur(1) 加一点模糊,模拟老镜头的柔焦感。参数 1 表示模糊半径为 1 像素,效果很轻微。

注意顺序:先提亮,再调色,最后柔焦。如果先柔焦再调色,颜色矩阵会作用在已经模糊的像素上,效果可能不太对。

第四步:获取结果

headFilter.getEffectPixelMap().then(result => {
  resolve(result);
})

getEffectPixelMap() 把滤镜链应用到图片上,返回处理后的 PixelMap。这个操作是异步的,因为图像处理需要时间。

完整流程

把上面的步骤串起来:

function applyVintageFilter(source: ArrayBuffer): Promise<image.PixelMap> {
  return new Promise(async (resolve, reject) => {
    let imageSource = image.createImageSource(source);
    await imageSource.createPixelMap().then(async (pixelMap: image.PixelMap) => {
      let headFilter = effectKit.createEffect(pixelMap);
      if (headFilter != null) {
        headFilter.brightness(0.1);        // 提亮
        headFilter.setColorMatrix(vintageMatrix);  // 复古色调
        headFilter.blur(1);                // 柔焦
      }
      headFilter.getEffectPixelMap().then(result => {
        resolve(result);
      })
    })
  })
}

在页面中使用

@Entry
@Component
struct VintageFilter {
  @State originalMap: image.PixelMap | null = null;
  @State vintageMap: image.PixelMap | null = null;

定义两个状态变量:原图和复古效果。

  async aboutToAppear() {
    const context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    const fileData: Uint8Array = await context.resourceManager.getRawFileContent('photo.jpg');
    const buffer: ArrayBuffer = fileData.buffer.slice(0);

rawfile 文件夹加载图片。getRawFileContent 返回 Uint8Array,我们需要转成 ArrayBuffer 才能用。

    // 保存原图
    let imageSource = image.createImageSource(buffer);
    this.originalMap = await imageSource.createPixelMap();

    // 应用复古滤镜
    this.vintageMap = await applyVintageFilter(buffer);
  }

同时保存原图和处理后的图,方便对比。

  build() {
    Column() {
      Text('复古胶片滤镜')
        .fontSize(24)
        .margin(20)

      // 原图
      if (this.originalMap) {
        Text('原图')
          .fontSize(16)
          .margin({ bottom: 10 })
        Image(this.originalMap)
          .width('90%')
          .aspectRatio(1.5)
      }

      // 复古效果
      if (this.vintageMap) {
        Text('复古效果')
          .fontSize(16)
          .margin({ top: 20, bottom: 10 })
        Image(this.vintageMap)
          .width('90%')
          .aspectRatio(1.5)
      }
    }
    .width('100%')
    .height('100%')
  }
}

布局很简单:上面显示原图,下面显示复古效果。aspectRatio(1.5) 让图片保持 3:2 的比例。

颜色矩阵调参思路

颜色矩阵的每个值都会影响最终效果:

偏暖色调

降低饱和度

增加对比度

冷色调

颜色矩阵5x4

第一行: R通道输出

第二行: G通道输出

第三行: B通道输出

第四行: A通道

调整目标

增大R权重和偏移

让RGB权重接近

权重和大于1

增大B权重压低R

调参技巧

颜色矩阵不是随便写的,每个值都有含义。这里分享一些调参技巧:

想让画面偏暖? 增大第一行的常量偏移(最后一个值),同时增大 R 通道对 R 输入的权重。

想降低饱和度? 让每行的三个权重接近(R≈G≈B),这样输出的颜色就会趋向灰色。

想增加对比度? 让权重之和大于 1,同时用负的常量偏移补偿。

想做冷色调? 增大 B 通道的权重和偏移,压低 R 通道。

你可以试着改改矩阵里的值,看看效果有什么变化。这种"手动调色"的过程其实很有趣——就像在调一台老式电视机的旋钮。

滤镜链组合方式

effectKit支持多种滤镜叠加使用:

PixelMap原始图片

创建滤镜链头节点

brightness亮度调整

setColorMatrix颜色矩阵

blur模糊效果

grayscale灰度效果

其他滤镜...

getEffectPixelMap获取结果

输出处理后图片

小结

复古胶片滤镜的核心就是:

  1. 颜色矩阵调整色调(偏暖、降饱和度)
  2. brightness 微调亮度
  3. blur(1) 加一点柔焦

三个 Filter 叠加,效果已经很不错了。如果你想更精细,可以再加上 grayscale() 做部分去色,或者用不同的颜色矩阵做更多风格化效果。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐