
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(); // 重新获取处理后的图片
总结
- 核心原理:基于 3x3 卷积矩阵对图像每个像素的 8 邻域进行加权计算,实现像素级滤镜效果;
- 代码设计 :
- 复用基类的纹理坐标计算逻辑,专注于卷积核的传递和计算;
- 通过 OpenGL ES 的
uniform变量实现 CPU 到 GPU 的卷积核数据传递;
- 扩展场景 :通过修改卷积核可实现锐化、模糊、边缘检测、浮雕等多种效果,是图像处理的基础滤镜类。
