2026/3/26 22:02:20
网站建设
项目流程
河北网站建设价格大全,模板在线制作,商丘网站推广,上海做网站比较有名的公司有哪些GPUImageChromaKeyBlendFilter 代码全解析
本文将逐行解析 GPUImageChromaKeyBlendFilter.java 代码#xff0c;涵盖代码注释、模块功能、核心逻辑及实际使用方式#xff0c;该类是 Android 平台基于 OpenGL ES 2.0 实现的色度键混合滤镜#xff08;绿幕抠图#xff09;涵盖代码注释、模块功能、核心逻辑及实际使用方式该类是 Android 平台基于 OpenGL ES 2.0 实现的色度键混合滤镜绿幕抠图属于 GPUImage 开源框架的核心滤镜之一。完整代码逐行注释版/* * Copyright (C) 2018 CyberAgent, Inc. * 版权声明归属CyberAgent公司2018年发布 * * 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. */// 声明包名归属CyberAgent的GPUImage滤镜模块packagejp.co.cyberagent.android.gpuimage.filter;// 导入OpenGL ES 2.0核心API用于操作着色器、统一变量等底层GL逻辑importandroid.opengl.GLES20;/** * Selectively replaces a color in the first image with the second image * 类功能描述选择性将第一张图片中的指定颜色区域替换为第二张图片的内容绿幕抠图核心逻辑 */publicclassGPUImageChromaKeyBlendFilterextendsGPUImageTwoInputFilter{// 定义色度键混合的片段着色器GLSL代码运行在GPU上逐像素计算publicstaticfinalStringCHROMA_KEY_BLEND_FRAGMENT_SHADER precision highp float;\n// 声明浮点精度为高精度保证颜色计算无失真 \n varying highp vec2 textureCoordinate;\n// 顶点着色器传递的第一个输入纹理坐标待抠图的绿幕图 varying highp vec2 textureCoordinate2;\n// 顶点着色器传递的第二个输入纹理坐标替换的背景图\n uniform float thresholdSensitivity;\n// 统一变量颜色匹配阈值控制抠图的严格程度 uniform float smoothing;\n// 统一变量平滑度控制抠图边缘的渐变过渡 uniform vec3 colorToReplace;\n// 统一变量需要替换的目标颜色RGB归一化值 uniform sampler2D inputImageTexture;\n// 统一变量第一个输入纹理的采样器关联绿幕图纹理ID uniform sampler2D inputImageTexture2;\n// 统一变量第二个输入纹理的采样器关联背景图纹理ID \n void main()\n// 片段着色器主函数每个像素执行一次 {\n vec4 textureColor texture2D(inputImageTexture, textureCoordinate);\n// 采样绿幕图当前像素的RGBA颜色 vec4 textureColor2 texture2D(inputImageTexture2, textureCoordinate2);\n// 采样背景图当前像素的RGBA颜色 \n// 计算目标替换颜色的YCrCb分量亮度色度抠图核心只比较色度避免亮度干扰 float maskY 0.2989 * colorToReplace.r 0.5866 * colorToReplace.g 0.1145 * colorToReplace.b;\n// 目标颜色的亮度YITU-R BT.601标准转换公式 float maskCr 0.7132 * (colorToReplace.r - maskY);\n// 目标颜色的红色差Cr float maskCb 0.5647 * (colorToReplace.b - maskY);\n// 目标颜色的蓝色差Cb \n// 计算绿幕图当前像素的YCrCb分量 float Y 0.2989 * textureColor.r 0.5866 * textureColor.g 0.1145 * textureColor.b;\n// 当前像素的亮度Y float Cr 0.7132 * (textureColor.r - Y);\n// 当前像素的红色差Cr float Cb 0.5647 * (textureColor.b - Y);\n// 当前像素的蓝色差Cb \n// 计算混合权重判断当前像素是否属于目标颜色决定替换程度 float blendValue 1.0 - smoothstep(thresholdSensitivity, thresholdSensitivity smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));\n/* * 核心逻辑拆解 * 1. distance计算当前像素与目标颜色的Cr/Cb色度欧氏距离 * 2. smoothstep将距离映射到[阈值, 阈值平滑度]区间生成0~1的平滑值 * 3. 1.0 - 结果距离越近越接近目标颜色blendValue越接近1完全替换为背景图 */ gl_FragColor mix(textureColor, textureColor2, blendValue);\n// 按权重混合像素mix(a,b,t)t0取a绿幕图t1取b背景图 };// GLSL统一变量的位置句柄用于Java层向GPU传递参数privateintthresholdSensitivityLocation;// 阈值灵敏度变量句柄privateintsmoothingLocation;// 平滑度变量句柄privateintcolorToReplaceLocation;// 目标颜色变量句柄// 滤镜默认参数privatefloatthresholdSensitivity0.4f;// 阈值灵敏度默认值privatefloatsmoothing0.1f;// 平滑度默认值privatefloat[]colorToReplacenewfloat[]{0.0f,1.0f,0.0f};// 默认替换绿色RGB归一化// 无参构造方法publicGPUImageChromaKeyBlendFilter(){super(CHROMA_KEY_BLEND_FRAGMENT_SHADER);// 调用父类构造方法传入自定义片段着色器顶点着色器复用父类双输入纹理实现}OverridepublicvoidonInit(){// 重写父类初始化方法GL程序创建后调用super.onInit();// 获取GLSL统一变量的位置句柄通过变量名关联thresholdSensitivityLocationGLES20.glGetUniformLocation(getProgram(),thresholdSensitivity);smoothingLocationGLES20.glGetUniformLocation(getProgram(),smoothing);colorToReplaceLocationGLES20.glGetUniformLocation(getProgram(),colorToReplace);}OverridepublicvoidonInitialized(){// 重写父类方法GL程序初始化完成后调用super.onInitialized();// 设置默认参数到GPU的统一变量中setSmoothing(smoothing);setThresholdSensitivity(thresholdSensitivity);setColorToReplace(colorToReplace[0],colorToReplace[1],colorToReplace[2]);}/** * The degree of smoothing controls how gradually similar colors are replaced in the image * The default value is 0.1 * 平滑度Setter控制相似颜色的替换渐变程度值越大边缘越自然 */publicvoidsetSmoothing(finalfloatsmoothing){this.smoothingsmoothing;// 更新本地参数setFloat(smoothingLocation,this.smoothing);// 传递给GPU的统一变量}/** * The threshold sensitivity controls how similar pixels need to be colored to be replaced * The default value is 0.3 * 阈值灵敏度Setter控制像素颜色与目标颜色的相似度要求值越小越严格 */publicvoidsetThresholdSensitivity(finalfloatthresholdSensitivity){this.thresholdSensitivitythresholdSensitivity;// 更新本地参数setFloat(thresholdSensitivityLocation,this.thresholdSensitivity);// 传递给GPU}/** * The color to be replaced is specified using individual red, green, and blue components (normalized to 1.0). * The default is green: (0.0, 1.0, 0.0). * 目标替换颜色Setter通过归一化RGB值0~1指定要抠除的颜色 * * param redComponent 红色分量0~1 * param greenComponent 绿色分量0~1 * param blueComponent 蓝色分量0~1 */publicvoidsetColorToReplace(floatredComponent,floatgreenComponent,floatblueComponent){colorToReplacenewfloat[]{redComponent,greenComponent,blueComponent};// 更新本地颜色setFloatVec3(colorToReplaceLocation,colorToReplace);// 传递RGB向量给GPU}}代码模块功能解析1. 基础层版权与依赖导入版权声明遵循 Apache 2.0 许可证明确使用权限包导入jp.co.cyberagent.android.gpuimage.filter是 GPUImage 框架的滤镜核心包android.opengl.GLES20是 Android 操作 OpenGL ES 2.0 的核心 API用于与 GPU 通信。2. 核心层类与继承关系类名GPUImageChromaKeyBlendFilter色度键混合滤镜继承GPUImageTwoInputFilter双输入纹理滤镜基类支持同时处理“绿幕图待抠图”和“背景图替换图”两张输入图片是实现抠图的基础。3. 核心逻辑GLSL 片段着色器片段着色器是滤镜的核心运行在 GPU 上核心逻辑如下色彩空间转换将 RGB 转换为 YCrCb亮度色度仅比较色度Cr/Cb避免亮度干扰是抠图的关键优化混合权重计算通过distance计算当前像素与目标颜色的色度距离结合smoothstep生成 0~1 的混合权重像素混合通过mix按权重混合两张图的像素实现“目标颜色区域替换为背景图”。4. 交互层成员变量与生命周期统一变量句柄thresholdSensitivityLocation等变量存储 GLSL 统一变量的索引是 Java 层向 GPU 传参的“桥梁”默认参数预设绿色为抠除颜色、阈值 0.4、平滑度 0.1适配常规绿幕场景生命周期方法onInit()GL 程序创建后获取统一变量句柄onInitialized()GL 程序初始化完成后设置默认参数到 GPU。5. 配置层Setter 方法提供三个核心参数的配置接口支持动态调整抠图效果setSmoothing调整抠图边缘的平滑度值越大边缘过渡越自然避免锯齿setThresholdSensitivity调整颜色匹配的严格程度值越小仅“极接近目标颜色”的像素会被替换setColorToReplace支持自定义抠除颜色如蓝幕可传0.0f, 0.0f, 1.0f。着色器绿幕抠图工作原理该着色器的执行规则是为图像的每个像素执行一次main函数通过「颜色识别→权重计算→像素混合」的三步核心逻辑完成抠图全程在GPU上并行执行效率远高于CPU端的抠图计算适合移动端图片/视频的实时抠图场景。下面先附上逐行精准注释版代码再分阶段拆解其完整工作原理并解析核心参数的作用。逐行精准注释版代码// 声明浮点精度为高精度保证颜色计算的准确性避免抠图出现色彩失真precision highpfloat;// 从顶点着色器传递的纹理坐标易变变量经光栅化插值后逐像素唯一// 对应第一个输入纹理待抠图的绿幕/蓝幕图inputImageTexturevarying highp vec2 textureCoordinate;// 对应第二个输入纹理替换的背景图inputImageTexture2varying highp vec2 textureCoordinate2;// 统一变量颜色匹配阈值灵敏度Java层可动态调整控制抠图的颜色匹配严格程度uniformfloatthresholdSensitivity;// 统一变量边缘平滑度Java层可动态调整控制抠图边缘的渐变过渡效果避免锯齿uniformfloatsmoothing;// 统一变量需要抠除/替换的目标颜色RGB三维向量值为0~1的归一化值如绿色是(0.0,1.0,0.0)uniform vec3 colorToReplace;// 统一变量2D纹理采样器关联第一个输入纹理绿幕图的ID用于采样像素颜色uniform sampler2D inputImageTexture;// 统一变量2D纹理采样器关联第二个输入纹理背景图的IDuniform sampler2D inputImageTexture2;// 片段着色器主函数每个像素执行一次是抠图逻辑的核心入口voidmain(){// 步骤1采样两个纹理的当前像素颜色// 从绿幕图中采样当前像素的RGBA颜色vec4r/g/b/a均为0~1vec4 textureColortexture2D(inputImageTexture,textureCoordinate);// 从背景图中采样当前像素的RGBA颜色与绿幕图像素位置一一对应vec4 textureColor2texture2D(inputImageTexture2,textureCoordinate2);// 步骤2将「目标替换颜色」从RGB色彩空间转换为YCrCb色彩空间// 计算目标颜色的亮度分量YITU-R BT.601标准转换公式数字图像通用floatmaskY0.2989*colorToReplace.r0.5866*colorToReplace.g0.1145*colorToReplace.b;// 计算目标颜色的红色差分量Cr色度分量反映红色与亮度的差值floatmaskCr0.7132*(colorToReplace.r-maskY);// 计算目标颜色的蓝色差分量Cb色度分量反映蓝色与亮度的差值floatmaskCb0.5647*(colorToReplace.b-maskY);// 步骤3将「绿幕图当前像素颜色」也转换为YCrCb色彩空间// 计算当前像素的亮度分量YfloatY0.2989*textureColor.r0.5866*textureColor.g0.1145*textureColor.b;// 计算当前像素的红色差分量CrfloatCr0.7132*(textureColor.r-Y);// 计算当前像素的蓝色差分量CbfloatCb0.5647*(textureColor.b-Y);// 步骤4计算混合权重blendValue核心判断当前像素是否为目标颜色决定替换程度floatblendValue1.0-smoothstep(thresholdSensitivity,thresholdSensitivitysmoothing,distance(vec2(Cr,Cb),vec2(maskCr,maskCb)));// 步骤5按权重混合两个像素颜色输出最终像素颜色// mix(a, b, t)GLSL内置混合函数t0取a绿幕图t1取b背景图0~1之间平滑过渡gl_FragColormix(textureColor,textureColor2,blendValue);}分阶段拆解核心工作原理整个着色器的抠图逻辑分为5个核心阶段环环相扣最终实现“精准抠除目标颜色、自然替换背景”的效果其中YCrCb色彩空间转换和混合权重计算是最关键的两个环节。阶段1纹理采样——获取两个纹理的当前像素颜色这是抠图的基础通过GLSL内置的texture2D(sampler, uv)函数根据顶点着色器传递并插值后的纹理坐标分别从**绿幕图inputImageTexture和背景图inputImageTexture2**中采样出当前像素的RGBA颜色得到两个变量textureColor绿幕图当前像素的颜色待判断是否为目标抠除颜色textureColor2背景图当前像素的颜色若绿幕图当前像素是目标颜色则用该颜色替换。关键细节两个纹理的采样坐标textureCoordinate和textureCoordinate2是一一对应的保证绿幕图的某个像素位置会精准替换为背景图相同位置的像素避免背景错位。阶段2色彩空间转换——RGB → YCrCb抠图的核心优化这是整个抠图逻辑的灵魂步骤也是为什么不用直接RGB颜色比较、而要先转换色彩空间的关键。2.1 为什么要转换为YCrCbRGB色彩空间的红、绿、蓝三个分量是相互关联的且受亮度影响极大。比如绿幕图中同一绿色因光照不均亮部/暗部RGB值会相差很大直接用RGB比较会导致“同色不同值”抠图不精准比如暗部的绿色没被抠除。而YCrCb色彩空间将颜色拆分为三个相互独立的分量完美解决了亮度干扰的问题YLuminance亮度分量反映像素的明暗程度CrChrominance Red红色差分量反映红色与亮度的差值色度分量CbChrominance Blue蓝色差分量反映蓝色与亮度的差值色度分量。其中Cr和Cb是色度分量仅代表颜色的“色相”与亮度无关——绿幕图中无论绿色是亮是暗其Cr和Cb值基本保持不变。因此通过比较Cr和Cb值来判断是否为目标颜色能彻底避免光照不均导致的抠图不精准问题这是工业级绿幕抠图的标准做法。2.2 转换公式说明代码中使用的是ITU-R BT.601标准转换公式这是数字图像如JPG/PNG/视频中RGB转YCrCb的通用公式系数是经过标准化的保证转换的准确性亮度YY 0.2989*R 0.5866*G 0.1145*B绿色对亮度的贡献最大红色次之蓝色最小符合人眼视觉特性红色差CrCr 0.7132*(R - Y)提取红色与亮度的差值隔离亮度影响蓝色差CbCb 0.5647*(B - Y)提取蓝色与亮度的差值隔离亮度影响。代码中分别对**目标替换颜色colorToReplace和绿幕图当前像素颜色textureColor**执行该转换得到各自的YCrCb分量为后续的颜色匹配做准备。阶段3色度距离计算——判断当前像素是否为目标颜色转换为YCrCb后完全忽略亮度分量Y仅通过色度分量Cr和Cb来判断当前像素是否为目标颜色核心是计算当前像素的Cr/Cb与目标颜色的Cr/Cb之间的欧氏距离。代码中通过GLSL内置的distance函数实现distance(vec2(Cr, Cb), vec2(maskCr, maskCb))vec2(Cr, Cb)绿幕图当前像素的色度坐标仅保留Cr和Cb形成二维向量vec2(maskCr, maskCb)目标替换颜色的色度坐标distance(a, b)计算两个二维向量之间的欧氏距离距离越小说明当前像素的色度与目标颜色的色度越相似也就是当前像素越接近要抠除的颜色。举个例子如果当前像素是纯绿色目标颜色其Cr/Cb与目标颜色的Cr/Cb完全一致距离为0如果当前像素是红色其Cr/Cb与目标颜色相差极大距离会很大。阶段4混合权重计算——生成0~1的平滑替换权重得到色度距离后需要将其转换为0~1之间的混合权重blendValue这个权重决定了当前像素“保留绿幕图”还是“替换为背景图”代码中通过smoothstep函数实现核心转换再做反向计算float blendValue 1.0 - smoothstep(thresholdSensitivity, thresholdSensitivity smoothing, distance(...));先拆解两个核心GLSL内置函数再讲权重的含义4.1 smoothstep生成平滑的0~1过渡值smoothstep(min, max, x)是GLSL的平滑步长函数核心作用是将值x映射到[min, max]区间并生成0~1之间的平滑插值规则如下若x ≤ min返回0.0若x ≥ max返回1.0若min x max返回0~1之间的平滑值采用三次插值过渡更自然无生硬突变。4.2 1.0 - 反向计算匹配抠图的权重逻辑结合smoothstep和反向计算blendValue的最终规则为色度距离情况smoothstep返回值blendValue值抠图逻辑混合规则距离 ≤ 阈值极相似0.01.0完全替换为背景图mix取textureColor2距离 ≥ 阈值平滑度完全不相似1.00.0完全保留绿幕图mix取textureColor阈值 距离 阈值平滑度相似但非纯目标色0~1的平滑值1~0的平滑值平滑混合绿幕图和背景图边缘过渡4.3 两个核心参数的作用thresholdSensitivity/smoothing这两个Java层可动态调整的参数直接控制抠图的精度和边缘自然度也是该滤镜的可配置核心thresholdSensitivity阈值灵敏度控制颜色匹配的严格程度。值越小仅“色度距离极近极接近目标颜色”的像素会被判定为抠除区域抠图更精准值越大会将“相似度一般”的像素也判定为抠除区域可能导致误抠非目标颜色。smoothing平滑度控制抠图边缘的过渡范围。值越大[阈值, 阈值平滑度]的区间越宽边缘的混合过渡越自然避免抠图边缘出现锯齿、生硬的断层值越小边缘过渡越窄抠图边缘越锐利但容易出现锯齿。举个例子若thresholdSensitivity0.4smoothing0.1则色度距离在00.4的像素会完全替换为背景0.40.5的像素会平滑混合0.5以上的像素完全保留绿幕图。阶段5像素混合与输出——最终实现抠图换背景这是抠图的最后一步通过GLSL内置的mix函数根据计算出的blendValue权重平滑混合绿幕图和背景图的当前像素颜色最终赋值给OpenGL内置的输出变量gl_FragColor作为当前像素的最终显示颜色。gl_FragColor mix(textureColor, textureColor2, blendValue);mix(a, b, t)是GLSL的线性混合函数核心公式为mix_result a * (1 - t) b * t结合抠图的权重逻辑最终效果为当blendValue1.0纯目标颜色mix_result textureColor*0 textureColor2*1 textureColor2→ 完全替换为背景图当blendValue0.0非目标颜色mix_result textureColor*1 textureColor2*0 textureColor→ 完全保留绿幕图当blendValue0.5边缘像素mix_result textureColor*0.5 textureColor2*0.5→ 绿幕图和背景图各占50%实现自然的边缘过渡。最终所有像素执行完上述逻辑后绿幕图中的目标颜色区域会被精准抠除并替换为背景图非目标颜色区域则完整保留实现完美的抠图换背景效果。核心参数的调优建议在实际使用中通过Java层调整thresholdSensitivity和smoothing两个参数能适配不同的绿幕/蓝幕素材达到最佳抠图效果通用调优建议如下阈值灵敏度thresholdSensitivity初始值设为0.4若抠图不彻底目标颜色没抠干净适当增大如0.5若出现误抠非目标颜色被替换适当减小如0.3平滑度smoothing初始值设为0.1若抠图边缘有锯齿、生硬适当增大如0.2若边缘过度模糊、丢失细节适当减小如0.05目标颜色colorToReplace默认是绿色(0.0,1.0,0.0)若为蓝幕抠图改为蓝色(0.0,0.0,1.0)即可也可根据实际素材的颜色微调如偏黄的绿幕适当降低绿色分量、提高红色分量。整体工作流程总结该色度键混合片段着色器的完整抠图流程可概括为6个核心步骤每个像素独立执行GPU并行计算保证实时性采样从绿幕图和背景图中采样当前像素的RGBA颜色转换将目标替换颜色和绿幕图当前像素颜色从RGB转换为YCrCb色彩空间分离亮度和色度提取忽略亮度分量Y仅保留两个颜色的色度分量Cr和Cb测距计算当前像素与目标颜色的Cr/Cb欧氏距离判断颜色相似度加权通过smoothstep和反向计算将距离转换为0~1的平滑混合权重blendValue混合通过mix函数按权重混合两个像素颜色输出最终抠图后的像素颜色。该实现是移动端工业级绿幕抠图的经典方案兼顾了抠图精度和边缘自然度且基于GPU并行计算能实现图片/视频的实时抠图广泛应用于直播、短视频、图片编辑等移动端场景。实际使用步骤Android 项目1. 依赖引入首先在项目build.gradle引入 GPUImage 库dependencies { implementation jp.co.cyberagent.android:gpuimage:2.1.0 }2. 核心使用代码// 1. 初始化GPUImage关联显示控件GPUImagegpuImagenewGPUImage(context);GLSurfaceViewglSurfaceViewfindViewById(R.id.gl_surface_view);gpuImage.setGLSurfaceView(glSurfaceView);// 2. 加载输入图片绿幕图待抠图、背景图替换图BitmapgreenScreenBitmapBitmapFactory.decodeResource(getResources(),R.drawable.green_screen_photo);BitmapbackgroundBitmapBitmapFactory.decodeResource(getResources(),R.drawable.bg_photo);// 3. 创建抠图滤镜实例GPUImageChromaKeyBlendFilterchromaKeyFilternewGPUImageChromaKeyBlendFilter();// 4. 自定义参数可选默认值也可直接使用chromaKeyFilter.setThresholdSensitivity(0.3f);// 更严格的颜色匹配chromaKeyFilter.setSmoothing(0.2f);// 更自然的边缘过渡chromaKeyFilter.setColorToReplace(0.0f,1.0f,0.0f);// 确认抠除绿色// 5. 设置滤镜与输入图片gpuImage.setFilter(chromaKeyFilter);gpuImage.setImage(greenScreenBitmap);// 设置主纹理绿幕图((GPUImageTwoInputFilter)gpuImage.getFilter()).setSecondInput(backgroundBitmap);// 设置次纹理背景图// 6. 渲染并显示gpuImage.requestRender();3. 注意事项图片尺寸两张输入图片建议尺寸一致避免拉伸变形参数调优阈值过小会导致抠图不彻底过大则会误替换非目标颜色平滑度过大则边缘模糊过小则边缘有锯齿性能优化避免频繁修改参数每次修改都会触发 GPU 统一变量更新建议一次性配置完成。总结GPUImageChromaKeyBlendFilter是 Android 平台高性能绿幕抠图的经典实现核心是通过 OpenGL ES 2.0 将抠图逻辑转移到 GPU 执行相比 CPU 抠图效率提升数倍。其核心设计思路是“色度空间分离平滑混合”既保证了抠图精度又兼顾了边缘自然度是移动端音视频、图片编辑场景中绿幕抠图的首选方案。