1.安装
1.1 Visual Studio配置OpenGL ES环境
1.1.1 下载OpenGL ES相关配置
1.下载《OpenGL ES3.0 Programming guide》示例代码并解压,如图:
https://codeload.github.com/danginsburg/opengles3-book/zip/master
-
下载mali opengl es模拟器(我下载的是64位的,在Visual Studio中也选择debug x64),并解压,如图:
https://developer.arm.com/downloads/-/opengl-es-emulator-downloads
-
然后用Cmake编译(Cmake下载地址:https://cmake.org/download/)。
安装之后,将Cmake安装目录的Bin路径添加到windows系统环境环境变量Path中。
然后打开Cmak-GUI.exe,编译源码。需要手动创建一个编译后保存二进制文件的目录(build)。依次进行如下步骤:
再点击“Configure”后,出现以下窗口,来选取编译所用的生成器。我安装的是VS 2019,电脑是64位操作系统(对应mali opengl es模拟器也是64位)。
然后点击“Finish”。然后会出现红色字体提示。将“EFL_LIBRARY”“OPENGLES3_LIBRARY”分别选择D:/OpenGlProject/OpenGL_ES_All/Mali_OpenGL_ES_Emulator64bit/libEGL.lib、D:/OpenGlProject/OpenGL_ES_All/Mali_OpenGL_ES_Emulator64bit/libGLESv2.lib。如图对应地址值:
然后点击“Generate”生成所需文件。此时,可以之间点击“Open Project”按钮,直接用VS2019打开生成的工程(也可以在刚刚创建的build文件夹中打开GLFW.sln)。
1.1.2 配置visual studio
1. 打开工程后,首先右击“Hello_Triangle”子工程,先以“Hello_Triangle”子工程为启动项。
-
进入工程属性页面,配置include包含目录、lib库目录:
-
然后配置好VS,选择“Debug”“x64”。点击“本地Windows调试器”试运行一下(会提示缺少mali opengl es模拟器动态库的错误):
-
将mali opengl es模拟器文件夹中的动态库等,复制到子工程“Hello_Triangle”的Debug目录下:
复制到:
-
最后,点击“本地Windows调试器”运行一下,无误的话便会显示一个三角形:
(补充)OpenGL ES 基础
-
YUV渲染
原理:也叫做 YCbCr,其中 “Y” 表示明亮度(Luminance),“U” 和 “V” 分别表示色度(Chrominance)和浓度(Chroma)。
YUV 编码模型的图像数据一般不能直接用于显示,还需要将其转换为 RGB(RGBA) 编码模型,才能够正常显示图像。
在GLSL中计算如下,构建mat3矩阵时按照列构建:
vec4 YUV2RGB(vec2 Coord) { `
**vec3** rgb = **mat3**(1.0, 1.0, 1.0,
0, -0.344, 1.770,
1.403, -0.714, 0)
\* **vec3**(texture(y\_samp, Coord).r,
texture(u\_samp, Coord).r - 0.5,
texture(v\_samp, Coord).r - 0.5);
`return vec4(rgb, 1.0); -
FBO离屏渲染
使用 FBO 可以让渲染操作不用再渲染到屏幕上,而是渲染到离屏 Buffer 中,然后可以使用 glReadPixels 或者 HardwareBuffer 将渲染后的图像数据读出来,从而实现在后台利用 GPU 完成对图像的处理。
创建并初始化 FBO 的步骤:
// 创建一个 2D 纹理用于连接 FBO 的颜色附着
glGenTextures(1, &m_FboTextureId);
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
// 创建 FBO
glGenFramebuffers(1, &m_FboId);
// 绑定 FBO
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
// 绑定 FBO 纹理
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
// 将纹理连接到 FBO 附着
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);
// 分配内存大小
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// 检查 FBO 的完整性状态
if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {
` `LOGCATE(“FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE”);
` `return false;
}
// 解绑纹理
glBindTexture(GL_TEXTURE_2D, GL_NONE);
// 解绑 FBO
glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
2.OpenGL ES 使用场景
- 图片处理。比如图片色调转换、美颜等。视频滤镜、音频滤镜.
- 摄像头预览效果处理。比如美颜相机、恶搞相机等。
- 视频处理。摄像头预览效果处理可以,这个自然也不在话下了。
- 3D 游戏。比如神庙逃亡、都市赛车等。
2.1 OpenGL ES人脸检测
人脸检测主要基于预训练的“face_detection_yunet”深度学习人脸检测模型。
结合OpenCV库对视频帧提取,对人脸关键点、文字、贴图等可视化图像处理。
最终基于OpenGL将数据帧在window渲染显示。
1.首先配置环境:
opencv_world455d.lib
glfw/glfw3dll.lib
opengl32.lib
glew\lib\Release\x64\glew32.lib
assimp-vc142-mtd.lib
2.初始化人脸检测模型:
// *模型参数*
String modelPath = “resources/face_detection_yunet_2022mar.onnx”;
int backendId = 0;
int targetId = 0;
float scoreThreshold = 0.9;
` `float nmsThreshold = 0.3;
` `int topK = 5;
` `// Initialize FaceDetectorYN
` `Ptr
3.可视化,将帽子拼接到原图:
static Mat visualize(Mat input, Mat faces, Mat hat, Mat hat_alpha, bool print_flag = false, double fps = 30, int thickness = 2)
{
Mat output = input.clone();
if (fps > 0)
{
putText(output, format(“FPS: %.2f”, fps), Point2i(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
}
for (int i = 0; i < faces.rows; i++)
{
if (print_flag)
{
cout « “Face “ « i
« ”, top-left coordinates: (“ « faces.at
« “box width: “ « faces.at
« “score: “ « faces.at
}
// Draw bounding box
rectangle(output, Rect2i(int(faces.at
float bbox_wid = faces.at
float scale = (bbox_wid / hat.cols) * 2.0;
Mat iHat, iHat_alpha;
resize(hat, iHat, Size(), scale, scale);
resize(hat_alpha, iHat_alpha, Size(), scale, scale);
// *以bbox上边为帽子的下边缘*
int bx = faces.at
int by = faces.at
int offset = (int)(iHat.cols / 2 - faces.at
int sx = bx - offset; // *帽子左*
int sy = max(0, by - iHat.rows) + (int)(iHat.rows * 0.45); // *帽子上*
iHat.copyTo(output(Rect(sx, sy, iHat.cols, iHat.rows)), iHat_alpha); // *(帽子左上角,帽子大小)*
// std::cout « faces.at
// Draw landmarks
circle(output, Point2i(int(faces.at
circle(output, Point2i(int(faces.at
circle(output, Point2i(int(faces.at
circle(output, Point2i(int(faces.at
circle(output, Point2i(int(faces.at
// Put score
putText(output, format(“%.4f”, faces.at
}
return output;
}
4.最后,用OpenGL渲染显示窗口:
// *人脸检测(in:frame ; out:frameOut)*
frame = detect_face(frame);
// *激活纹理*
CreateTexture(frame.cols, frame.rows, frame.data, 0);
// *绘制一个四边形*
glm::mat4 model = glm::mat4(1.0f);
// model = glm::translate(model, glm::vec3(0.7f, 3.8f, 2.0f));
// model = glm::rotate(model, glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));
// model = glm::scale(model, glm::vec3(0.1f)); // Make it a smaller cube
ourShader.use();
glBindVertexArray(planeVAO);
ourShader.setMat4(“model”, model);
glDrawArrays(GL_QUADS, 0, 4);
glBindVertexArray(0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
glfwSwapBuffers(window);
glfwPollEvents();
waitKey(1000 / fps);
5.人脸检测与帽子贴图显示:
1280*720
左上角显示视频帧率; 显示人脸关键点(双眼、双嘴角、鼻子); 将帽子贴图上去(在OpenCV中处理)
顶点着色器:
由于加载的原始纹理图片y轴翻转,所以我直接在顶点着色器内部进行一次y轴翻转,将图片摆正。
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 model;
void main()
{
` `//TexCoord = aTexCoord;
` `TexCoord = vec2(aTexCoord.x, 1.0f-aTexCoord.y); //*1.图片翻转(修改纹理)*
` `vec4 pos = vec4(aPos,0.0f, 1.0f);
` `gl_Position = model * pos;
}
或者修改顶点:
void main()
{
` `TexCoord = aTexCoord;
` `vec4 pos = vec4(aPos.x, -aPos.y, 0.0f, 1.0f); //*2.图片翻转(修改顶点)*
` `gl_Position = model * pos;
}
片段着色器:
#version 330 core
varying vec2 TexCoord;
uniform sampler2D _texture;
void main()
{
` `vec4 color = texture2D(_texture,TexCoord);
` `gl_FragColor = color;
}
6.灰度显示、反相、卷积边缘检测:
顶点着色器与上面一样不变。
片段着色器:
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D _texture;
void main()
{
` `// 1.绿幕背景换成蓝色背景
` `vec4 texColor = texture2D(_texture, TexCoord);
` `bool bkg_R = texColor.x > 10.0/255 && texColor.x < 105.0/255;
` `bool bkg_G = texColor.y > 90.0/255 && texColor.y <= 255.0/255;
` `bool bkg_B = texColor.z > 35.0/255 && texColor.z < 160.0/255;
` `if (bkg_R && bkg_G && bkg_B)
` `texColor = vec4(0.0f, 0.0f, 0.5f, 1.0f);
` `// 2.反相(颜色反转)
` `vec4 texColor = vec4(1.0f - texture(_texture, TexCoord));
` `texColor = vec4(texColor.r, texColor.g, texColor.b, 1.0f);
` `// 3.转灰度图
` `vec4 texColor = texture(_texture, TexCoord);
` `float average = (texColor.r + texColor.g + texColor.b) / 3.0;
` `texColor = vec4(average,average,average,1.0);
` `// 4.***** 卷积核 ******
` `const float offset = 1.0/800.0; //计算卷积的偏移数组
` `vec2 OffSet[9] = vec2[](
` `vec2(-offset, offset), // 左上
` `vec2( 0.0f, offset), // 正上
` `vec2( offset, offset), // 右上
` `vec2(-offset, 0.0f), // 左
` `vec2( 0.0f, 0.0f), // 中
` `vec2( offset, 0.0f), // 右
` `vec2(-offset, -offset), // 左下
` `vec2( 0.0f, -offset), // 正下
` `vec2( offset, -offset) // 右下
` `);
` `float kernel[9] = float[]( //锐化的卷积核
` `// -1, -1, -1,
` `// -1, 9, -1,
` `// -1, -1, -1
` `1, 0, -1, //垂直边缘检测
` `1, 0, -1,
` `1, 0, -1
` `);
` `vec4 sampleTex[9];
` `for(int i=0;i<9;i++)
` `{
` `sampleTex[i] = vec4(texture2D(_texture, TexCoord.st+OffSet[i])); //获取像素点周围共9个像素
` `}
` `vec4 col = vec4(0.0f);
` `for(int i=0;i<9;i++)
` `{
` `col += sampleTex[i]*kernel[i];
` `}
` `vec4 texColor = vec4(col.r, col.g, col.b, 1.0f);
` `FragColor = texColor;
}
绿幕背景更换效果:
反相(颜色反转)效果:
转灰度图效果:
(卷积核)锐化效果:
(卷积核)垂直边缘检测:
(卷积核)水平边缘检测:
2.2 OpenGL ES滤镜效果
在OpenGL中颜色是用向量vec4中RGBA四个分量表示(取值范围0.0f-1.0f)。
###
2.2.1基本色调处理
- 冷色调:单一增加B通道值;
- 暖色调:增加R、G通道值。
冷色调片段着色器:
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D _texture;
void main()
{
` `vec4 texColor = texture2D(_texture, TexCoord);
` `texColor = texColor + vec4(0.0f, 0.0f, 0.2f, 0.0f); //暖色调(增加R、G通道值)
` `FragColor = texColor;
}
暖色调片段着色器:
` `texColor = texColor + vec4(0.2f, 0.2f, 0.0f, 0.0f); //暖色调(增加R、G通道值)
(冷色调,暖色调)
熔铸片段着色器:
vec4 mask = texture2D(_texture, TexCoord);
float r = mask.r * 0.5 / (mask.g + mask.b + 0.01);
float g = mask.r * 0.5 / (mask.r + mask.b + 0.01);
float b = mask.r * 0.5 / (mask.r + mask.g + 0.01);
vec4 texColor = vec4(r, g, b, 1.0);
灰度处理:
有5中方法来实现灰度滤镜的算法(前三种方法是利用权重来实现的):
- 浮点算法: Gray = R * 0.3 + G * 0.59 + B * 0.11 (根据对应纹素的颜色值调整RGB的比例)
- 平均值法: Gray = (R + G + B) / 3; (获取到对应纹素的RGB平均值,填充到三个通道上面)
- 仅取绿色: Gray = G (一个颜色填充三个通道)
(浮点计算,平均值法)
(仅取绿色)
浮雕:
浮雕就是检测边缘信息,然后转灰度图,然后提亮。
浮雕片段着色器:
const highp vec3 gray = vec3(0.2125, 0.7154, 0.0721);
const vec4 bgColor = vec4(0.5, 0.5, 0.5, 1.0);
const vec2 iResolution = vec2(100.0, 100.0);
vec2 uv = TexCoord;
vec2 preUV = vec2(uv.x-0.5/iResolution.x, uv.y-0.5/iResolution.y);
vec4 currentMask = texture2D(_texture, TexCoord);
vec4 preMask = texture2D(_texture, preUV);
vec4 delColor = currentMask - preMask; // 两个图层相减得到边缘信息
float luminance = dot(delColor.rgb, gray); //转灰度(三通道浮点计算法)
vec4 texColor = vec4(vec3(luminance), 0.0) + bgColor;// 边缘检测的灰度图+背景提亮
FragColor = texColor;
(边缘 -> 灰度 -> 提亮)
###
2.2.2模糊处理
均值模糊:
多个方向纹理偏移叠加。
通过多个纹理叠加,每个纹理uv偏移量设置不同达到一点重影效果来实现模糊。
一般偏移不大于0.01,太大就是重影,不像模糊了。
vec4 texColor = texture2D(_texture, TexCoord);
float dis = 0.005; //距离越大越模糊
texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y-dis));
texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y+dis));
texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y-dis));
texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y+dis));
texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y-dis));
texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y+dis));
texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y-dis));
texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y+dis));
texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y-dis));
texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y+dis));
texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y-dis));
texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y+dis));
texColor/=13.0; //周边13个颜色相加,然后取平均,作为这个点的颜色
高斯模糊(低通):
是一种典型的模糊方法,一般用于降噪、模糊处理。
类似半透明玻璃的效果。数字信号角度就是过滤高频,保留低频(低通滤波器)。
用到的高斯核,对应于2D高斯曲线值(正态分布),高斯核为:
越是接近中心点取值就越大。
模糊的本质就是让当前像素值和周围像素值接近,各个像素和周围像素值同时作用。
3*3高斯卷积核: 1/16∗121242121
3x3高斯核片段着色器:
const int KernelSize = 9; // 3x3=9(2)高斯模糊
const float fStep = 0.003; //3x3的卷积核设置太大时,会像重影,效果不如5x5的卷积核
vec2 uv = TexCoord;
//用来存储3x3的卷积矩阵
float Kernel[KernelSize] = float[](
` `1.0, 2.0, 1.0, //3X3卷积核
` `2.0, 4.0, 2.0,
` `1.0, 2.0, 1.0
);
vec2 Offset[KernelSize] = vec2[]( //像素点偏移位置
` `vec2(-fStep,-fStep), vec2(0.0,-fStep), vec2(fStep,-fStep),
` `vec2(-fStep,0.0), vec2(0.0,0.0), vec2(fStep,0.0),
` `vec2(-fStep, fStep), vec2(0.0, fStep), vec2(fStep, fStep)
);
vec4 sum = vec4(0.0);
for (int i = 0; i < KernelSize; i++)
{
` `vec4 tmp = texture2D(_texture, uv + Offset[i]);
` `sum += tmp * Kernel[i]/16.0;
}
vec4 texColor = sum;
(3*3高斯核步长0.003, 步长0.005)步长太大时,会出现重影,3*3高斯核效果不太好。
5*5高斯卷积核:
5x5高斯核片段着色器:
const int KernelSize = 25; // 5x5=25(2)高斯模糊
const float fStep = 0.003; //3x3的卷积核设置太大时,会像重影,效果不如5x5的卷积核
vec2 uv = TexCoord;
//用来存储3x3的卷积矩阵
float Kernel[KernelSize] = float[](
` `1.0, 4.0, 6.0, 4.0, 1.0, //5X5卷积核
` `4.0, 16.0, 24.0, 16.0, 4.0,
` `6.0, 24.0, 36.0, 24.0, 6.0,
` `4.0, 16.0, 24.0, 16.0, 4.0,
` `1.0, 4.0, 6.0, 4.0, 1.0
);
vec2 Offset[KernelSize] = vec2[]( //像素点偏移位置
ec2(-2.0*fStep,-2.0*fStep), vec2(-fStep,-2.0*fStep), vec2(0.0,-2.0*fStep), vec2(fStep,-2.0*fStep), ec2(2.0*fStep,-2.0*fStep),
` `vec2(-2.0*fStep,-1.0*fStep), vec2(-fStep,-1.0*fStep), vec2(0.0,-1.0*fStep), ec2(fStep,-1.0*fStep), vec2(2.0*fStep,-1.0*fStep),
` `vec2(-2.0*fStep,0.0), vec2(-fStep,0.0), vec2(0.0,0.0), ec2(fStep,0.0), vec2(2.0*fStep,0.0),
` `vec2(-2.0*fStep,1.0*fStep), vec2(-fStep,1.0*fStep), vec2(0.0,1.0*fStep), ec2(fStep,1.0*fStep), vec2(2.0*fStep,1.0*fStep),
` `vec2(-2.0*fStep,2.0*fStep), vec2(-fStep,2.0*fStep), vec2(0.0,2.0*fStep), ec2(fStep,2.0*fStep), vec2(2.0*fStep,2.0*fStep)
);
vec4 sum = vec4(0.0);
for (int i = 0; i < KernelSize; i++)
{
` `vec4 tmp = texture2D(_texture, uv + Offset[i]);
` `sum += tmp * Kernel[i]/256.0;
}
vec4 texColor = sum;
(5*5高斯核步长0.003, 步长0.005)步长太大时,5*5高斯核效果比3*3效果好。
2.2.3马赛克
马赛克处理我分为正方形、六边形、三角形的马赛克。
正方形马赛克:
把图片的一个相当大小的区域用同一个颜色值来表示。
滤镜算法主要有以下几步:
- 根据纹理坐标计算实际图像中的位置,相当于将纹理颜色区放大;
- 计算出一个小马赛克的坐标,即找到马赛克提取颜色值的像素点;
- 将马赛克坐标换算回纹理坐标,即将纹理颜色区缩小。
片段着色器:
const vec2 TexSize = vec2(600.0, 600.0); // 图片size(1)正方形马赛克
const vec2 mosaicSize = vec2(4.0, 4.0);
vec2 intXY = vec2(TexCoord.x*TexSize.x, TexCoord.y*TexSize.y);//计算图像的实际位置
vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);
vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);//换算回纹理坐标(0.0-1.0)
vec4 texColor = texture2D(_texture, UVMosaic);
六边形马赛克:
将一张图片,分割成由六边形组成,再取每个六边形的重点画出一个个的矩形,根据矩形的奇偶排列情况求出对应的2个中心点,并计算纹理坐标与两个中心点的距离,根据距离判断,采取就近原则,当前的六边形就采用近的中心点的颜色值。
首先设置矩形的长宽比例:
TB长:3 TR宽:√3
根据纹理坐标,计算矩阵坐标:
wx: 3*len wy: √3*len
根据行列奇偶情况,获取六边形中心点坐标:
然后计算矩形四个顶点坐标(六边形中心点坐标):1.5改为3
然后计算当前片元距两个中心点的距离:
s1 = √((v1.x-x)² + (v1.y-y)²)
s2 = √((v2.x-x)² + (v2.y-y)²)
片段着色器:
float length = 0.015; //马赛克大小 (2)六边形马赛克
float TR = 0.866025; // √3/2
float x = TexCoord.x;
float y = TexCoord.y;
int wx = int(x / 1.5 / length);
int wy = int(y / TR / length);
vec2 v1, v2, vn;
if (wx/2 * 2 == wx) {
` `if (wy/2 * 2 == wy) {
` `//(0,0),(1,1)
` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
` `} else {
` `//(0,1),(1,0)
` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
` `}
}else {
` `if (wy/2 * 2 == wy) {
` `//(0,1),(1,0)
` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));
` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));
` `} else {
` `//(0,0),(1,1)
` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));
` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));
` `}
}
float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));
float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));
if (s1 < s2) {
` `vn = v1;
} else {
` `vn = v2;
}
vec4 texColor = texture2D(_texture, vn);
三角形马赛克:
是基于六边形,将六边形6等分,得到6个正三角形。然后求每个三角形中心点,根据夹角判断属于哪个三角形,就将该三角形中心点颜色作为当前三角形颜色。
首先求夹角:
计算6个三角形中心点:
根据夹角判断属于哪个三角形:
顶点着色器部分:(省略六边形部分)
vec4 mid = texture2D(_texture, vn);
float a = atan((x - vn.x)/(y - vn.y)); //计算夹角
vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);
vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0);
vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);
vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);
if (a >= PI6 && a < PI6 * 3.0) {
` `vn = area1;
} else if (a >= PI6 * 3.0 && a < PI6 * 5.0) {
` `vn = area2;
} else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0) | (a < -PI6 * 5.0 && a > -PI6 * 6.0)) { |
` `vn = area3;
} else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0) {
` `vn = area4;
} else if(a <= -PI6 && a> -PI6 * 3.0) {
` `vn = area5;
} else if (a > -PI6 && a < PI6) {
` `vn = area6;
}
vec4 texColor;
if(gl_FragCoord.y > 250.0)
` `texColor = texture2D(_texture, vn);
else
` `texColor = texture2D(_texture, TexCoord);
###
2.2.4多分镜处理
多分镜的原理类似,具体参照下面的示意图。
二分屏:
片段着色器部分:
vec2 uv = TexCoord; //(1)左右二分镜(不能在原TexCoord上修改)
float x;
if(uv.x>=0.0 && uv.x<=0.5)
` `x = uv.x + 0.25;
else
` `x = uv.x - 0.25;
vec4 texColor = texture2D(_texture, vec2(x,uv.y));
vec2 uv = TexCoord; //(1)上下二分镜(不能在原TexCoord上修改)
float y;
if(uv.y>=0.0 && uv.y<=0.5)
` `y = uv.y + 0.25;
else
` `y = uv.y - 0.25;
vec4 texColor = texture2D(_texture, vec2(uv.x,y));
三分屏:
片段着色器部分:
vec2 uv = TexCoord; //(2)左右三分镜(不能在原TexCoord上修改)
float x;
if(uv.x>=0.0 && uv.x<=1.0/3.0)
` `x = uv.x + 1.0/3.0;
else if(uv.x>1.0/3.0 && uv.x<=2.0/3.0)
` `x = uv.x;
else
` `x = uv.x - 1.0/3.0;
vec4 texColor = texture2D(_texture, vec2(x,uv.y));
vec2 uv = TexCoord; //(2)上下三分镜(不能在原TexCoord上修改)
float y;
if(uv.y>=0.0 && uv.y<=1.0/3.0)
` `y = uv.y + 1.0/3.0;
else if(uv.y>1.0/3.0 && uv.y<=2.0/3.0)
` `y = uv.y;
else
` `y = uv.y - 1.0/3.0;
vec4 texColor = texture2D(_texture, vec2(uv.x,y));
四分屏:
片段着色器部分:
vec2 uv = TexCoord; //(3)四分镜(不能在原TexCoord上修改)
if(uv.x<=0.5)
` `uv.x = uv.x * 2.0;
else
` `uv.x = (uv.x - 0.5) * 2.0;
if(uv.y<=0.5)
` `uv.y = uv.y * 2.0;
else
` `uv.y = (uv.y - 0.5) * 2.0;
vec4 texColor = texture2D(_texture, uv);
2.2.5旋涡
在一定半径范围内,将采样点旋转一定角度,如果旋转时,旋转角随着半径距离递减,就会出现旋涡效果。
片段着色器部分:
const float PI = 3.14159265;
const float rotateRadian = PI / 3.0;
const float radiusRatio = 0.8; // 旋涡半径缩小率
const float center = 0.5; //纹理坐标中心(范围0.0-1.0)
const vec2 iResolution = vec2(100.0, 100.0);
float radius = min(iResolution.x, iResolution.y) * radiusRatio / 2.0; // 计算旋涡半径
vec2 uv = TexCoord; // (不能在原TexCoord上修改)
uv.x *= iResolution.x;
uv.y *= iResolution.y;
vec2 centerUV = iResolution.xy * center;
vec2 deltaUV = uv - centerUV;
float deltaR = length(deltaUV);
float beta = atan(deltaUV.y, deltaUV.x) + rotateRadian * 2.0 * (1.0-(deltaR / radius) * (deltaR / radius));//抛物线递减因子
vec2 dstUV = uv;
if (deltaR <= radius)
{
` `dstUV = centerUV + deltaR * vec2(cos(beta), sin(beta));
}
dstUV.x /= iResolution.x;
dstUV.y /= iResolution.y;
vec4 texColor = texture2D(_texture, dstUV);
2.2.6边缘检测
垂直边缘检测Sobel算子: 10−120−210−1
水平边缘检测Sobel算子: 121000−1−2−1
卷积核的竖直与水平边缘检测:
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D _texture;
void main()
{
` `// 4.***** 卷积核 ******
` `const float offset = 1.0/800.0; //计算卷积的偏移数组
` `vec2 OffSet[9] = vec2[](
` `vec2(-offset, offset), // 左上
` `vec2( 0.0f, offset), // 正上
` `vec2( offset, offset), // 右上
` `vec2(-offset, 0.0f), // 左
` `vec2( 0.0f, 0.0f), // 中
` `vec2( offset, 0.0f), // 右
` `vec2(-offset, -offset), // 左下
` `vec2( 0.0f, -offset), // 正下
` `vec2( offset, -offset) // 右下
` `);
` `float kernel[9] = float[]( //锐化的卷积核
` `// -1, -1, -1,
` `// -1, 9, -1,
` `// -1, -1, -1
` `1, 0, -1, //垂直边缘检测
` `2, 0, -2,
` `1, 0, -1
` `// 1, 2, 1, //水平边缘检测
` `// 0, 0, 0,
` `// -1,-2,-1
);
` `vec4 sampleTex[9];
` `for(int i=0;i<9;i++)
` `{
` `sampleTex[i] = vec4(texture2D(_texture, TexCoord.st+OffSet[i])); //获取像素点周围共9个像素
` `}
` `vec4 col = vec4(0.0f);
` `for(int i=0;i<9;i++)
` `{
` `col += sampleTex[i]*kernel[i];
` `}
` `vec4 texColor = vec4(col.r, col.g, col.b, 1.0f);
` `FragColor = texColor;
}
(竖直边缘, 水平边缘)
计算梯度边缘检测:
vec4 color = texture2D(_texture, TexCoord);
float len = length(color.rgb); // 纹理距原点距离
vec4 texColor = vec4(vec3(step(0.06, length(vec2(dFdx(len), dFdy(len))))), 1.0); //计算梯度(梯度>0.06赋值1)
2.2.7水波纹
片段着色器部分:
float stongth = 0.1;
vec2 uv = TexCoord.xy;
float waveX = sin((uv.y + iGlobalTime) * 10.0) * 0.5 * 0.05 * stongth;
float waveY = sin((uv.x + iGlobalTime) * 10.0) * 0.5 * 0.05 * stongth;
// vec4 texColor = texture2D(_texture, uv + vec2(waveX, 0.0)); //水平水波(不能在TexCoord上加减)
vec4 texColor = texture2D(_texture, uv + vec2(0.0, waveY)); //竖直水波
##
2.2 美颜效果
- 磨皮度、磨皮值:本质就是让像素点模糊,可以使用高斯模糊,但是可能导致边缘会不清晰,用双边滤波(Bilateral Filter) ,有针对性的模糊像素点,能保证边缘不被模糊.
- 美白:本质就是提高亮度.
- 红润:本质就是改变色调.
美颜参数值调整:
使用params向量接收美颜磨皮度、色调四个分量,用来调整片段着色器部分。
/**
` `* @brief *美颜*
` `* @param ourShader
` `* 磨皮度、磨皮值:本质就是让像素点模糊,可以使用高斯模糊,但是可能导致边缘会不清晰,用双边滤波(Bilateral Filter) ,有针对性的模糊像素点,能保证边缘不被模糊.
` `* 美白:本质就是提高亮度.
` `* 红润:本质就是改变色调.
` `*/
void Beauty(Shader *ourShader)
{
` `float beautyLevel = 1.2f; // *磨皮度*
` `float toneLevel = -0.5f; // *色调*
` `float brightness = 0.1f; // *亮度* 0.47f;
` `float texelWidthOffset = 2.0f;
` `float texelHeightOffset = 2.0f;
` `brightness = sin((float)glfwGetTime() * 3.0) * 0.5; //(-0.5,0.5)
` `// toneLevel = sin((float)glfwGetTime()*3.0)*1.0; //(-1.0,1.0)
` `// beautyLevel = (sin((float)glfwGetTime()*3.0)+1.0)*1.5; //(0.0,3.0)
` `cout«“beautyLevel:”«beautyLevel«”, brightness:”«brightness«”, toneLevel:”«toneLevel«endl;
` `glm::vec4 params = glm::vec4(0.4f + beautyLevel, 0.7f + beautyLevel, 0.4f + toneLevel, 0.4f + toneLevel);
` `ourShader->setVec4(“params”, params);
` `ourShader->setFloat(“brightness”, brightness);
` `ourShader->setFloat(“texelWidthOffset”, texelWidthOffset);
` `ourShader->setFloat(“texelHeightOffset”, texelHeightOffset);
}
片段着色器部分:
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D _texture;
const vec2 singleStepOffset = vec2(2.0f / 1280.0f, 2.0f / 720.0f);
// params:(0.4f+beautyLevel, 0.7f+beautyLevel, 0.4f+toneLevel,0.4f+toneLevel)
uniform vec4 params; // toneLevel:-0.5f(-5.0f,5.0f), beautyLevel:1.2f(0.0f,2.5f)
uniform float brightness; //美白亮度0.47f(0.0f,1.0f)
uniform float texelWidthOffset; // 2.0f(-10.0f,10.0f)
uniform float texelHeightOffset; // 2.0f(-10.0f,10.0f)
const vec3 W = vec3(0.299, 0.587, 0.114);
const mat3 saturateMatrix = mat3(
` `1.1102, -0.0598, -0.061,
` `-0.0774, 1.0826, -0.1186,
` `-0.0228, -0.0228, 1.1772);
vec2 blurCoordinates[24];
float hardLight(float color)
{
` `if (color <= 0.5)
` `color = color * color * 2.0;
` `else
` `color = 1.0 - ((1.0 - color) * (1.0 - color) * 2.0);
` `return color;
}
void main()
{
` `vec3 centralColor = texture2D(_texture, TexCoord).rgb;
` `vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
` `blurCoordinates[0] = TexCoord.xy + singleStepOffset * vec2(0.0, -10.0);
` `blurCoordinates[1] = TexCoord.xy + singleStepOffset * vec2(0.0, 10.0);
` `blurCoordinates[2] = TexCoord.xy + singleStepOffset * vec2(-10.0, 0.0);
` `blurCoordinates[3] = TexCoord.xy + singleStepOffset * vec2(10.0, 0.0);
` `blurCoordinates[4] = TexCoord.xy + singleStepOffset * vec2(5.0, -8.0);
` `blurCoordinates[5] = TexCoord.xy + singleStepOffset * vec2(5.0, 8.0);
` `blurCoordinates[6] = TexCoord.xy + singleStepOffset * vec2(-5.0, 8.0);
` `blurCoordinates[7] = TexCoord.xy + singleStepOffset * vec2(-5.0, -8.0);
` `blurCoordinates[8] = TexCoord.xy + singleStepOffset * vec2(8.0, -5.0);
` `blurCoordinates[9] = TexCoord.xy + singleStepOffset * vec2(8.0, 5.0);
` `blurCoordinates[10] = TexCoord.xy + singleStepOffset * vec2(-8.0, 5.0);
` `blurCoordinates[11] = TexCoord.xy + singleStepOffset * vec2(-8.0, -5.0);
` `blurCoordinates[12] = TexCoord.xy + singleStepOffset * vec2(0.0, -6.0);
` `blurCoordinates[13] = TexCoord.xy + singleStepOffset * vec2(0.0, 6.0);
` `blurCoordinates[14] = TexCoord.xy + singleStepOffset * vec2(6.0, 0.0);
` `blurCoordinates[15] = TexCoord.xy + singleStepOffset * vec2(-6.0, 0.0);
` `blurCoordinates[16] = TexCoord.xy + singleStepOffset * vec2(-4.0, -4.0);
` `blurCoordinates[17] = TexCoord.xy + singleStepOffset * vec2(-4.0, 4.0);
` `blurCoordinates[18] = TexCoord.xy + singleStepOffset * vec2(4.0, -4.0);
` `blurCoordinates[19] = TexCoord.xy + singleStepOffset * vec2(4.0, 4.0);
` `blurCoordinates[20] = TexCoord.xy + singleStepOffset * vec2(-2.0, -2.0);
` `blurCoordinates[21] = TexCoord.xy + singleStepOffset * vec2(-2.0, 2.0);
` `blurCoordinates[22] = TexCoord.xy + singleStepOffset * vec2(2.0, -2.0);
` `blurCoordinates[23] = TexCoord.xy + singleStepOffset * vec2(2.0, 2.0);
` `float sampleColor = centralColor.g * 22.0;
` `sampleColor += texture2D(_texture, blurCoordinates[0]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[1]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[2]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[3]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[4]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[5]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[6]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[7]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[8]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[9]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[10]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[11]).g;
` `sampleColor += texture2D(_texture, blurCoordinates[12]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[13]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[14]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[15]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[16]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[17]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[18]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[19]).g * 2.0;
` `sampleColor += texture2D(_texture, blurCoordinates[20]).g * 3.0;
` `sampleColor += texture2D(_texture, blurCoordinates[21]).g * 3.0;
` `sampleColor += texture2D(_texture, blurCoordinates[22]).g * 3.0;
` `sampleColor += texture2D(_texture, blurCoordinates[23]).g * 3.0;
` `sampleColor = sampleColor / 62.0;
` `float ass = centralColor.g - sampleColor + 0.5;
` `for (int i = 0; i < 5; i++)
` `{
` `ass = hardLight(ass);
` `}
` `float lumance = dot(centralColor, W);
` `float alpha = pow(lumance, params.r);
` `vec3 smoothColor = centralColor + (centralColor - vec3(ass)) * alpha * 0.1;
` `smoothColor.r = clamp(pow(smoothColor.r, params.g), 0.0, 1.0);
` `smoothColor.g = clamp(pow(smoothColor.g, params.g), 0.0, 1.0);
` `smoothColor.b = clamp(pow(smoothColor.b, params.g), 0.0, 1.0);
` `vec3 lvse = vec3(1.0) - (vec3(1.0) - smoothColor) * (vec3(1.0) - centralColor);
` `vec3 bianliang = max(smoothColor, centralColor);
` `vec3 rouguang = 2.0 * centralColor * smoothColor + centralColor * centralColor - 2.0 * centralColor * centralColor * smoothColor;
` `vec4 texColor = vec4(mix(centralColor, lvse, alpha), 1.0);
` `texColor.rgb = mix(texColor.rgb, bianliang, alpha);
` `texColor.rgb = mix(texColor.rgb, rouguang, params.b);
` `vec3 satcolor = texColor.rgb * saturateMatrix;
` `texColor.rgb = mix(texColor.rgb, satcolor, params.a);
` `texColor.rgb = vec3(texColor.rgb + vec3(brightness));
` `FragColor = vec4(texColor.r / 1.0, texColor.g / 1.0, texColor.b / 1.0, texColor.a / 1.0);
}
亮度调整:
色调调整:
美颜磨皮调整:
3.OpenGL ES 3.0 in Android
本工程在Android studio 2021 3.1,ndk 为android-ndk-r21e。
使用库:
ncnn + opencv3 + mnn + glm + opengles 3
其中ncnn为新版基于Vulkan加速的推理框架ncnnVulkan;OpenCV主要获取Mat矩阵;mnn为另一种模型推理框架;glm为配合OpenGL的向量库。
3.1 OpenGL ES工程
1. java/com/example/cameraspecialeffect/MainActivity.java:
修改滤镜弹出框显示种类final String items[]
-
cpp/CMakeLists.txt:
添加库:add_library
-
cpp/native-lib.cpp:
添加新的头文件#include;
修改滤镜渲染函数:Java_com_example_cameraspecialeffect_GLRenderJNI_setRenderTypeJNI
-
修改对应添加新cpp与h的include与类名。#idndef
添加GLSL
-
cpp/native-lib.cpp:
如果是BaseVideoRender改的,添加include新,对应的设置asset、g_render。
回想起自己的这五个半月的工作经历,虽然有眼泪也有辛酸,但最多的还是历练与收获。记得刚到公司的第一天,我被安排在一个办公室文员身边学习。真的,第一天是最难熬的,从学校到社会的大环境的转变,身边接触的人也完全换了主角,老师变成老板,同学变成同事,相处之道完全不同,大家把你当成隐形人的感受只有亲身经历过的人才能体会。“实践是检验真理的唯一标准”从学校到社会的大环境的巨大的转变中,我们可能彷徨,迷茫,无法立刻适应新的环境。我们也许看不惯人与人之间的勾心斗角,无法忍受同事之间漠不关心的眼神和言语。很多时候觉得自己没有受到领导重用,所干的只是一些无关重要的杂活,自己的提议或工作不能得到领导的肯定。做不出成绩时,会有来自各方面的压力,领导的眼色同事的嘲讽。失落时的无奈想找人诉说是却发现没有人能够倾诉。而在学校,有同学老师的关心和支持,每日只是上上课,很简单。谚语有云:“纸上得来终觉浅,绝知此事要躬行。”
两个月的实习时间虽然不长,但是我从中学到了很多知识,关于做人,做事,做学问。幸好宿舍的女孩子们对我很好,告诉我他们刚出来工作的经历,也是从被当成隐形人开始的,从打杂的过来的,劝我要挺得住,坚持走过来就好了。我刚开始接触的工作就是招聘,我自己还是一个刚刚走上工作岗位的新手,却要去招人,有点紧张也有点兴奋。我们公司是以模具为主的,有很多有关模具的专有名词是我以前从来没有接触过的,所以也就有很多我不熟悉的工种,时间久了有关模具的知识,听的多了,问的多了,也自然就明白一些了。
大家上午好,我是中央研究院-系统开发二部-图像算法开发组陈帅,在入职的这段时间里,真的是收获很多,提升很多。
从学校到社会的大环境的巨大的转变中,我在来公司之前会有很多顾虑,觉得我们可能彷徨,迷茫,无法立刻适应新的环境。但来了昊一源之后,便全部打消了这些顾虑。
从一开始的军训活动,我看到了我们2022届源动力的无限活力、动力、毅力以及创造力,使得我们逐渐认识彼此、认识公司、也更加清楚地认识自己。再到后来的工厂实习,感悟到我们公司对待产品的严谨态度、各个部门之间的通力配合,就像谚语所说“纸上得来终觉浅,绝知此事要躬行”,不但使自己完成了从学校学生到公司一员的转换,更体会了我们公司最近的快速发展与不断壮大。
实现OpenGL中坐标系统的变换,完成多光源光照渲染,加载渲染3D模型,实现gamma校正,渲染多种滤镜效果,渲染处理视频绿幕抠图。
3.2绿幕抠图滤镜
原理:
1。计算当前像素点RGB值对应的HSV值;
2。设定HSV三个分量的权重,根据权重计算当前像素点的HSV值到给定背景色的HSV值的欧式距离;
3。将欧式距离用smoothstep做平滑,0.5以下的一定要滤掉;
4。将原图和背景图用平滑值混合。
HSV基本颜色分量范围:
片段着色器部分:
` `vec2 RGBtoUV(vec3 rgb)
` `{
` `return vec2
` `(
` `rgb.r * -0.169 + rgb.g * -0.331 + rgb.b * 0.5 + 0.5,
` `rgb.r * 0.5 + rgb.g * -0.419 + rgb.b * -0.081 + 0.5
` `);
` `}
` `vec4 ProcessChromaKey(vec2 texCoord)
` `{
` `vec4 rgba = texture2D(tex, texCoord);
` `float chromaDist = distance(RGBtoUV(texture2D(tex, texCoord).rgb), RGBtoUV(keyColor));
` `float baseMask = chromaDist - similarity;
` `float fullMask = pow(clamp(baseMask / smoothness, 0., 1.), 1.5);
` `rgba.a = fullMask;
` `float spillVal = pow(clamp(baseMask / spill, 0., 1.), 1.5);
` `float desat = clamp(rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722, 0., 1.);
` `rgba.rgb = mix(vec3(desat, desat, desat), rgba.rgb, spillVal);
` `return rgba;
` `}
varying highp vec2 vCoordinate;
uniform sampler2D uVideoframe;
uniform highp mat4 uMVPMatrix;
void main()
{
` `// lookup the color of the texel corresponding to the fragment being
` `// generated while rendering a triangle
` `lowp vec4 tempColor = texture2D(uVideoframe, vCoordinate);
` `// Calculate the average intensity of the texel’s red and blue components
` `lowp float rbAverage = tempColor.r * 0.5 + tempColor.b * 0.5;
` `// Calculate the difference between the green element intensity and the
` `// average of red and blue intensities
` `lowp float gDelta = tempColor.g - rbAverage;
` `// If the green intensity is greater than the average of red and blue
` `// intensities, calculate a transparency value in the range 0.0 to 1.0
` `// based on how much more intense the green element is
` `tempColor.a = 1.0 - smoothstep(0.0, 0.25, gDelta);
` `// Use the cube of the of the transparency value. That way, a fragment that
` `// is partially translucent becomes even more translucent. This sharpens
` `// the final result by avoiding almost but not quite opaque fragments that
` `// tend to form halos at color boundaries.
` `tempColor.a = tempColor.a * tempColor.a * tempColor.a;
` `gl_FragColor = tempColor;
}
完整改写:
// * 相机纹理 *
` `lowp vec4 tempColor = vec4(rgb, 1.0);
` `// * 计算纹素的红色和蓝色分量的平均强度 *
` `lowp float rbAverage = tempColor.r * 0.5 + tempColor.b * 0.5;
` `// * 计算绿色元素强度与红蓝色强度平均值之间的差 *
` `lowp float gDelta = tempColor.g * (0.45*green_strenth+0.55) - rbAverage;
` `// * 如果绿色强度大于红色和蓝色强度的平均值,则根据绿色元素的强度计算 0.0 到 1.0 范围内的透明度值 *
` `tempColor.a = 1.0 - smoothstep(0.0, 0.25, gDelta); // 1.0-0.75
` `// * 使用透明度值的多次幂。这样,部分透明的片段变得更加透明。这通过避免几乎但不完全不透明的碎片来锐化最终结果,这些碎片倾向于在颜色边界处形成光晕*
` `tempColor.a = tempColor.a * tempColor.a * tempColor.a;
` `// *大海*
` `vec4 back = texture(back_tex, TexCoord);
` `vec3 result_mix;
` `result_mix = tempColor.rgb * (tempColor.a) + back.rgb * (1.0-tempColor.a);
` `out_color = vec4(result_mix,1.0);
3.3人像外灰色
参考片段着色器部分:
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D u_texture0;//rgba相机图
uniform sampler2D u_texture1;//人像
uniform int u_renderType;
uniform float u_offset;
void main()
{
` `float gray = texture(u_texture1, v_texCoord).r;//人像
` `vec4 rgba = texture(u_texture0, v_texCoord); // *相机图*
` `if(gray > 0.01) { // *人物部分*
` `outColor = rgba; // *相机原图*
` `}
` `else
` `{
` `float Y = 0.299 * rgba.r + 0.587 * rgba.g + 0.114 * rgba.b;
` `vec4 grayColor = vec4(vec3(Y), 1.0); //RGB 灰度化
` `outColor = mix(grayColor, rgba, u_offset); //混合渐变
` `}
YUV转RGB:
改写片段着色器部分:
#version 300 es
layout(binding = 0) uniform sampler2D y_samp;
layout(binding = 1) uniform sampler2D u_samp;
layout(binding = 2) uniform sampler2D v_samp;
layout(binding = 3) uniform sampler2D alpha_tex;
in vec2 TexCoord;
out vec4 out_color;
// ************************************************************************************************
// *YUV转RGBA*
vec4 YUV2RGBA(vec2 Coord)
{
` `vec3 rgb = mat3(1.0, 1.0, 1.0,
` `0, -0.344, 1.770,
` `1.403, -0.714, 0)
` `* vec3(texture(y_samp, Coord).r,
` `texture(u_samp, Coord).r - 0.5,
` `texture(v_samp, Coord).r - 0.5);
` `return vec4(rgb, 1.0);
}
// *转灰度(平均值法)*
vec4 RGB2GREY(vec3 RGB)
{
` `float average = (RGB.r + RGB.g + RGB.b)/3.0;
` `return vec4(average, average, average, 1.0);
}
// ************************************************************************************************
void main()
{
` `// *原图*
` `vec4 texColor_origin = YUV2RGBA(TexCoord);
` `// *转灰度(2)平均值法*
` `vec4 texColor_grey = RGB2GREY(texColor_origin.rgb);
` `// *人像alpha通道*
` `vec4 alpha = texture(alpha_tex, TexCoord);
` `// 过滤掉不太确定的地方
` `if(alpha.r < 0.12)
` `alpha.r = 0.0;
` `// *2. 人像部分添加灰色背景*
` `if(alpha.r != 0.0)
` `{
` `texColor_grey.rgb = texColor_origin.rgb;
` `}
` `// *3. 输出*
` `out_color = texColor_grey;
}
问题一:
(用uniform传入的Y、U、V纹理与传入的人像分割纹理时间存在延时)
解决:
将YUV改为模型推理时用的输入Mat :src_mat。
// 初始化纹理(fore)
` `glGenTextures(1, &fore_tex_id);
` `glBindTexture(GL_TEXTURE_2D, fore_tex_id);
` `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
` `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
` `glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glActiveTexture(GL_TEXTURE4);
` `glBindTexture(GL_TEXTURE_2D, fore_tex_id);
` `glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, src_mat.cols, src_mat.rows, 0, GL_RED, GL_FLOAT, src_mat.data);
问题二:
(前后景颜色分割边缘明显)
解决:
应改为前后景alpha通道的mix融合,而不是简单判断if(alpha.r != 0.0),去替换人像彩色部分。
` `// *原图*
` `vec4 texColor_origin = texture(src_mat, TexCoord); // *改为推理时的输入src_mat,避免人像与背景延时大问题*
` `texColor_origin = vec4(texColor_origin.b,texColor_origin.g,texColor_origin.r,texColor_origin.a); // RGBA->BGRA
` `// *转灰度(2)平均值法*
` `vec4 texColor_grey = RGB2GREY(texColor_origin.rgb);
` `// *人像alpha通道*
` `vec4 alpha = texture(alpha_tex, TexCoord);
` `// 过滤掉不太确定的地方(别过滤了,保持平滑mix效果更好)
// if(alpha.r < 0.12)
// alpha.r = 0.0;
` `// *2. 人像部分添加灰色背景*
` `// *(别简单判断融合了,通过alpha渐变融合效果更好)*
` `vec3 result_mix = mix(texColor_grey.rgb, texColor_origin.rgb, alpha.r);
` `// *3. 输出*
` `out_color = vec4(result_mix,1.0);
3.4脸部马赛克
主要是基于人脸检测模型推理,
获得人脸的左下与右上的坐标点位置:
X1Y1:(lines[max_area_id *8 + 6], lines[max_area_id *8 + 7])
X2Y2:(lines[max_area_id *8 + 2], lines[max_area_id *8 + 3])
然后通过uniform传坐标数据至着色器:
// 发送数据
` `glUniform2f(x1y1_loc, lines[max_area_id *8 + 6], lines[max_area_id *8 + 7]);
` `glUniform2f(x2y2_loc, lines[max_area_id *8 + 2], lines[max_area_id *8 + 3]);
在着色器部分:
#version 300 es
layout(binding = 0) uniform sampler2D y_samp;
layout(binding = 1) uniform sampler2D u_samp;
layout(binding = 2) uniform sampler2D v_samp;
uniform vec2 x1y1;
uniform vec2 x2y2;
uniform int has_face; // 是否有人脸
uniform vec2 img_size;// 图像尺寸
in vec2 TexCoord;
out vec4 out_color;
vec4 YUV2RGB(vec2 Coord)
{
` `vec3 rgb = mat3(1.0, 1.0, 1.0,
` `0, -0.344, 1.770,
` `1.403, -0.714, 0)
` `* vec3(texture(y_samp, Coord).r,
` `texture(u_samp, Coord).r - 0.5,
` `texture(v_samp, Coord).r - 0.5);
` `return vec4(rgb, 1.0);
}
void main()
{
` `vec4 texColor1 = YUV2RGB(TexCoord);
` `vec2 mosaicSize = vec2(48.0, 48.0);//马赛克大小
` `vec2 intXY = vec2(TexCoord.x*img_size.x, TexCoord.y*img_size.y);//计算图像的实际位置
` `vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);
` `vec2 UVMosaic = vec2(XYMosaic.x/img_size.x, XYMosaic.y/img_size.y);//换算回纹理坐标(0.0-1.0)
` `vec4 texColor2 = YUV2RGB(UVMosaic);
` `// * 人脸范围 & 人脸数>0 *
` `if(has_face>0 && (gl_FragCoord.x > (x1y1.x+1.0)*0.5*img_size.x && gl_FragCoord.x < (x2y2.x+1.0)*0.5*img_size.x) && (gl_FragCoord.y>(x1y1.y+1.0)*0.5*img_size.y && gl_FragCoord.y<(x2y2.y+1.0)*0.5*img_size.y))
// if(gl_FragCoord.x < 1400.0/2.0 && gl_FragCoord.y < 1800.0/2.0)
` `{
` `// *马赛克*
` `out_color = texColor2;
` `}
` `else
` `{
` `// *原图*
` `out_color = texColor1;
` `}
}
PFLD98人脸关键点:
其中ncnn为新版基于Vulkan加速的推理框架ncnnVulkan;OpenCV主要获取Mat矩阵;mnn为另一种模型推理框架;glm为配合OpenGL的向量库