核心内容摘要
《心弦共鸣:当师徒情谊绽放出别样的花火》
GPUImageChromaKeyBlendFilter 代ç �全解æ��本文将é€�行解æ��GPUImageChromaKeyBlendFilter.java代ç �涵盖代ç �注释ã€�模å�—功能ã€�æ ¸å¿ƒé€»è¾‘å�Šå®�际使用方å¼�该类是 Android å¹³å�°åŸºäº� OpenGL ES
0 å®�ç�°çš„色度键混å�ˆæ»¤é•œç»¿å¹•æŠ å›¾å±�äº� GPUImage å¼€æº�框æ�¶çš„æ ¸å¿ƒæ»¤é•œä¹‹ä¸€ã€‚完整代ç �é€�行注释版/* * Copyright (C) 2018 CyberAgent, Inc. * 版æ�ƒå£°æ˜�å½’å±�CyberAgentå…¬å�¸2018å¹´å�‘布 * * Licensed under the Apache License, Version
0 (the License); * ���议基�Apache
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-
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
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, textureCoordinate
;\n// é‡‡æ ·èƒŒæ™¯å›¾å½“å‰�åƒ�ç´ çš„RGBA颜色 \n// è®¡ç®—ç›®æ ‡æ›¿æ�¢é¢œè‰²çš„YCrCb分é‡�äº®åº¦è‰²åº¦æŠ å›¾æ ¸å¿ƒå�ªæ¯”较色度é�¿å…�亮度干扰 float maskY
2989 * colorToReplace.r
5866 * colorToReplace.g
1145 * colorToReplace.b;\n// ç›®æ ‡é¢œè‰²çš„äº®åº¦YITU-R BT.601æ ‡å‡†è½¬æ�¢å…¬å¼� float maskCr
7132 * (colorToReplace.r - maskY);\n// ç›®æ ‡é¢œè‰²çš„çº¢è‰²å·®Cr float maskCb
5647 * (colorToReplace.b - maskY);\n// ç›®æ ‡é¢œè‰²çš„è“�色差Cb \n// 计算绿幕图当å‰�åƒ�ç´ çš„YCrCb分é‡� float Y
2989 * textureColor.r
5866 * textureColor.g
1145 * textureColor.b;\n// 当å‰�åƒ�ç´ çš„äº®åº¦Y float Cr
7132 * (textureColor.r - Y);\n// 当å‰�åƒ�ç´ çš„çº¢è‰²å·®Cr float Cb
5647 * (textureColor.b - Y);\n// 当å‰�åƒ�ç´ çš„è“�色差Cb \n// 计算混å�ˆæ�ƒé‡�判æ–当å‰�åƒ�ç´ æ˜¯å�¦å±�äº�ç›®æ ‡é¢œè‰²å†³å®šæ›¿æ�¢ç¨‹åº¦ float blendValue
0 - smoothstep(thresholdSensitivity, thresholdSensitivity smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));\n/* * æ ¸å¿ƒé€»è¾‘æ‹†è§£ *
distance计算当å‰�åƒ�ç´ ä¸�ç›®æ ‡é¢œè‰²çš„Cr/Cb色度欧æ°�è·�离 *
smoothstepå°†è·�ç¦»æ˜ å°„åˆ°[阈值, 阈值平滑度]区间生æˆ�0~1的平滑值 *
3.
0 - 结æ�œè·�离越近越æ�¥è¿‘ç›®æ ‡é¢œè‰²blendValueè¶Šæ�¥è¿‘1完全替æ�¢ä¸ºèƒŒæ™¯å›¾ */ gl_FragColor mix(textureColor, textureColor2, blendValue);\n// 按æ�ƒé‡�æ··å�ˆåƒ�ç´ mix(a,b,t)t0å�–a绿幕图t1å�–b背景图 };// GLSL统一å�˜é‡�çš„ä½�ç½®å�¥æŸ„用äº�Java层å�‘GPUä¼ é€’å�‚æ•°privateintthresholdSensitivityLocation;// 阈值ç�µæ•�度å�˜é‡�å�¥æŸ„privateintsmoothingLocation;// 平滑度å�˜é‡�å�¥æŸ„privateintcolorToReplaceLocation;// ç›®æ ‡é¢œè‰²å�˜é‡�å�¥æŸ„// 滤镜默认å�‚æ•°privatefloatthresholdSensitivity
4f;// 阈值��度默认值privatefloatsmoothing
1f;// 平滑度默认值privatefloat[]colorToReplacenewfloat[]{
0f,
0f,
0f};// 默认替æ�¢ç»¿è‰²RGB归一化// æ— å�‚æ�„é€ æ–¹æ³•publicGPUImageChromaKeyBlendFilter(){super(CHROMA_KEY_BLEND_FRAGMENT_SHADER);// 调用父类æ�„é€ æ–¹æ³•ä¼ å…¥è‡ªå®šä¹‰ç‰‡æ®µç�€è‰²å™¨é¡¶ç‚¹ç�€è‰²å™¨å¤�用父类å�Œè¾“入纹ç�†å®�ç�°}OverridepublicvoidonInit(){// é‡�写父类åˆ�始化方法GL程åº�创建å��调用super.onInit();// è�·å�–GLSL统一å�˜é‡�çš„ä½�ç½®å�¥æŸ„通过å�˜é‡�å��å…³è�”thresholdSensitivityLocationGLES
glGetUniformLocation(getProgram(),thresholdSensitivity);smoothingLocationGLES
glGetUniformLocation(getProgram(),smoothing);colorToReplaceLocationGLES
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
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
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.
. * The default is green: (
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}}代ç �模å�—功能解æ��
基础层版���赖导入版�声��循 Apache
0 许å�¯è¯�æ˜�确使用æ�ƒé™�包导入jp.co.cyberagent.android.gpuimage.filter是 GPUImage 框æ�¶çš„æ»¤é•œæ ¸å¿ƒåŒ…android.opengl.GLES20是 Android æ“�作 OpenGL ES
0 çš„æ ¸å¿ƒ API用äº�ä¸� GPU 通信。
æ ¸å¿ƒå±‚ç±»ä¸�继承关系类å��GPUImageChromaKeyBlendFilter色度键混å�ˆæ»¤é•œç»§æ‰¿GPUImageTwoInputFilterå�Œè¾“入纹ç�†æ»¤é•œåŸºç±»æ”¯æŒ�å�Œæ—¶å¤„ç�†â€œç»¿å¹•å›¾å¾…æŠ å›¾â€�和“背景图替æ�¢å›¾â€�ä¸¤å¼ è¾“å…¥å›¾ç‰‡æ˜¯å®�ç�°æŠ 图的基础。
æ ¸å¿ƒé€»è¾‘GLSL 片段ç�€è‰²å™¨ç‰‡æ®µç�€è‰²å™¨æ˜¯æ»¤é•œçš„æ ¸å¿ƒè¿�行在 GPU ä¸Šæ ¸å¿ƒé€»è¾‘å¦‚ä¸‹è‰²å½©ç©ºé—´è½¬æ�¢å°† RGB 转æ�¢ä¸º YCrCb亮度色度仅比较色度Cr/Cbé�¿å…�äº®åº¦å¹²æ‰°æ˜¯æŠ å›¾çš„å…³é”®ä¼˜åŒ–æ··å�ˆæ�ƒé‡�计算通过distance计算当å‰�åƒ�ç´ ä¸�ç›®æ ‡é¢œè‰²çš„è‰²åº¦è·�离结å�ˆsmoothstep生æˆ� 0~1 的混å�ˆæ�ƒé‡�åƒ�ç´ æ··å�ˆé€šè¿‡mix按æ�ƒé‡�æ··å�ˆä¸¤å¼ 图的åƒ�ç´ å®�ç�°â€œç›®æ ‡é¢œè‰²åŒºåŸŸæ›¿æ�¢ä¸ºèƒŒæ™¯å›¾â€�。
交互层æˆ�员å�˜é‡�ä¸�生命周期统一å�˜é‡�å�¥æŸ„thresholdSensitivityLocationç‰å�˜é‡�å˜å‚¨ GLSL 统一å�˜é‡�的索引是 Java 层å�‘ GPU ä¼ å�‚的“桥æ¢�â€�默认å�‚æ•°é¢„è®¾ç»¿è‰²ä¸ºæŠ é™¤é¢œè‰²ã€�阈值
4�平滑度
1适�常规绿幕场景生命周期方法onInit()GL 程�创建���统一���柄onInitialized()GL 程��始化完��设置默认�数到 GPU。
é…�置层Setter 方法æ��ä¾›ä¸‰ä¸ªæ ¸å¿ƒå�‚æ•°çš„é…�ç½®æ�¥å�£æ”¯æŒ�动æ€�è°ƒæ•´æŠ å›¾æ•ˆæ�œsetSmoothingè°ƒæ•´æŠ å›¾è¾¹ç¼˜çš„å¹³æ»‘åº¦å€¼è¶Šå¤§è¾¹ç¼˜è¿‡æ¸¡è¶Šè‡ªç„¶é�¿å…�锯齿setThresholdSensitivity调整颜色匹é…�çš„ä¸¥æ ¼ç¨‹åº¦å€¼è¶Šå°�仅“æ��æ�¥è¿‘ç›®æ ‡é¢œè‰²â€�çš„åƒ�ç´ ä¼šè¢«æ›¿æ�¢setColorToReplace支æŒ�è‡ªå®šä¹‰æŠ é™¤é¢œè‰²å¦‚è“�幕å�¯ä¼
0f,
0f,
0f。ç�€è‰²å™¨ç»¿å¹•æŠ å›¾å·¥ä½œå�Ÿç�†è¯¥ç�€è‰²å™¨çš„æ‰§è¡Œè§„则是为图åƒ�çš„æ¯�个åƒ�ç´ æ‰§è¡Œä¸€æ¬¡main函数通过「颜色识别→æ�ƒé‡�计算→åƒ�ç´ æ··å�ˆã€�çš„ä¸‰æ¥æ ¸å¿ƒé€»è¾‘完æˆ�æŠ å›¾å…¨ç¨‹åœ¨GPU上并行执行效ç�‡è¿œé«˜äº�CPUç«¯çš„æŠ å›¾è®¡ç®—é€‚å�ˆç§»åŠ¨ç«¯å›¾ç‰‡/视频的å®�æ—¶æŠ å›¾åœºæ™¯ã€‚ä¸‹é�¢å…ˆé™„上é€�行精准注释版代ç �å†�分阶段拆解其完整工作å�Ÿç�†å¹¶è§£æ��æ ¸å¿ƒå�‚数的作用。é€�行精准注释版代ç �// 声æ˜�浮点精度为高精度ä¿�è¯�颜色计算的准确性é�¿å…�æŠ å›¾å‡ºç�°è‰²å½©å¤±çœŸprecision highpfloat;// ä»�顶点ç�€è‰²å™¨ä¼ 递的纹ç�†å��æ ‡æ˜“å�˜å�˜é‡�ç»�å…‰æ …åŒ–æ�’值å��é€�åƒ�ç´ å”¯ä¸€// 对应第一个输入纹ç�†å¾…æŠ å›¾çš„ç»¿å¹•/è“�幕图inputImageTexturevarying highp vec2 textureCoordinate;// 对应第二个输入纹ç�†æ›¿æ�¢çš„背景图inputImageTexture2varying highp vec2 textureCoordinate2;// 统一å�˜é‡�颜色匹é…�阈值ç�µæ•�度Java层å�¯åЍæ€�调整æ�§åˆ¶æŠ 图的颜色匹é…�ä¸¥æ ¼ç¨‹åº¦uniformfloatthresholdSensitivity;// 统一å�˜é‡�边缘平滑度Java层å�¯åЍæ€�调整æ�§åˆ¶æŠ 图边缘的æ¸�å�˜è¿‡æ¸¡æ•ˆæ�œé�¿å…�锯齿uniformfloatsmoothing;// 统一å�˜é‡�需è¦�æŠ é™¤/替æ�¢çš„ç›®æ ‡é¢œè‰²RGB三维å�‘é‡�值为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,textureCoordinate
;// æ¥éª¤2å°†ã€Œç›®æ ‡æ›¿æ�¢é¢œè‰²ã€�ä»�RGB色彩空间转æ�¢ä¸ºYCrCb色彩空间// è®¡ç®—ç›®æ ‡é¢œè‰²çš„äº®åº¦åˆ†é‡�YITU-R BT.601æ ‡å‡†è½¬æ�¢å…¬å¼�æ•°å—图åƒ�通用floatmaskY
2989*colorToReplace.r
5866*colorToReplace.g
1145*colorToReplace.b;// è®¡ç®—ç›®æ ‡é¢œè‰²çš„çº¢è‰²å·®åˆ†é‡�Cr色度分é‡�å��æ˜ çº¢è‰²ä¸�亮度的差值floatmaskCr
7132*(colorToReplace.r-maskY);// è®¡ç®—ç›®æ ‡é¢œè‰²çš„è“�色差分é‡�Cb色度分é‡�å��æ˜ è“�色ä¸�亮度的差值floatmaskCb
5647*(colorToReplace.b-maskY);// æ¥éª¤3将「绿幕图当å‰�åƒ�ç´ é¢œè‰²ã€�也转æ�¢ä¸ºYCrCb色彩空间// 计算当å‰�åƒ�ç´ çš„äº®åº¦åˆ†é‡�YfloatY
2989*textureColor.r
5866*textureColor.g
1145*textureColor.b;// 计算当å‰�åƒ�ç´ çš„çº¢è‰²å·®åˆ†é‡�CrfloatCr
7132*(textureColor.r-Y);// 计算当å‰�åƒ�ç´ çš„è“�色差分é‡�CbfloatCb
5647*(textureColor.b-Y);// æ¥éª¤4计算混å�ˆæ�ƒé‡�blendValueæ ¸å¿ƒåˆ¤æ–当å‰�åƒ�ç´ æ˜¯å�¦ä¸ºç›®æ ‡é¢œè‰²å†³å®šæ›¿æ�¢ç¨‹åº¦floatblendValue
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颜色比较ã€�而è¦�先转æ�¢è‰²å½©ç©ºé—´çš„关键。
1 为什么è¦�转æ�¢ä¸ºYCrCbRGB色彩空间的红ã€�绿ã€�è“�三个分é‡�是相互关è�”的且å�—亮度影å“�æ��大。比如绿幕图ä¸å�Œä¸€ç»¿è‰²å› 光照ä¸�å�‡äº®éƒ¨/暗部RGB值会相差很大直æ�¥ç”¨RGB比较会导致“å�Œè‰²ä¸�å�Œå€¼â€�æŠ å›¾ä¸�ç²¾å‡†æ¯”å¦‚æš—éƒ¨çš„ç»¿è‰²æ²¡è¢«æŠ é™¤ã€‚è€ŒYCrCb色彩空间将颜色拆分为三个相互独立的分é‡�完ç¾�解决了亮度干扰的问题YLuminance亮度分é‡�å��æ˜ åƒ�ç´ çš„æ˜�暗程度CrChrominance Red红色差分é‡�å��æ˜ çº¢è‰²ä¸�亮度的差值色度分é‡�CbChrominance Blueè“�色差分é‡�å��æ˜ è“�色ä¸�亮度的差值色度分é‡�。其ä¸Crå’ŒCb是色度分é‡�仅代表颜色的“色相â€�ä¸�äº®åº¦æ— å…³â€”â€”ç»¿å¹•å›¾ä¸æ— 论绿色是亮是暗其Crå’ŒCb值基本ä¿�æŒ�ä¸�å�˜ã€‚å› æ¤é€šè¿‡æ¯”较Crå’ŒCb值æ�¥åˆ¤æ–是å�¦ä¸ºç›®æ ‡é¢œè‰²èƒ½å½»åº•é�¿å…�光照ä¸�å�‡å¯¼è‡´çš„æŠ 图ä¸�ç²¾å‡†é—®é¢˜è¿™æ˜¯å·¥ä¸šçº§ç»¿å¹•æŠ å›¾çš„æ ‡å‡†å�šæ³•ã€‚
2 转æ�¢å…¬å¼�说æ˜�代ç �ä¸ä½¿ç”¨çš„æ˜¯ITU-R BT.601æ ‡å‡†è½¬æ�¢å…¬å¼�这是数å—图åƒ�如JPG/PNG/视频ä¸RGB转YCrCb的通用公å¼�系数是ç»�è¿‡æ ‡å‡†åŒ–çš„ä¿�è¯�转æ�¢çš„准确性亮度YY
2989*R
5866*G
1145*B绿色对亮度的贡献最大红色次之�色最�符�人眼视觉特性红色差CrCr
7132*(R - Y)��红色�亮度的差值隔离亮度影��色差CbCb
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
0 - smoothstep(thresholdSensitivity, thresholdSensitivity smoothing, distance(...));å…ˆæ‹†è§£ä¸¤ä¸ªæ ¸å¿ƒGLSL内置函数å†�讲æ�ƒé‡�çš„å�«ä¹‰
1 smoothstep生æˆ�平滑的0~1过渡值smoothstep(min, max, x)是GLSL的平滑æ¥é•¿å‡½æ•°æ ¸å¿ƒä½œç”¨æ˜¯å°†å€¼xæ˜ å°„åˆ°[min, max]区间并生æˆ�0~1之间的平滑æ�’值规则如下若x ≤ minè¿”å›�
0若x ≥ max返�
0è‹¥min x maxè¿”å›�0~1之间的平滑值采用三次æ�’å€¼è¿‡æ¸¡æ›´è‡ªç„¶æ— ç”Ÿç¡¬çª�å�˜ã€‚
2
0 - å��å�‘计算匹é…�æŠ å›¾çš„æ�ƒé‡�逻辑结å�ˆsmoothstepå’Œå��å�‘计算blendValue的最终规则为色度è·�离情况smoothstepè¿”å›�值blendValueå€¼æŠ å›¾é€»è¾‘æ··å�ˆè§„则è·�离 ≤ 阈值æ��相似
0.
0
0完全替�为背景图mix�textureColor2�离 ≥ 阈值平滑度完全�相似
1.
0
0完全ä¿�留绿幕图mixå�–textureColor阈值 è·�离 阈值平滑度相似但é��çº¯ç›®æ ‡è‰²0~1的平滑值1~0的平滑值平滑混å�ˆç»¿å¹•图和背景图边缘过渡
3 ä¸¤ä¸ªæ ¸å¿ƒå�‚数的作用thresholdSensitivity/smoothing这两个Java层å�¯åЍæ€�调整的å�‚æ•°ç›´æ�¥æ�§åˆ¶æŠ 图的精度和边缘自然度也是该滤镜的å�¯é…�ç½®æ ¸å¿ƒthresholdSensitivity阈值ç�µæ•�度æ�§åˆ¶é¢œè‰²åŒ¹é…�çš„ä¸¥æ ¼ç¨‹åº¦ã€‚å€¼è¶Šå°�仅“色度è·�离æ��è¿‘æ��æ�¥è¿‘ç›®æ ‡é¢œè‰²â€�çš„åƒ�ç´ ä¼šè¢«åˆ¤å®šä¸ºæŠ é™¤åŒºåŸŸæŠ å›¾æ›´ç²¾å‡†å€¼è¶Šå¤§ä¼šå°†â€œç›¸ä¼¼åº¦ä¸€èˆ¬â€�çš„åƒ�ç´ ä¹Ÿåˆ¤å®šä¸ºæŠ é™¤åŒºåŸŸå�¯èƒ½å¯¼è‡´è¯¯æŠ é��ç›®æ ‡é¢œè‰²ã€‚smoothing平滑度æ�§åˆ¶æŠ 图边缘的过渡范围。值越大[阈值, 阈值平滑度]的区间越宽边缘的混å�ˆè¿‡æ¸¡è¶Šè‡ªç„¶é�¿å…�æŠ å›¾è¾¹ç¼˜å‡ºç�°é”¯é½¿ã€�生硬的æ–层值越å°�è¾¹ç¼˜è¿‡æ¸¡è¶Šçª„æŠ å›¾è¾¹ç¼˜è¶Šé”�利但容易出ç�°é”¯é½¿ã€‚举个例å�è‹¥thresholdSensitivity
4smoothing
1则色度�离在
0
4çš„åƒ�ç´ ä¼šå®Œå…¨æ›¿æ�¢ä¸ºèƒŒæ™¯
0.
4
5çš„åƒ�ç´ ä¼šå¹³æ»‘æ··å�ˆ
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结å�ˆæŠ 图的æ�ƒé‡�逻辑最终效æ�œä¸ºå½“blendValue
0çº¯ç›®æ ‡é¢œè‰²mix_result textureColor*0 textureColor2*1 textureColor2→ 完全替æ�¢ä¸ºèƒŒæ™¯å›¾å½“blendValue
0é��ç›®æ ‡é¢œè‰²mix_result textureColor*1 textureColor2*0 textureColor→ 完全ä¿�留绿幕图当blendValue
5边缘åƒ�ç´ mix_result textureColor*
5 textureColor2*
5→ 绿幕图和背景图å�„å� 50%å®�ç�°è‡ªç„¶çš„边缘过渡。最终所有åƒ�ç´ æ‰§è¡Œå®Œä¸Šè¿°é€»è¾‘å��绿幕图ä¸çš„ç›®æ ‡é¢œè‰²åŒºåŸŸä¼šè¢«ç²¾å‡†æŠ é™¤å¹¶æ›¿æ�¢ä¸ºèƒŒæ™¯å›¾é��ç›®æ ‡é¢œè‰²åŒºåŸŸåˆ™å®Œæ•´ä¿�ç•™å®�ç�°å®Œç¾�çš„æŠ å›¾æ�¢èƒŒæ™¯æ•ˆæ�œã€‚æ ¸å¿ƒå�‚数的调优建议在å®�际使用ä¸é€šè¿‡Java层调整thresholdSensitivityå’Œsmoothing两个å�‚数能适é…�ä¸�å�Œçš„绿幕/è“�å¹•ç´ æ��è¾¾åˆ°æœ€ä½³æŠ å›¾æ•ˆæ�œé€šç”¨è°ƒä¼˜å»ºè®®å¦‚下阈值ç�µæ•�度thresholdSensitivityåˆ�始值设为
4è‹¥æŠ å›¾ä¸�å½»åº•ç›®æ ‡é¢œè‰²æ²¡æŠ å¹²å‡€é€‚å½“å¢�大如
5若出ç�°è¯¯æŠ é��ç›®æ ‡é¢œè‰²è¢«æ›¿æ�¢é€‚当å‡�å°�如
3平滑度smoothing�始值设为
1è‹¥æŠ å›¾è¾¹ç¼˜æœ‰é”¯é½¿ã€�生硬适当å¢�大如
2若边缘过度模糊�丢失细节适当��如
05ç›®æ ‡é¢œè‰²colorToReplace默认是绿色(
0,
0,
0.
若为è“�å¹•æŠ å›¾æ”¹ä¸ºè“�色(
0,
0,
1.
å�³å�¯ä¹Ÿå�¯æ ¹æ�®å®�é™…ç´ æ��的颜色微调如å��黄的绿幕适当é™�ä½�绿色分é‡�ã€�æ��高红色分é‡�。整体工作æµ�程总结该色度键混å�ˆç‰‡æ®µç�€è‰²å™¨çš„å®Œæ•´æŠ å›¾æµ�程å�¯æ¦‚括为6ä¸ªæ ¸å¿ƒæ¥éª¤æ¯�个åƒ�ç´ ç‹¬ç«‹æ‰§è¡ŒGPU并行计算ä¿�è¯�å®�æ—¶æ€§é‡‡æ ·ä»�绿幕图和背景图ä¸é‡‡æ ·å½“å‰�åƒ�ç´ çš„RGBA颜色转æ�¢å°†ç›®æ ‡æ›¿æ�¢é¢œè‰²å’Œç»¿å¹•图当å‰�åƒ�ç´ é¢œè‰²ä»�RGB转æ�¢ä¸ºYCrCb色彩空间分离亮度和色度æ��å�–忽略亮度分é‡�Yä»…ä¿�留两个颜色的色度分é‡�Crå’ŒCb测è·�计算当å‰�åƒ�ç´ ä¸�ç›®æ ‡é¢œè‰²çš„Cr/Cb欧æ°�è·�离判æ–é¢œè‰²ç›¸ä¼¼åº¦åŠ æ�ƒé€šè¿‡smoothstepå’Œå��å�‘计算将è·�离转æ�¢ä¸º0~1的平滑混å�ˆæ�ƒé‡�blendValueæ··å�ˆé€šè¿‡mix函数按æ�ƒé‡�æ··å�ˆä¸¤ä¸ªåƒ�ç´ é¢œè‰²è¾“å‡ºæœ€ç»ˆæŠ å›¾å��çš„åƒ�ç´ é¢œè‰²ã€‚è¯¥å®�ç�°æ˜¯ç§»åŠ¨ç«¯å·¥ä¸šçº§ç»¿å¹•æŠ å›¾çš„ç»�å…¸æ–¹æ¡ˆå…¼é¡¾äº†æŠ å›¾ç²¾åº¦å’Œè¾¹ç¼˜è‡ªç„¶åº¦ä¸”åŸºäº�GPU并行计算能å®�ç�°å›¾ç‰‡/视频的å®�æ—¶æŠ å›¾å¹¿æ³›åº”ç”¨äº�ç›´æ’ã€�çŸè§†é¢‘ã€�图片编辑ç‰ç§»åŠ¨ç«¯åœºæ™¯ã€‚å®�际使用æ¥éª¤Android 项目
�赖引入首先在项目build.gradle引入 GPUImage 库dependencies { implementation jp.co.cyberagent.android:gpuimage:
2.