OpenGL (一)OpenGL ES 繪制基礎(chǔ)
OpenGL (二)GLSurface 視頻錄制
OpenGL (三)濾鏡filter 應(yīng)用
OpenGL (四)貼紙和磨皮理論
用到了人臉識(shí)別定位吏颖。SeetaFace2
具體內(nèi)容搜索關(guān)鍵字产喉,到github搜搜,找到Android編譯,不再用鏈接,這里的書寫環(huán)境用到鏈接容易鎖文章
編譯好之后焕数,進(jìn)行配置android C++ 環(huán)境
具體api的時(shí)候參考他的example
大眼萌算法
其實(shí)就是根據(jù)photoshop上面功能的算法公式
Local Scale warps 局部縮放
前提:定位到眼睛的位置
解釋: rmax為圓形選區(qū)的半徑透罢,r為控制點(diǎn)移動(dòng)位移钦铁,即目的矢量,as是(-1,1)之間的縮放參數(shù)殉疼,a<0表示縮小梯浪,a>0表示放大,給出點(diǎn)X時(shí)瓢娜,可以求出它變換前的坐標(biāo)U(精確的浮點(diǎn)坐標(biāo))挂洛,然后用變化前圖像在U點(diǎn)附近的像素進(jìn)行插值,求出U的像素值眠砾。如此對(duì)圓形選區(qū)內(nèi)的每一個(gè)像素進(jìn)行求值抹锄,便可得出變換后的圖像。
理解圖像
實(shí)現(xiàn)
寫著色器
bigeye_frag.frag
precision mediump float;//數(shù)據(jù)精度
varying vec2 aCoord;
uniform sampler2D vTexture;// samplerExternalOES 圖片荠藤,采樣器
uniform vec2 left_eye;
uniform vec2 right_eye;
//r: 要畫的點(diǎn)與眼睛的距離
//max 最大半徑
//0.4 放大系數(shù) -1 -- 1 大于0 就是放大
float fs(float r,float rmax){
// return (1.0-pow(r/rmax-1.0,2.0)* 0.5)*r;
return (1.0-pow(r/rmax-1.0,2.0)* 0.5);
}
vec2 newCoord(vec2 coord,vec2 eye,float rmax){
vec2 p = cood;
//得到要畫的點(diǎn)coord伙单,與眼睛的距離
float r = distance(coord,eye);
if(r < rmax){
// 在這個(gè)范圍,新的點(diǎn)與眼睛的距離
float fsr = fs(r,rmax);
// (縮放后的點(diǎn)- 眼睛的點(diǎn))/(原點(diǎn)-眼睛的點(diǎn))= fsr /r
//縮放的點(diǎn)=fsr/r * (原點(diǎn)-眼睛的點(diǎn)) + 眼睛的點(diǎn)
// p = fsr/r*(coord-eye) + eye;
// 優(yōu)化 抵消掉,這需要除哈肖,上面公式需要乘
p = fsr/(coord-eye) + eye;
}
return p;
}
void main(){
// gl_FragColor = texture2D(vTexture,aCoord);//從圖片中找到像素點(diǎn)吻育,賦值
float rmax = distance(left_eye,right_eye)/2.0;
vec2 p = newCoord(aCoord,left_eye,rmax);
p = newCoord(aCoord,right_eye,rmax);
gl_FragColor = texture2D(vTexture,p);//從圖片中找到像素點(diǎn),賦值
}
opecv 相關(guān)+CameraX采集人臉數(shù)據(jù)
參考之前的文章
將識(shí)別的人臉圖像通過SeetaFace2 定位到眼睛的位置淤井,
然后計(jì)算布疼,利用著色器在指定區(qū)域著色。
大眼濾鏡
public class BigEyeFilter extends AbstractFrameFilter {
private FloatBuffer left;
private FloatBuffer right;
int left_eye;
int right_eye;
Face face;
public BigEyeFilter(Context context) {
super(context, R.raw.base_vert, R.raw.bigeye_frag);
left = ByteBuffer.allocateDirect(8).order(ByteOrder.nativeOrder()).asFloatBuffer();
right = ByteBuffer.allocateDirect(8).order(ByteOrder.nativeOrder()).asFloatBuffer();
}
@Override
public void initGL(Context context, int vertexShaderId, int fragmentShaderId) {
super.initGL(context, vertexShaderId, fragmentShaderId);
left_eye = GLES20.glGetUniformLocation(program, "left_eye");
right_eye = GLES20.glGetUniformLocation(program, "right_eye");
}
@Override
public int onDraw(int texture, FilterChain filterChain) {
FilterContext filterContext = filterChain.filterContext;
face = filterContext.face;
return super.onDraw(texture, filterChain);
}
@Override
public void beforeDraw() {
super.beforeDraw();
if (face == null) {
// Log.e("zcw_opengl","未識(shí)別到人臉");
return;
}
Log.e("zcw_opengl","是一個(gè)-------人臉");
float x = face.left_x / face.imgWidth;
float y = 1.0f - face.left_y / face.imgHeight;
left.clear();
left.put(x).put(y).position(0);
GLES20.glUniform2fv(left_eye, 1, left);
x = face.right_x / face.imgWidth;
y = 1.0f - face.right_y / face.imgHeight;
right.clear();
right.put(x).put(y).position(0);
GLES20.glUniform2fv(right_eye, 1, right);
}
}
責(zé)任連模式币狠,使用濾鏡開發(fā)更簡(jiǎn)單
public class FilterContext {
public Face face; // 人臉
public float[] cameraMtx; //攝像頭轉(zhuǎn)換矩陣
public int width;
public int height;
public void setSize(int width, int height) {
this.width = width;
this.height = height;
}
public void setTransformMatrix(float[] mtx) {
this.cameraMtx = mtx;
}
public void setFace(Face face) {
this.face = face;
}
}
--------
public class FilterChain {
public FilterContext filterContext;
private List<AbstractFilter> filters;
private int index;
public FilterChain(List<AbstractFilter> filters, int index, FilterContext filterContext) {
this.filters = filters;
this.index = index;
this.filterContext = filterContext;
}
public int proceed(int textureId) {
if (index >= filters.size()) {
return textureId;
}
FilterChain nextFilterChain = new FilterChain(filters, index + 1, filterContext);
AbstractFilter abstractFilter = filters.get(index);
return abstractFilter.onDraw(textureId, nextFilterChain);
}
public void setSize(int width, int height) {
filterContext.setSize(width, height);
}
public void setTransformMatrix(float[] mtx) {
filterContext.setTransformMatrix(mtx);
}
public void setFace(Face face) {
filterContext.setFace(face);
}
public void release() {
for (AbstractFilter filter : filters) {
filter.release();
}
}
}
------
/**
* FBO
*/
public abstract class AbstractFrameFilter extends AbstractFilter {
int[] frameBuffer;
int[] frameTextures;
public AbstractFrameFilter(Context context, int vertexShaderId, int fragmentShaderId) {
super(context, vertexShaderId, fragmentShaderId);
}
public void createFrame(int width, int height) {
releaseFrame();
//創(chuàng)建FBO
/**
* 1游两、創(chuàng)建FBO + FBO中的紋理
*/
frameBuffer = new int[1];
frameTextures = new int[1];
GLES20.glGenFramebuffers(1, frameBuffer, 0);
OpenGLUtils.glGenTextures(frameTextures);
/**
* 2、fbo與紋理關(guān)聯(lián)
*/
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextures[0]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
null);
//紋理關(guān)聯(lián) fbo
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0]); //綁定FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
frameTextures[0],
0);
/**
* 3漩绵、解除綁定
*/
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
@Override
public int onDraw(int texture, FilterChain filterChain) {
FilterContext filterContext = filterChain.filterContext;
createFrame(filterContext.width, filterContext.height);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0]); //綁定fbo
super.onDraw(texture, filterChain);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); //
return filterChain.proceed(frameTextures[0]);
}
@Override
public void release() {
super.release();
releaseFrame();
}
private void releaseFrame() {
if (frameTextures != null) {
GLES20.glDeleteTextures(1, frameTextures, 0);
frameTextures = null;
}
if (frameBuffer != null) {
GLES20.glDeleteFramebuffers(1, frameBuffer, 0);
}
}
}
核心 onDraw
public int onDraw(int texture, FilterChain filterChain) {
FilterContext filterContext = filterChain.filterContext;
createFrame(filterContext.width, filterContext.height);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0]); //綁定fbo
super.onDraw(texture, filterChain);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); //
return filterChain.proceed(frameTextures[0]);
}
我們有很多過濾器贱案,可以看成不同處理流程,都繼承超類AbstractFrameFilter 并且實(shí)現(xiàn)onDraw 調(diào)用super.onDraw
CameraFilter,BigEyeFilter,ScreenFilter,RecordFilter