当前位置: 首页 > news >正文

个人的视频网站如何做嘉兴网站专业制作

个人的视频网站如何做,嘉兴网站专业制作,湖南专业竞价优化服务,营销策划公司的成本有哪些从本篇开始#xff0c;会在上一篇搭建的滤镜框架的基础上#xff0c;介绍具体的滤镜效果该如何制作。本篇会先介绍大眼滤镜#xff0c;先来看一下效果#xff0c;原图如下#xff1a; 使用手机后置摄像头对眼部放大后的效果#xff1a; 制作大眼滤镜所需的主要知识点会在上一篇搭建的滤镜框架的基础上介绍具体的滤镜效果该如何制作。本篇会先介绍大眼滤镜先来看一下效果原图如下 使用手机后置摄像头对眼部放大后的效果 制作大眼滤镜所需的主要知识点 OpenCV 人脸定位SeetaFace 五官定位OpenGL 绘制大眼特效 下面让我们一步步来实现这个效果吧。 1、项目配置 要对眼部进行放大那么一定需要识别到图像中眼睛的位置通常我们会先识别到人脸再去识别人眼这样比从整张图片的范围内直接定位人眼要快。 人脸定位我们使用 OpenCV虽然 OpenCV 也提供了人眼定位的模型文件但是由于准确率一般因此我们使用中科院开源的 SeetaFace 定位人眼。为人眼添加滤镜效果的任务自然落到 OpenGL 上。 OpenCV 在 Android Studio 上的配置在OpenCV 入门一 —— OpenCV 基础中已经讲过去参考那篇文章这里就不再赘述下面只介绍 SeetaFace 的配置。 1.1 配置 SeetaFace 中科院开源的人脸识别引擎 SeetaFace比 OpenCV 自带模型的识别率要好一些。GitHub 下载 SeetaFaceEngine 可以看到包括三个核心模块 SeetaFace Alignment面部特征点定位模块SeetaFace Detection人脸检测模块SeetaFace Identification人脸特征提取与比对模块 SeetaFace_config.docx 是在 Windows 的 VS 配置 SeetaFace 的文档这里我们要将其配置到 AS 中。步骤如下 将 FaceAlignment 目录下的 include 和 src 两个目录以及 CMakeLists.txt 拷贝到 /src/main/cpp/SeetaFace 目录下其中 src 目录中有一个 test 目录我们并不需要可以删除 test 目录但是该目录下的 face_alignment_test.cpp 可以指导我们如何使用 SeetaFace 修改 SeetaFace 的 CMakeLists.txt注释掉不需要的部分 # 低于主 CMakeLists 要求的最低版本干掉 #cmake_minimum_required(VERSION 2.8.4)# 不需要 #project(seeta_fa_lib)# 不需要构建示例可以和最后的 if (BUILD_EXAMPLES) 一起干掉 # Build options #option(BUILD_EXAMPLES Set to ON to build examples ON)# Use C11 #set(CMAKE_CXX_STANDARD 11) #set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -stdc11) message(STATUS C11 support has been enabled by default.)set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -O2)set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -msse4.1)include_directories(include)set(src_files src/cfan.cppsrc/face_alignment.cppsrc/sift.cpp)# 修改编译为静态库 add_library(seeta_fa_lib STATIC ${src_files}) set(fa_required_libs seeta_fa_lib)#[[if (BUILD_EXAMPLES)message(STATUS Build with examples.)find_package(OpenCV)if (NOT OpenCV_FOUND)message(WARNING OpenCV not found. Test will not be built.)else()include_directories(${OpenCV_INCLUDE_DIRS} build)link_directories(build)list(APPEND fa_required_libs ${OpenCV_LIBS} seeta_facedet_lib)add_executable(fa_test src/test/face_alignment_test.cpp)target_link_libraries(fa_test ${fa_required_libs})endif() endif()]]修改主 CMakeLists.txt # 指定 SeetaFace 的 CMakeLists 文件 add_subdirectory(${CMAKE_SOURCE_DIR}/SeetaFace/FaceAlignment)# 导入 SeetaFace 的头文件 include_directories(${CMAKE_SOURCE_DIR}/SeetaFace/FaceAlignment/include)target_link_libraries(opencvlogopencv_java4 # 链接 OpenCV 动态库android # 因为要用 ANativeWindow 渲染因此要链接 libandroidseeta_fa_lib # 链接 SeetaFace 静态库 )修改模块的 build.gradle因为 SeetaFace 的 CMakeLists.txt 中声明了使用 C11gradle 要做出相应的配置 android {defaultConfig {externalNativeBuild {cmake {cppFlags -stdc11}}} }将人脸识别模型 SeetaFaceEngine-master/FaceAlignment/model/seeta_fa_v1.1.bin 拷贝到 /src/main/assets/ 目录下 SeetaFace 支持识别人脸的 5 个关键点两只眼睛各 1 个、鼻子 1 个、嘴边两侧各 1 个。 如果 Native 层编辑 cpp 代码时没有代码提示、格式排版甚至有错也不报新建文件时没有 C/C 的选项并且 Build - Refresh Linked C Projects 也是灰色的可能是因为没有在 build.gradle 中添加 Native 编译配置 android { externalNativeBuild {cmake {path file(src/main/cpp/CMakeLists.txt)version 3.22.1} } }2、人脸识别与人眼识别 有关 OpenCV 人脸识别的内容我们在 OpenCV 系列文章的OpenCV 入门六—— Android 下的人脸识别中详细讲过。虽然这里又添加了 SeetaFace 进行人眼识别但主要过程没有太大的变化。 从代码结构上说识别工作要分为两层 Native 层具体的识别工作都是交由 OpenCV 和 SeetaFace 在 Native 层完成的我们需要将识别的结果人脸的坐标和宽高数据以及五官坐标封装成一个上层的 Face 对象并返回给上层上层定义封装人脸数据的 Face 类同时还需要一个 FaceTracker 作为上层与 Native 层沟通的桥梁一方面接收外界的指令通知 Native 进行初始化、人脸检测等工作另一方面接收 Native 层的识别结果存入 Face 并提供给外界作为 OpenGL 添加各种滤镜的依据 从过程上说主要分为以下几个步骤 初始化使用指定的识别模型在 Native 层初始化 OpenCV 和 SeetaFace开始识别开启 OpenCV 的跟踪识别人脸识别OpenCV 进行人脸识别识别到的结果保存到一个集合中SeetaFace 再对集合中的每个人脸进行特征点识别将包含两眼位置信息的特征点数据保存起来反射构造上层对象将人脸信息和特征点信息通过反射的方式封装到上层的 Face 对象中并将其返回给上层 接下来结合代码详细说明上述实现步骤。 2.1 初始化 先将 OpenCV 和 SeetaFace 识别人脸的模型文件拷贝到项目的 /src/main/res/raw 目录下在创建渲染器时将模型文件拷贝到手机中 class GLRender(private val mGLSurfaceView: GLSurfaceView) : GLSurfaceView.Renderer,SurfaceTexture.OnFrameAvailableListener {private val mOpencvModelPath Utils.copyAsset2Dir(mGLSurfaceView.context, lbpcascade_frontalface.xml)private val mSeetaFaceModelPath Utils.copyAsset2Dir(mGLSurfaceView.context, seeta_fa_v1.1.bin) }工具类参考代码如下 class Utils {companion object {fun copyAsset2Dir(context: Context, assetName: String): String {val cascadeDir context.getDir(cascade, Context.MODE_PRIVATE)val cascadeFile File(cascadeDir, assetName)if (!cascadeFile.exists()) {context.resources.assets.open(assetName).use { inputStream -FileOutputStream(cascadeFile).use { outputStream -val buffer ByteArray(2048)var length: Intwhile (inputStream.read(buffer).also { length it } 0) {outputStream.write(buffer, 0, length)}}}}return cascadeFile.absolutePath}} }然后在渲染器监听到 GLSurfaceView 的尺寸发生变化时创建上层的 FaceTracker override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {...// 创建 FaceTracker 开始检测人脸mFaceTracker FaceTracker(mCameraHelper, mOpencvModelPath, mSeetaFaceModelPath)mFaceTracker.startTracking()}FaceTracker 要调用 Native 方法进行初始化和开启检测 class FaceTracker(private val mCameraHelper: CameraHelper,opencvModelPath: String,seetaFaceModelPath: String ) {// Native 层 FaceTracker 对象的地址private var mFaceTracker 0Linit {mFaceTracker nativeInit(opencvModelPath, seetaFaceModelPath)}fun startTracking() {nativeStart(mFaceTracker)}private external fun nativeInit(opencvModelPath: String, seetaFaceModelPath: String): Longprivate external fun nativeStart(faceTracker: Long) }nativeInit() 会创建 Native 层的 FaceTracker 对象并将地址返回给上层这样上层在执行后续的开启识别、结束识别、人脸检测等方法时将该地址传入便可在 Native 层直接将地址转换成 Native 的 FaceTracker 对象进而执行相应的函数 #include FaceTracker.hextern C JNIEXPORT jlong JNICALL Java_com_opengl_filters_FaceTracker_nativeInit(JNIEnv *env, jobject thiz,jstring opencv_model_path_,jstring seeta_face_model_path_) {const char *opencv_model_path env-GetStringUTFChars(opencv_model_path_, nullptr);const char *seeta_face_model_path env-GetStringUTFChars(seeta_face_model_path_, nullptr);auto faceTracker new FaceTracker(opencv_model_path, seeta_face_model_path);env-ReleaseStringUTFChars(opencv_model_path_, opencv_model_path);env-ReleaseStringUTFChars(seeta_face_model_path_, seeta_face_model_path);// 将 Native 对象的地址返回给上层return reinterpret_castjlong(faceTracker); }extern C JNIEXPORT void JNICALL Java_com_opengl_filters_FaceTracker_nativeStart(JNIEnv *env, jobject thiz, jlong face_tracker) {if (face_tracker) {auto *faceTracker reinterpret_castFaceTracker *(face_tracker);faceTracker-startTracking();} }FaceTracker.h 内需要定义初始化 OpenCV 的跟踪器对象所需的 CascadeDetectorAdapter #ifndef OPENGL_FACETRACKER_H #define OPENGL_FACETRACKER_H#include opencv2/opencv.hpp #include jni.h #include SeetaFace/FaceAlignment/include/face_alignment.husing namespace cv;class CascadeDetectorAdapter : public DetectionBasedTracker::IDetector { public:CascadeDetectorAdapter(cv::Ptrcv::CascadeClassifier detector) :IDetector(),Detector(detector) {}// 检测人脸的函数Mat 相当于 Android 的一张 Bitmap。一张图片有几个人脸就会调用本方法几次void detect(const cv::Mat Image, std::vectorcv::Rect objects) {Detector-detectMultiScale(Image, objects, scaleFactor,minNeighbours, 0, minObjSize, maxObjSize);}virtual ~CascadeDetectorAdapter() default;private:CascadeDetectorAdapter();cv::Ptrcv::CascadeClassifier Detector; };class FaceTracker {public:FaceTracker(const char *opencv_model_path, const char *seeta_face_model_path);void startTracking();void stopTracking();void detect(const Mat src, std::vectorRect2f rectangles);private:PtrDetectionBasedTracker tracker nullptr;Ptrseeta::FaceAlignment faceAlignment nullptr; };#endif //OPENGL_FACETRACKER_HFaceTracker 的构造函数要创建 OpenCV 和 SeetaFace 的检测器对象 FaceTracker::FaceTracker(const char *opencv_model_path, const char *seeta_face_model_path) {// 1.创建 OpenCV 识别对象// 1.1 创建检测器PtrCascadeClassifier detectorClassifier makePtrCascadeClassifier(opencv_model_path);PtrCascadeDetectorAdapter mainDetector makePtrCascadeDetectorAdapter(detectorClassifier);// 1.2 创建跟踪器PtrCascadeClassifier trackerClassifier makePtrCascadeClassifier(opencv_model_path);PtrCascadeDetectorAdapter trackingDetector makePtrCascadeDetectorAdapter(trackerClassifier);// 1.3 创建识别对象DetectionBasedTracker::Parameters detectionParams;tracker makePtrDetectionBasedTracker(mainDetector, trackingDetector, detectionParams);// 2.创建 SeetaFace 识别对象faceAlignment makePtrseeta::FaceAlignment(seeta_face_model_path); }初始化完成至于 startTracking() 仅需调用 tracker 的 run() 即可开始检测 void FaceTracker::startTracking() {if (tracker) {tracker-run();} }2.2 人脸识别 进入识别流程需要将图像数据传给 Native 层进行识别图像数据来自 CameraHelper typealias CameraPreviewCallback (data: ByteArray) - Unitclass CameraHelper(private val mActivity: Activity,private var mCameraId: Int,private var mWidth: Int,private var mHeight: Int ) : Camera.PreviewCallback {private var mPreviewCallback: CameraPreviewCallback? null// Camera.PreviewCallbackoverride fun onPreviewFrame(data: ByteArray?, camera: Camera?) {data?.let {// 将 mBuffer 继续放入回调队列中接收数据mCamera.addCallbackBuffer(mBuffer)// 将预览画面数据回调给外界mPreviewCallback?.invoke(it)}}fun setPreviewCallback(callback: CameraPreviewCallback) {mPreviewCallback callback} }渲染器设置 PreviewCallback 获取图像数据转发给 FaceTracker 要求检测 override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {// 1.初始化 CameraHelpermCameraHelper CameraHelper(mGLSurfaceView.context as Activity,Camera.CameraInfo.CAMERA_FACING_BACK,CameraHelper.WIDTH,CameraHelper.HEIGHT)mCameraHelper.setPreviewCallback {mFaceTracker.detect(it)}...}由于人脸检测是耗时操作肯定放在子线程中FaceTracker 采用 HandlerThread 来处理检测工作 init {mFaceTracker nativeInit(opencvModelPath, seetaFaceModelPath)mHandlerThread HandlerThread(Face-Detect-Thread)mHandlerThread.start()mHandler Handler(mHandlerThread.looper) { message -mFace nativeDetect(mFaceTracker,message.obj as ByteArray,mCameraHelper.getCameraId(),CameraHelper.WIDTH,CameraHelper.HEIGHT)true}}fun detect(data: ByteArray) {// 先移除之前的消息保持检测最新的 datamHandler.removeMessages(MSG_DETECT)// 添加新的 data 到消息队列中mHandler.obtainMessage(MSG_DETECT, data).sendToTarget()}这样就会调用 nativeDetect() 进入 Native 层 extern C JNIEXPORT jobject JNICALL Java_com_opengl_filters_FaceTracker_nativeDetect(JNIEnv *env, jobject thiz, jlong face_tracker,jbyteArray data_, jint camera_id, jint width,jint height) {if (!face_tracker) {return nullptr;}jbyte *data env-GetByteArrayElements(data_, nullptr);auto *faceTracker reinterpret_castFaceTracker *(face_tracker);// 1.人脸检测...// 2.生成上层的 Face 对象返回给上层... }主要任务有两项 利用 OpenCV 和 SeetaFace 检测人脸信息并保存将人脸信息封装到上层的 Face 对象中并返回 下面分别来看这两项内容实现。 检测过程 大致过程如下 根据原图像生成用于 OpenCV 识别的图片对象 Mat将 Mat 由 YUV NV21 格式转换为 RGBA 格式并且旋转图像将其调正取原图的灰度图和直方图均衡化准备正式开始识别调用 OpenCV 的 API 进行检测结果保存在集合中从 OpenCV 的结果中取出数据进行 SeetaFace 识别将检测的关键点数据保存起来 我们先来看 nativeDetect() 的实现包含前三步 extern C JNIEXPORT jobject JNICALL Java_com_opengl_filters_FaceTracker_nativeDetect(JNIEnv *env, jobject thiz, jlong face_tracker,jbyteArray data_, jint camera_id, jint width,jint height) {if (!face_tracker) {return nullptr;}jbyte *data env-GetByteArrayElements(data_, nullptr);auto *faceTracker reinterpret_castFaceTracker *(face_tracker);// 1.人脸检测// 1.1 创建 Mat 对象并做预处理Mat src(height * 3 / 2, width, CV_8UC1, data);// 将 src 的格式由 YUV NV21 转换为 RGBAcvtColor(src, src, COLOR_YUV2RGBA_NV21);// 对原始图像进行旋转调正if (camera_id 1) {// 前置摄像头需要逆时针旋转 90°rotate(src, src, ROTATE_90_COUNTERCLOCKWISE);// 前置还需要取一个水平方向的镜像如果传 0 就是竖直方向flip(src, src, 1);} else {// 后置摄像头需要顺时针旋转 90°rotate(src, src, ROTATE_90_CLOCKWISE);}// 1.2 对 Mat 进行人脸检测// 将图片转换为灰度图可以减少杂色增加识别几率Mat gray;cvtColor(src, gray, COLOR_RGBA2GRAY);// 增强对比度目的是增强轮廓因为识别是对轮廓进行识别equalizeHist(gray, gray);// 检测人脸结果的矩形保存到 rectangles 中std::vectorRect2f rectangles;faceTracker-detect(gray, rectangles);// data 使命结束及时释放env-ReleaseByteArrayElements(data_, data, 0);... }具体的检测工作由 FaceTracker 的 detect() 完成检测结果保存在 rectangles 中 void FaceTracker::detect(const Mat src, std::vectorRect2f rectangles) {// 1.先将 OpenCV 检测到矩形保存到 faces 中std::vectorRect faces;// 检测tracker-process(src);// 获取结果tracker-getObjects(faces);if (!faces.empty()) {// 先只处理一个人脸将其位置信息保存到 rectangles 中备用Rect face faces[0];rectangles.emplace_back(face.x, face.y, face.width, face.height);// 2.使用 SeetaFace 检测人脸以获取五官位置需要准备三个参数// 2.1 图像数据 ImageData切记如果使用空参构造函数一定要为 num_channels 赋值seeta::ImageData imageData seeta::ImageData(src.cols, src.rows);imageData.data src.data;// 2.2 人脸矩形信息 FaceInfoseeta::FaceInfo faceInfo;seeta::Rect bbox;bbox.x face.x;bbox.y face.y;bbox.width face.width;bbox.height face.height;faceInfo.bbox bbox;// 2.3 人脸 5 个关键点的集合是一个入参出参seeta::FacialLandmark landmarks[5];// 2.4 执行 SeetaFace 人脸定位faceAlignment-PointDetectLandmarks(imageData, faceInfo, landmarks);// 3.将关键点保存到 rectangles 中for (auto landmark : landmarks) {// 我们只需要关键点的坐标而无需宽高数据rectangles.emplace_back(landmark.x, landmark.y, 0, 0);}} }过程梳理 先用 OpenCV 检测人脸调用 tracker-process(src) 对原图进行检测再通过 tracker-getObjects(faces) 将检测到的人脸矩形保存到 faces 集合中调用 SeetaFace 的 faceAlignment-PointDetectLandmarks() 检测人脸的 5 个特征点该函数需要三个参数 ImageData保存图像信息的对象包括图像宽高以及像素数据如果通过构造函数创建该对象可以不用显式指定 num_channels构造函数会为其赋默认值为 1FaceInfo人脸信息主要是指定它的 bbox 字段包含人脸矩形的左上角坐标以及矩形宽高FacialLandmark人脸特征点关键点SeetaFace 会将左眼、右眼、鼻子、左嘴角、右嘴角这 5 个特征点的坐标检测出来这里声明了 FacialLandmark 类型的数组就是用来接收这 5 个点的 将人脸信息起始点和宽高以及 5 个关键点信息主要是起始点宽高由于不需要都被设置为 0共 6 个矩形保存到参数的 rectangles 集合中 人脸宽高数据在本节的大眼滤镜中用不到但是在下一篇添加贴纸效果时有用由于是很小的点也不适宜在下一篇中单独拿出来说因此就在这里直接保存这个信息了。 创建上层对象 这里的上层对象就是指 Face class Face(// 关键点的左上角坐标集合val landmarks: FloatArray,// 人脸宽高val faceWidth: Int,val faceHeight: Int,// 被检测的图像宽高val imgWidth: Int,val imgHeight: Int )我们要在 Native 通过反射的方式创建该对象主要就是先准备好构造方法内的参数数据。其中landmarks 关键点坐标可以通过上一步中计算出的 rectangles 集合获取人脸宽高保存在 rectangles 中的第一个矩形内被检测的图像宽高可以通过灰度图获取。 参考代码如下 extern C JNIEXPORT jobject JNICALL Java_com_opengl_filters_FaceTracker_nativeDetect(JNIEnv *env, jobject thiz, jlong face_tracker,jbyteArray data_, jint camera_id, jint width,jint height) {// 2.生成上层的 Face 对象// 2.1 先获取被检测的图片宽高数据备用int imgWidth gray.cols;int imgHeight gray.rows;src.release();gray.release();int rectSize rectangles.size();if (rectSize) {// 2.2 创建 Face 构造方法中的关键点集合 FloatArrayint floatArraySize rectSize * 2;jfloatArray floatArray env-NewFloatArray(floatArraySize);for (int i 0; i rectSize; i) {float f[2] {rectangles[i].x, rectangles[i].y};env-SetFloatArrayRegion(floatArray, i * 2, 2, f);}// 2.3 获取人脸矩形宽高Rect faceRect rectangles[0];int faceWidth faceRect.width;int faceHeight faceRect.height;// 2.4 获取 Face 类与构造函数的 ID创建 Face 对象并返回给上层jclass clazz env-FindClass(com/opengl/filters/Face);jmethodID constructorID env-GetMethodID(clazz, init, ([FIIII)V);return env-NewObject(clazz, constructorID, floatArray, faceWidth, faceHeight, imgWidth,imgHeight);}return nullptr; }这样在 Native 层通过反射的方式创建了上层的 Face 对象并返回给上层的 FaceTracker后者可以对外提供 Face 对象以供后续 OpenGL 绘制滤镜所用 fun getFace() mFace3、添加大眼滤镜 前面的工作保证我们能获取到人眼坐标接下来就是使用 OpenGL 在绘制时添加滤镜效果了。 3.1 着色器 顶点着色器使用 base_vertex 即可需要新建一个片元着色器 big_eyes_fragment.glsl // 声明 float 是中等精度的 precision mediump float;// 采样点坐标 varying vec2 aCoord;// 采样器 uniform sampler2D vTexture; // 左眼坐标 uniform vec2 left_eye; // 右眼坐标 uniform vec2 right_eye;// 公式用于计算将眼睛放大后的顶点到放大中心的距离 // r 是未放大前顶点坐标到眼睛中心的距离 // rmax 是放大后顶点到眼睛中心的最大距离 float fs(float r, float rmax) {// 放大系数float a 0.4;// pow 是内置函数用于计算幂次虽然是计算二次方但是也要写为 2.0return (1.0 - pow(r / rmax - 1.0, 2.0) * a) * r; }// 计算放大后的点的坐标 // coord 原来的点eye 眼睛坐标rmax 放大后的最大距离 vec2 calNewCoord(vec2 oldCoord, vec2 eye, float rmax) {vec2 newCoord oldCoord;// 原来的点到眼睛中心的距离float dis distance(oldCoord, eye);// 未到最大距离可以进行放大if (dis 0.0f dis rmax) {// 求出放大后的点到眼睛的距离float fsr fs(dis, rmax);// 按比例计算新点坐标(新点 - 眼睛) / (旧点 - 眼睛) 放大后距离 / 放大前距离// 即 (newCoord - eye) / (coord - eye) fsr / disnewCoord eye (oldCoord - eye) * (fsr / dis);}return newCoord; }void main() {// 两眼间距离除以 2 就是放大后的最大距离float rmax distance(left_eye, right_eye) / 2.0;// 获取左右眼放大后的坐标左眼和右眼都要做一次在哪个眼睛的放大区间就放大哪一个vec2 newCoord calNewCoord(aCoord, left_eye, rmax);// 注意第一个参数要传 newCoord如果传了 aCoord 那么就只判断了右眼newCoord calNewCoord(newCoord, right_eye, rmax);gl_FragColor texture2D(vTexture, newCoord); }fs 函数中的公式来自于 1979 年的一篇论文 “Interactive Image Warping” 的第 41 页 用该公式可以计算出放大后的点到眼睛的距离当然这个距离不能大于 rmax也就是两眼间距的一半。计算出距离后可以通过 calNewCoord() 计算出放大后点的坐标然后就可以让 OpenGL 进行绘制了。 3.2 实现滤镜 大眼滤镜包括我们后续要实现的美颜滤镜和贴纸它们都是绘制在 FBO 上的而不是直接渲染到屏幕上因此可以抽出一个基类将 FBO 的共同操作放入其中 /*** 使用 FBO 绘制的 Filter 基类*/ open class BaseFrameFilter(context: Context, mVertexSourceId: Int, mFragmentSourceId: Int) :BaseFilter(context, mVertexSourceId, mFragmentSourceId) {protected var mFrameBuffers: IntArray? nullprotected var mFrameBufferTextures: IntArray? nulloverride fun onReady(width: Int, height: Int) {super.onReady(width, height)// 1.先清空 mFrameBuffers 的残留数据mFrameBuffers?.let {releaseFrameBuffer()}// 2.创建 FBOmFrameBuffers IntArray(1)// FBO 个数、保存 FBO ID 的数组、偏移量用数组的第几个来保存glGenFramebuffers(mFrameBuffers?.size ?: 1, mFrameBuffers, 0)// 3.创建 FBO 的纹理mFrameBufferTextures IntArray(1)TextureHelper.generateTextures(mFrameBufferTextures!!)// 4.绑定 FBO 与纹理// 将 FBO 纹理 ID 绑定到 GL_TEXTURE_2D 目标上目标的类型是 2D 纹理glBindTexture(GL_TEXTURE_2D, mFrameBufferTextures!![0])// 更新纹理图像数据glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null)// 绑定 FBOglBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffers!![0])// 将纹理附加到 FBOglFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,mFrameBufferTextures!![0],0)// 5.解绑glBindFramebuffer(GL_FRAMEBUFFER, 0)glBindTexture(GL_TEXTURE_2D, 0)}override fun release() {super.release()releaseFrameBuffer()}protected fun releaseFrameBuffer() {mFrameBufferTextures?.let {glDeleteTextures(it.size, mFrameBufferTextures, 0)mFrameBufferTextures null}mFrameBuffers?.let {glDeleteFramebuffers(it.size, mFrameBuffers, 0)mFrameBuffers null}} }大眼滤镜 BigEyesFilter 直接继承该基类 class BigEyesFilter(context: Context) :BaseFrameFilter(context, R.raw.base_vertex, R.raw.big_eyes_fragment) {private val leftEye: Intprivate val rightEye: Intprivate var leftBuffer: FloatBufferprivate var rightBuffer: FloatBufferprivate var face: Face? nullinit {leftEye glGetUniformLocation(mProgramId, left_eye)rightEye glGetUniformLocation(mProgramId, right_eye)leftBuffer ByteBuffer.allocateDirect(2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer()rightBuffer ByteBuffer.allocateDirect(2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer()}override fun initCoordinator() {// 转 180° 调正val texture floatArrayOf(0.0f, 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f)mTextureBuffer.clear()mTextureBuffer.put(texture)}fun setFace(face: Face?) {this.face face}override fun onDrawFrame(textureId: Int): Int {// 1.判断不符合绘制条件的情况直接返回上一层的纹理 IDval landmarks face?.landmarksval imgWidth face?.imgWidthval imgHeight face?.imgHeightif (imgWidth null || imgHeight null || landmarks null) {return textureId}// 2.绘制前设置设置视窗、绑定 FBO、使用着色器程序glViewport(0, 0, mWidth, mHeight)glBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffers!![0])glUseProgram(mProgramId)// 3.设置顶点坐标和纹理坐标mVertexBuffer.position(0)glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, mVertexBuffer)glEnableVertexAttribArray(vPosition)mTextureBuffer.position(0)glVertexAttribPointer(vCoord, 2, GL_FLOAT, false, 0, mTextureBuffer)glEnableVertexAttribArray(vCoord)// 4.将眼睛坐标传给片元着色器var x landmarks[2] / imgWidthvar y landmarks[3] / imgHeightleftBuffer.clear()leftBuffer.put(x)leftBuffer.put(y)leftBuffer.position(0)glUniform2fv(leftEye, 1, leftBuffer)// 右眼坐标x landmarks[4] / imgWidthy landmarks[5] / imgHeightrightBuffer.clear()rightBuffer.put(x)rightBuffer.put(y)rightBuffer.position(0)glUniform2fv(rightEye, 1, rightBuffer)// 5.后续常规操作OpenGL 绘制// 激活图层glActiveTexture(GL_TEXTURE0)// 绑定glBindTexture(GL_TEXTURE_2D, textureId)// 传递参数glUniform1i(vTexture, 0)// 通知 OpenGL 绘制glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)// 解绑 FBOglBindFramebuffer(GL_FRAMEBUFFER, 0)glBindTexture(GL_TEXTURE_2D, 0)return mFrameBufferTextures!![0]} }核心思路就是从 Face 中提取出左眼和右眼的坐标传递给片元着色器 big_eyes_fragment 中定义的两个变量 left_eye 和 right_eye。 3.3 装配大眼滤镜 在渲染器中创建 BigEyesFilter 并将其添加到绘制的责任链中 private lateinit var mBigEyesFilter: BigEyesFilteroverride fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {...// 3.创建滤镜对象mScreenFilter ScreenFilter(mGLSurfaceView.context)mCameraFilter CameraFilter(mGLSurfaceView.context)mBigEyesFilter BigEyesFilter(mGLSurfaceView.context)}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {...// 设置 OpenGL 的绘制视窗mCameraFilter.onReady(width, height)mBigEyesFilter.onReady(width, height)mScreenFilter.onReady(width, height)...}override fun onDrawFrame(gl: GL10?) {...// 3.交给滤镜进行具体的绘制工作mCameraFilter.setMatrix(mMatrix)var textureId mCameraFilter.onDrawFrame(mTextureIds[0])mBigEyesFilter.setFace(mFaceTracker.getFace())textureId mBigEyesFilter.onDrawFrame(textureId)mScreenFilter.onDrawFrame(textureId)}
http://www.ho-use.cn/article/10819918.html

相关文章:

  • 编程网站项目做哪个比较好做网站怎么切psd图
  • 网站被抄袭怎么办网站图片处理方案
  • 赶集网站建设多少钱微商城怎么开
  • 凡科网站官网百度一下网页
  • 外贸cms 网站wordpress4.5 ueditor 1.4.3
  • 免费公司注册网站吗深圳网站建设服务中心官网
  • 深圳中小企业网站建设帝国cms手机网站模板
  • 杭州 网站定制asp.net网站运行助手
  • 深圳门户网站wordpress软件下载主题
  • 如何破解网站后台密码加强服务保障满足群众急需ruu7
  • 百度网站地图提交网站做pc
  • 如何制作餐馆网站网站建设技巧亅金手指排名25
  • 茂名网站建设维护口碑好的定制网站建设制作商
  • 公司网站建设服务公司企业网络推广网站建设
  • 塘厦镇住房规划建设局网站微信怎么申请小程序
  • 淘客网站免费开源源码应用下载app排行榜
  • 商城网站设计服务网站代码的重点内容是什么
  • 网站维护和推广中国建设银行网站暑假工报名
  • 天津模板建站哪家好建设银行上海黄浦支行网站
  • 网站开发经理旅游网站开发背景意义
  • 网站建设费用明细报价网页设计图片为什么显示不出来
  • 闵行区网站建设公司wordpress 招聘公司模版
  • 微信小程序网站制作怎样做网站平台赚钱吗
  • 做企业网站 asp的cms系统哪个好wordpress新闻去掉
  • php做的网站怎么入侵在线注销营业执照
  • 旅游类作业网站建筑网站接单
  • 东莞锂电池网站建设广州软件定制公司
  • 做一个个人主页的网站怎么做wordpress列表模板
  • 广东圆心科技网站开发需要多少钱企业员工培训课程有哪些
  • 做简单网站代码宜都网站制作