【Android 美颜相机】第十三天:GPUImage3x3ConvolutionFilter解析

GPUImage3x3ConvolutionFilter 代码全解析

本文将逐行解析 GPUImage3x3ConvolutionFilter.java 代码,讲解其核心逻辑、代码含义及实际使用方式,该类是基于 OpenGL ES 实现的 3x3 卷积滤镜,常用于图片/视频的像素级处理(如锐化、模糊、边缘检测等)。

代码整体结构与基础声明

1. 版权与许可证声明

java 复制代码
/*
 * Copyright (C) 2018 CyberAgent, Inc.  // 版权归属:CyberAgent 公司
 *
 * Licensed under the Apache License, Version 2.0 (the "License");  // 遵循 Apache 2.0 开源许可证
 * you may not use this file except in compliance with the License.  // 使用代码需遵守许可证条款
 * You may obtain a copy of the License at  // 许可证获取地址
 *
 * http://www.apache.org/licenses/LICENSE-2.0  // 许可证官方地址
 *
 * Unless required by applicable law or agreed to in writing, software  // 除非法律要求或书面约定
 * distributed under the License is distributed on an "AS IS" BASIS,  // 软件按"原样"分发
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  // 无明示/默示担保
 * See the License for the specific language governing permissions and  // 许可证定义了权限和限制
 * limitations under the License.
 */

作用:开源代码的标准版权和许可证声明,明确代码的使用规则和法律约束。

2. 包导入

java 复制代码
package jp.co.cyberagent.android.gpuimage.filter;  // 归属 GPUImage 滤镜包

import android.opengl.GLES20;  // 导入 OpenGL ES 2.0 核心 API,用于操作着色器、统一变量等

作用:声明代码所属包,并导入 OpenGL ES 2.0 相关 API,是 Android 中实现 GPU 渲染的基础依赖。

类定义与核心着色器

1. 类声明

java 复制代码
/**
 * Runs a 3x3 convolution kernel against the image  // 类功能注释:对图像执行 3x3 卷积核计算
 */
public class GPUImage3x3ConvolutionFilter extends GPUImage3x3TextureSamplingFilter {  // 继承自 3x3 纹理采样滤镜基类,复用纹理采样逻辑

核心含义 :该类是 GPUImage 框架下的自定义滤镜,继承基类 GPUImage3x3TextureSamplingFilter(该基类已实现 3x3 纹理邻域的坐标计算,如左/右/上/下像素坐标),专注于 3x3 卷积计算。

2. 片段着色器(GLSL 代码)

片段着色器是 GPU 执行像素级计算的核心,该部分定义了卷积的具体逻辑:

java 复制代码
    public static final String THREE_X_THREE_TEXTURE_SAMPLING_FRAGMENT_SHADER = "" +
            "precision highp float;\n" +  // 设置浮点精度为高精度,保证计算准确性
            "\n" +
            "uniform sampler2D inputImageTexture;\n" +  // 输入纹理(待处理的图像),uniform 表示全局变量(CPU 传给 GPU)
            "\n" +
            "uniform mediump mat3 convolutionMatrix;\n" +  // 3x3 卷积矩阵,mediump 中等精度(平衡性能和精度)
            "\n" +
            "varying vec2 textureCoordinate;\n" +  // 当前像素的纹理坐标(顶点着色器传递到片段着色器)
            "varying vec2 leftTextureCoordinate;\n" +  // 左侧像素纹理坐标(基类计算)
            "varying vec2 rightTextureCoordinate;\n" +  // 右侧像素纹理坐标(基类计算)
            "\n" +
            "varying vec2 topTextureCoordinate;\n" +  // 上方像素纹理坐标(基类计算)
            "varying vec2 topLeftTextureCoordinate;\n" +  // 左上像素纹理坐标(基类计算)
            "varying vec2 topRightTextureCoordinate;\n" +  // 右上像素纹理坐标(基类计算)
            "\n" +
            "varying vec2 bottomTextureCoordinate;\n" +  // 下方像素纹理坐标(基类计算)
            "varying vec2 bottomLeftTextureCoordinate;\n" +  // 左下像素纹理坐标(基类计算)
            "varying vec2 bottomRightTextureCoordinate;\n" +  // 右下像素纹理坐标(基类计算)
            "\n" +
            "void main()\n" +  // 片段着色器主函数,每个像素执行一次
            "{\n" +
            "    // 采样当前像素及 8 邻域的像素颜色(RGBA)\n" +
            "    mediump vec4 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate);\n" +  // 采样下方像素颜色
            "    mediump vec4 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate);\n" +  // 采样左下像素颜色
            "    mediump vec4 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate);\n" +  // 采样右下像素颜色
            "    mediump vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);\n" +  // 采样中心(当前)像素颜色
            "    mediump vec4 leftColor = texture2D(inputImageTexture, leftTextureCoordinate);\n" +  // 采样左侧像素颜色
            "    mediump vec4 rightColor = texture2D(inputImageTexture, rightTextureCoordinate);\n" +  // 采样右侧像素颜色
            "    mediump vec4 topColor = texture2D(inputImageTexture, topTextureCoordinate);\n" +  // 采样上方像素颜色
            "    mediump vec4 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate);\n" +  // 采样右上像素颜色
            "    mediump vec4 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate);\n" +  // 采样左上像素颜色
            "\n" +
            "    // 核心:卷积计算(3x3 矩阵与邻域像素颜色相乘后累加)\n" +
            "    mediump vec4 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];\n" +  // 第一行卷积计算
            "    resultColor += leftColor * convolutionMatrix[1][0] + centerColor * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];\n" +  // 第二行卷积计算
            "    resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];\n" +  // 第三行卷积计算
            "\n" +
            "    gl_FragColor = resultColor;\n" +  // 输出最终像素颜色(卷积结果)
            "}";

核心逻辑

  • 纹理采样:通过 texture2D 从输入纹理中获取当前像素及 8 个邻域像素的颜色值;
  • 卷积计算:将 3x3 卷积矩阵的每个元素与对应位置的像素颜色相乘,累加后得到最终像素颜色;
  • 输出结果:将卷积结果赋值给 gl_FragColor,作为当前像素的最终颜色。

成员变量定义

java 复制代码
    private float[] convolutionKernel;  // 存储 3x3 卷积核的数组(长度为 9,按行优先存储:行0[0,1,2]、行1[3,4,5]、行2[6,7,8])
    private int uniformConvolutionMatrix;  // OpenGL 中"convolutionMatrix"统一变量的位置(用于 CPU 向 GPU 传递卷积矩阵)

作用

  • convolutionKernel:存储卷积核的数值(如锐化核、边缘检测核等);
  • uniformConvolutionMatrix:标记 GPU 着色器中卷积矩阵变量的位置,是 CPU 与 GPU 通信的关键标识。

构造方法

1. 默认构造方法

java 复制代码
    /**
     * Instantiates a new GPUimage3x3ConvolutionFilter with default values, that
     * will look like the original image.  // 注释:默认构造,滤镜效果为"原图"
     */
    public GPUImage3x3ConvolutionFilter() {
        this(new float[]{  // 调用带参数构造,传入"单位卷积核"
                0.0f, 0.0f, 0.0f,  // 第一行:0 0 0
                0.0f, 1.0f, 0.0f,  // 第二行:0 1 0(仅中心像素有效)
                0.0f, 0.0f, 0.0f   // 第三行:0 0 0
        });
    }

核心含义:默认卷积核是"单位矩阵",卷积后像素颜色与原图一致,无滤镜效果。

2. 带参数构造方法

java 复制代码
    /**
     * Instantiates a new GPUimage3x3ConvolutionFilter with given convolution kernel.
     *
     * @param convolutionKernel the convolution kernel  // 参数:自定义 3x3 卷积核(长度必须为9)
     */
    public GPUImage3x3ConvolutionFilter(final float[] convolutionKernel) {
        super(THREE_X_THREE_TEXTURE_SAMPLING_FRAGMENT_SHADER);  // 调用父类构造,传入自定义片段着色器
        this.convolutionKernel = convolutionKernel;  // 初始化卷积核数组
    }

核心含义:允许传入自定义卷积核,实现不同的滤镜效果(如锐化、模糊、边缘检测)。

生命周期方法(OpenGL 初始化)

1. onInit 方法

java 复制代码
    @Override
    public void onInit() {
        super.onInit();  // 调用父类初始化方法(如创建着色器程序)
        // 获取着色器中"convolutionMatrix"统一变量的位置,后续用于传递卷积核数据
        uniformConvolutionMatrix = GLES20.glGetUniformLocation(getProgram(), "convolutionMatrix");
    }

核心含义

  • 初始化时通过 GLES20.glGetUniformLocation 获取着色器中卷积矩阵变量的位置,该位置是 CPU 向 GPU 传递数据的"索引";
  • getProgram() 获取当前 OpenGL 着色器程序的 ID,是操作着色器的基础。

2. onInitialized 方法

java 复制代码
    @Override
    public void onInitialized() {
        super.onInitialized();  // 调用父类初始化完成方法
        setConvolutionKernel(convolutionKernel);  // 初始化卷积核,并传递到 GPU 着色器
    }

核心含义:着色器程序初始化完成后,立即将卷积核数据传递到 GPU,确保滤镜生效。

卷积核设置方法

java 复制代码
    /**
     * Sets the convolution kernel.
     *
     * @param convolutionKernel the new convolution kernel  // 参数:新的 3x3 卷积核
     */
    public void setConvolutionKernel(final float[] convolutionKernel) {
        this.convolutionKernel = convolutionKernel;  // 更新本地卷积核数组
        // 将卷积核数组转换为 3x3 矩阵,传递到 GPU 着色器的统一变量中
        setUniformMatrix3f(uniformConvolutionMatrix, this.convolutionKernel);
    }

核心含义

  • 对外提供更新卷积核的接口,支持动态切换滤镜效果;
  • setUniformMatrix3f 是 GPUImage 框架封装的方法,将 float 数组(长度9)转换为 3x3 矩阵,并通过 uniformConvolutionMatrix 位置传递到 GPU 着色器中。

实际使用方式

1. 基础使用(默认原图效果)

java 复制代码
// 1. 创建滤镜实例(默认单位卷积核,无效果)
GPUImage3x3ConvolutionFilter filter = new GPUImage3x3ConvolutionFilter();

// 2. 绑定到 GPUImage 实例(GPUImage 是核心渲染类)
GPUImage gpuImage = new GPUImage(context);
gpuImage.setFilter(filter);

// 3. 处理图片并显示/保存
gpuImage.setImage(bitmap);  // 设置待处理的图片
Bitmap resultBitmap = gpuImage.getBitmapWithFilterApplied();  // 获取处理后的图片
imageView.setImageBitmap(resultBitmap);  // 显示结果

2. 自定义卷积核(实现锐化效果)

锐化卷积核示例:

复制代码
1  1  1
1 -7  1
1  1  1

代码实现:

java 复制代码
// 1. 定义锐化卷积核(行优先:第一行1,1,1;第二行1,-7,1;第三行1,1,1)
float[] sharpenKernel = new float[]{
        1.0f, 1.0f, 1.0f,
        1.0f, -7.0f, 1.0f,
        1.0f, 1.0f, 1.0f
};

// 2. 创建自定义卷积核的滤镜
GPUImage3x3ConvolutionFilter sharpenFilter = new GPUImage3x3ConvolutionFilter(sharpenKernel);

// 3. 应用滤镜
GPUImage gpuImage = new GPUImage(context);
gpuImage.setFilter(sharpenFilter);
gpuImage.setImage(originalBitmap);
Bitmap sharpenedBitmap = gpuImage.getBitmapWithFilterApplied();
imageView.setImageBitmap(sharpenedBitmap);

3. 动态切换卷积核(运行时修改效果)

java 复制代码
// 初始化滤镜(默认效果)
GPUImage3x3ConvolutionFilter filter = new GPUImage3x3ConvolutionFilter();
gpuImage.setFilter(filter);

// 运行时切换为边缘检测核
float[] edgeDetectionKernel = new float[]{
        1.0f, 0.0f, -1.0f,
        1.0f, 0.0f, -1.0f,
        1.0f, 0.0f, -1.0f
};
filter.setConvolutionKernel(edgeDetectionKernel);  // 动态更新卷积核
Bitmap edgeBitmap = gpuImage.getBitmapWithFilterApplied();  // 重新获取处理后的图片

总结

  1. 核心原理:基于 3x3 卷积矩阵对图像每个像素的 8 邻域进行加权计算,实现像素级滤镜效果;
  2. 代码设计
    • 复用基类的纹理坐标计算逻辑,专注于卷积核的传递和计算;
    • 通过 OpenGL ES 的 uniform 变量实现 CPU 到 GPU 的卷积核数据传递;
  3. 扩展场景 :通过修改卷积核可实现锐化、模糊、边缘检测、浮雕等多种效果,是图像处理的基础滤镜类。
相关推荐
游戏开发爱好者82 小时前
在 Linux 环境通过命令行上传 IPA 到 App Store,iOS自动化构建与发布
android·linux·ios·小程序·uni-app·自动化·iphone
帅次2 小时前
系统分析师-移动应用系统分析与设计
android·ios·微信小程序·小程序·android studio·webview
MengFly_2 小时前
Compose中rememberUpdatedState的作用
android·kotlin·compose
韩立学长2 小时前
【开题答辩实录分享】以《志愿者公益网站的设计与实现》为例进行选题答辩实录分享
android·java·开发语言
%xiao Q11 小时前
GESP C++五级-202406
android·开发语言·c++
二哈喇子!12 小时前
JavaWeb+Vue分离项目实现增删改查讲解
android
2501_9445215913 小时前
Flutter for OpenHarmony 微动漫App实战:推荐动漫实现
android·开发语言·前端·javascript·flutter·ecmascript
2501_9445215914 小时前
Flutter for OpenHarmony 微动漫App实战:图片加载实现
android·开发语言·前端·javascript·flutter·php
新镜15 小时前
【Flutter】LTR/RTL 阿拉伯语言/希伯来语言
android·flutter·ios·客户端