HarmonyOS APP实战-基于Image Kit的图像处理APP - 第6篇:图片滤镜效果实现 HarmonyOS APP实战-基于Image Kit的图像处理APP - 第6篇滤镜与色彩矩阵1. 开篇在上篇「旋转翻转效果」中我们实现了RotationPage页面用户可以通过点击按钮或滑块对图片进行 90°、180° 旋转以及水平/垂直翻转。核心代码使用了PixelMap的rotate和flip方法并建立了RotationManager工具类封装旋转逻辑同时通过image.createPixelMap创建新的 PixelMap 来保存处理结果。本篇我们将继续深化图像处理能力聚焦于滤镜与色彩矩阵。我们将利用PixelMap.colorMatrix属性实现灰度、怀旧、冷色等预定义滤镜同时支持用户自定义 RGB 色彩矩阵参数所有调整均能实时更新预览。这会显著提升 APP 的实用性和视觉效果让用户能一键为图片赋予不同风格。2. 核心实现2.1 基础配置与常量定义首先我们需要了解colorMatrix的工作原理。它是一个 4×5 的浮点矩阵共 20 个值用于控制像素的 RGBA 通道。矩阵格式为[ r, r, r, r, r ] → 控制红色通道 [ g, g, g, g, g ] → 控制绿色通道 [ b, b, b, b, b ] → 控制蓝色通道 [ a, a, a, a, a ] → 控制 Alpha 通道矩阵与像素颜色值的计算方式为新颜色 矩阵 × 原颜色。我们将在ColorMatrixManager类中预定义几种常见滤镜矩阵// colorMatrixManager.ets/** * 色彩矩阵管理器 * 提供预定义滤镜矩阵和自定义矩阵工具 */import{image}fromkit.ImageKit;// 预定义滤镜类型枚举exportenumFilterType{NORMAL原图,GRAY灰度,VINTAGE怀旧,COOL冷色,WARM暖色,SEPIA棕褐色,INVERT负片}// 各滤镜对应的 4x5 矩阵20个浮点数exportconstFILTER_MATRICES:Recordstring,number[]{// 灰度滤镜将RGB转换为亮度值[FilterType.GRAY]:[0.33,0.59,0.11,0,0,0.33,0.59,0.11,0,0,0.33,0.59,0.11,0,0,0,0,0,1,0],// 怀旧滤镜降低饱和度偏黄棕色调[FilterType.VINTAGE]:[0.5,0.3,0.2,0,40,0.4,0.6,0.2,0,20,0.2,0.3,0.4,0,10,0,0,0,1,0],// 冷色滤镜增加蓝色、减少红色[FilterType.COOL]:[0.5,0.5,1.0,0,0,0.5,0.8,1.0,0,0,1.0,0.8,1.2,0,0,0,0,0,1,0],// 暖色滤镜增加红/黄色[FilterType.WARM]:[1.2,0.9,0.5,0,10,1.0,1.0,0.5,0,5,0.6,0.7,0.8,0,-10,0,0,0,1,0],// 棕褐色滤镜经典老照片效果[FilterType.SEPIA]:[0.39,0.77,0.19,0,0,0.35,0.69,0.17,0,0,0.27,0.53,0.13,0,0,0,0,0,1,0],// 负片滤镜颜色取反[FilterType.INVERT]:[-1,0,0,0,255,0,-1,0,0,255,0,0,-1,0,255,0,0,0,1,0],// 原图滤镜单位矩阵[FilterType.NORMAL]:[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0]};关键点说明矩阵的 20 个浮点数对应 4 行 5 列分别控制 R、G、B、A 通道。第 5 列是颜色偏移值即亮度 / 对比度调整。灰度滤镜将彩色转换为亮度值亮度公式为 0.33R 0.59G 0.11B。怀旧和冷色等滤镜通过调整各通道的权重和偏移来改变色温。所有矩阵值都在合理的范围内避免产生溢出或失真。2.2 核心逻辑ColorMatrixManager 类接下来我们实现ColorMatrixManager类封装应用滤镜的核心操作// colorMatrixManager.ets (续)/** * 色彩矩阵应用管理器 * 负责将滤镜矩阵应用到 PixelMap 上 */exportclassColorMatrixManager{/** * 应用色彩矩阵到 PixelMap * param pixelMap 原始 PixelMap * param matrix 4x5 色彩矩阵20个浮点数 * returns 处理后的新 PixelMap */publicstaticasyncapplyColorMatrix(pixelMap:image.PixelMap,matrix:number[],context:Object):Promiseimage.PixelMap{if(!pixelMap){thrownewError(PixelMap 不能为空);}if(matrix.length!20){thrownewError(矩阵必须包含 20 个浮点数);}// 获取原始图片信息constimageSize:image.Size{height:pixelMap.size.height,width:pixelMap.size.width};constpixelMapInfoawaitpixelMap.getImageInfo();// 调用 PixelMap 的 colorMatrix 属性设置色彩矩阵// 使用可变区域mutable的 PixelMap 进行修改pixelMap.colorMatrix(matrix);// 返回修改后的 PixelMap已原地修改returnpixelMap;}/** * 应用滤镜并返回新的 PixelMap不修改原图 * param pixelMap 原始 PixelMap * param filterType 滤镜类型 * returns 处理后的新 PixelMap */publicstaticasyncapplyFilter(pixelMap:image.PixelMap,filterType:FilterType):Promiseimage.PixelMap{// 获取对应滤镜矩阵constmatrixFILTER_MATRICES[filterType];if(!matrix){thrownewError(不支持的滤镜类型:${filterType});}// 将矩阵应用到 PixelMappixelMap.colorMatrix(matrix);returnpixelMap;}/** * 自定义色彩矩阵 * param pixelMap 原始 PixelMap * param customMatrix 自定义的 20 个浮点数矩阵 * returns 处理后的 PixelMap */publicstaticasyncapplyCustomMatrix(pixelMap:image.PixelMap,customMatrix:number[]):Promiseimage.PixelMap{if(customMatrix.length!20){thrownewError(自定义矩阵必须包含 20 个浮点数);}pixelMap.colorMatrix(customMatrix);returnpixelMap;}}关键点说明pixelMap.colorMatrix(matrix)是PixelMap的内置方法直接传入一个 20 个浮点数的数组即可应用色彩矩阵。该方法会原地修改PixelMap的像素数据因此我们需要确保传入的 PixelMap 是可变的mutable。文档中明确要求矩阵数组长度为 20否则会抛出异常。我们提供了applyFilter便捷方法只需传入滤镜枚举即可一键应用。注意colorMatrix方法直接修改原始 PixelMap若需保留原图应在调用前先复制一份通过image.createPixelMap创建副本。2.3 完整模块FilterPage 页面现在我们来实现完整的FilterPage页面包含滤镜选择和预览功能// FilterPage.ets/** * 滤镜与色彩矩阵页面 * 支持选择预定义滤镜和自定义色彩矩阵参数 */import{image}fromkit.ImageKit;import{ColorMatrixManager,FilterType,FILTER_MATRICES}from../utils/colorMatrixManager;EntryComponentstruct FilterPage{StateprivatecurrentFilter:FilterTypeFilterType.NORMAL;StateprivateselectedFilterIndex:number0;StateprivatepixelMap:image.PixelMap|nullnull;StateprivateoriginalPixelMap:image.PixelMap|nullnull;StateprivatecustomMatrix:number[][1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0];// 滤镜列表用于 UI 展示privatefilterList:FilterType[]Object.values(FilterType);aboutToAppear(){// 模拟接收从上一页面传递过来的 PixelMap// 实际项目中通过 AppStorage 或页面路由传递AppStorage.getimage.PixelMap(currentPixelMap).then((pixelMap){if(pixelMap){this.originalPixelMappixelMap;// 创建副本用于滤镜处理this.copyPixelMap(pixelMap);}});}/** * 复制 PixelMap 用于滤镜处理 */privateasynccopyPixelMap(source:image.PixelMap){constimageInfoawaitsource.getImageInfo();constpixelMap:image.PixelMapawaitimage.createPixelMap({width:imageInfo.size.width,height:imageInfo.size.height,pixelFormat:image.PixelMapFormats.RGBA_8888,alphaType:image.AlphaType.PREMUL,scaleMode:image.ScaleMode.FIT_TARGET_SIZE});// 复制像素数据constsrcArrayBuffersource.readPixelsToBuffer();awaitpixelMap.writeBufferToPixels(srcArrayBuffer);this.pixelMappixelMap;}/** * 应用滤镜 */privateasyncapplyFilter(filterType:FilterType){if(!this.pixelMap||!this.originalPixelMap){console.error(PixelMap 未就绪);return;}try{// 重置为原图awaitthis.copyPixelMap(this.originalPixelMap);// 应用滤镜awaitColorMatrixManager.applyFilter(this.pixelMap!,filterType);this.currentFilterfilterType;}catch(error){console.error(应用滤镜失败:,JSON.stringify(error));}}/** * 应用自定义矩阵 */privateasyncapplyCustomMatrix(){if(!this.pixelMap||!this.originalPixelMap){return;}try{awaitthis.copyPixelMap(this.originalPixelMap);awaitColorMatrixManager.applyCustomMatrix(this.pixelMap!,this.customMatrix);this.currentFilterFilterType.NORMAL;}catch(error){console.error(应用自定义矩阵失败:,error);}}build(){Column(){// 图片预览区域if(this.pixelMap){Image(this.pixelMap).width(100%).height(300).objectFit(ImageFit.Contain).margin({top:10})}else{Text(请先选择图片).width(100%).height(300).textAlign(TextAlign.Center).backgroundColor(#f0f0f0)}// 当前滤镜名称显示Text(当前滤镜:${this.currentFilter}).fontSize(16).fontWeight(FontWeight.Bold).margin({top:10})// 预定义滤镜列表Scroll(){Column(){ForEach(this.filterList,(filter:FilterType){Button(filter).width(90%).height(48).margin({top:6}).backgroundColor(filterthis.currentFilter?#007DFF:#E0E0E0).fontColor(filterthis.currentFilter?#FFFFFF:#333333).onClick((){this.applyFilter(filter);})})}.width(100%).padding({left:16,right:16})}.height(150)// 自定义矩阵参数滑块简化示例Text(自定义颜色调整).fontSize(14).margin({top:10})Row(){Text(红色:)Slider({value:this.customMatrix[0],min:0.0,max:2.0,step:0.1}).onChange((value:number){this.customMatrix[0]value;this.applyCustomMatrix();})}.padding({left:16,right:16})Row(){Text(绿色:)Slider({value:this.customMatrix[6],min:0.0,max:2.0,step:0.1}).onChange((value:number){this.customMatrix[6]value;this.applyCustomMatrix();})}.padding({left:16,right:16})Row(){Text(蓝色:)Slider({value:this.customMatrix[12],min:0.0,max:2.0,step:0.1}).onChange((value:number){this.customMatrix[12]value;this.applyCustomMatrix();})}.padding({left:16,right:16})}.width(100%).height(100%).backgroundColor(#FFFFFF)}}关键点说明页面初始化时从AppStorage获取上一页面传递的PixelMap并创建副本用于滤镜处理避免破坏原始图片。copyPixelMap方法通过image.createPixelMap和writeBufferToPixels复制像素数据。滤镜列表使用ForEach渲染选中按钮高亮显示。自定义矩阵滑块允许用户调整 RGB 三通道的权重实时应用并预览效果。colorMatrix方法直接作用于当前 PixelMap因此每次应用滤镜前需要恢复为原图。2.4 PixelMapUtils 工具类为了便于资源管理我们创建一个PixelMapUtils类来封装 PixelMap 的复制和释放操作// pixelMapUtils.ets/** * PixelMap 工具类 * 提供 PixelMap 复制、释放等通用操作 */import{image}fromkit.ImageKit;exportclassPixelMapUtils{/** * 复制 PixelMap * param source 源 PixelMap * returns 复制的 PixelMap */publicstaticasynccopyPixelMap(source:image.PixelMap):Promiseimage.PixelMap{constimageInfoawaitsource.getImageInfo();constwidthimageInfo.size.width;constheightimageInfo.size.height;// 创建新的 PixelMapconstpixelMap:image.PixelMapawaitimage.createPixelMap({width:width,height:height,pixelFormat:image.PixelMapFormats.RGBA_8888,alphaType:image.AlphaType.PREMUL,scaleMode:image.ScaleMode.FIT_TARGET_SIZE});// 读取源像素并写入新 PixelMapconstbuffersource.readPixelsToBuffer();awaitpixelMap.writeBufferToPixels(buffer);returnpixelMap;}/** * 释放 PixelMap 资源 * param pixelMap 要释放的 PixelMap */publicstaticreleasePixelMap(pixelMap:image.PixelMap|null){if(pixelMap){try{pixelMap.release();}catch(error){console.error(释放 PixelMap 失败:,JSON.stringify(error));}}}}关键点说明copyPixelMap方法先创建与原图尺寸相同的 RGBA_8888 格式 PixelMap再通过readPixelsToBuffer和writeBufferToPixels复制像素数据。releasePixelMap用于及时释放不再需要的 PixelMap 资源避免内存泄漏。该工具类后续在页面切换或批量处理中非常有用。3. 运行验证完成上述代码后将FilterPage添加到项目主页面中例如通过 Tab 切换或页面路由跳转。运行效果如下APP 启动后选择一张图片进入滤镜模块。显示原始图片下方列出所有预定义滤镜按钮。点击「灰度」按钮图片变为黑白效果。点击「怀旧」按钮图片呈现泛黄的老照片风格。通过底部自定义滑块调整红色/绿色/蓝色权重图片颜色实时变化。4. 小结与预告本篇我们成功实现了滤镜与色彩矩阵模块核心成果包括ColorMatrixManager类封装了 7 种预定义滤镜矩阵和自定义矩阵应用方法。FilterPage页面提供了完整的滤镜选择和实时预览功能支持一键切换。PixelMapUtils工具类简化了 PixelMap 的复制和资源管理。这些功能使 APP 的图像处理能力从基本的几何变换跃升至色彩风格调整大幅提升了用户视觉体验和实用性。下篇我们将继续深入色彩调整领域聚焦亮度、对比度、饱和度的精细调节通过像素点操作和色彩矩阵组合实现更专业的图像优化。