3.OpenCLの利用
美顔、ステッカー、フィルターおよびメークアップ効果の実現にOpenGLの利用が必要です。
3.1 OpenGLの初期化
opengl_check.exeを実行し,システムおよびGPUドライバーのopengl32.dllバージョンを確認する
opengl_checkが失敗の場合は、Mesa版のopengl32.dllを用いてOpenGLの初期化を行う
opengl_checkが成功の場合は、glfwライブラリを初期化し、glfwウィンドウを作成する。glfwウィンドウ作成が失敗の場合は、Mesa版のopengl32.dllを用いてOpenGLの初期化を行う
glfwウィンドウの作成が成功したら,gl3wの初期化を行う。gl3wの初期化が失敗シた場合は,
Mesa版のopengl32.dllを用いてOpenGLの初期化を行う
OpenGLのバージョンを確認し,2.1以下またはOpenGLSLバージョンは1.2以下の場合は,エラーで返す
OpenGL初期化の詳細はhelper_opengl.hに参照してください。
inline bool InitGL() {
glfwSetErrorCallback(PrintglfwError);
bool bGLInited = false;
if (!window) {
try {
int err = 0;
#ifdef WIN32
if (opengl_check()) {
#endif
err = glfwInit();
if (!err) {
throw "OpenGL: fail to create window context!";
}
// Create a offscream mode window and its OpenGL context
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
window = glfwCreateWindow(10, 10, "hello", NULL, NULL);
#ifdef WIN32
}
#endif
if (!window) {
#ifdef WIN32
MesaOpenGL();
#else
throw "OpenGL: fail to init glfw";
#endif
}
else {
// Make the window's context current
glfwMakeContextCurrent(window);
#ifdef WIN32
err = gl3wInit();
if (err != GL3W_OK) {
MesaOpenGL();
}
#else
glewExperimental = GL_TRUE;
err = glewInit();
if (err != GLEW_OK) {
throw "OpenGL: fail to init glew\n";
}
#endif
}
const unsigned char* glVer = glGetString(GL_VERSION);
const unsigned char* glslVer = glGetString(GL_SHADING_LANGUAGE_VERSION);
float OpenglVersion = 0.f, GLSLVersion = 0.f;
StringTo((char*)glVer, OpenglVersion);
StringTo((char*)glslVer, GLSLVersion);
fprintf(stdout, "Renderer: %s\n", glGetString(GL_RENDERER));
fprintf(stdout, "OpenGL version supported %s\n", glGetString(GL_VERSION));
fprintf(stdout, "OpenGLSL version supported %s\n",
glGetString(GL_SHADING_LANGUAGE_VERSION));
if (OpenglVersion <2.1f || GLSLVersion < 1.2f) {
throw "OpenGL version is too old, we only support above opengl2.1 and glsl1.2.0";
}
bGLInited = true;
}
catch (std::exception &e){
fprintf(stderr, "%s\n", e.what());
}
catch (const char* e){
fprintf(stderr, "%s\n", e);
}
catch (...) {
return false;
} }
else {
GLFWwindow* curCtx = glfwGetCurrentContext();
if (!curCtx)
glfwMakeContextCurrent(window);
bGLInited = true;
}
return bGLInited;
}
Mesa版opengl32.dllによるOpenGLの初期化は以下の通り。
glfwライブラリの初期化を行い,glfwウィンドウを作成し,gl3wの初期化を行う
opengl32.dllと区別するため、Mesa版opengl32.dllはMesaOpengl32.dllに命名されています。MesaOpenGLが利用されると、自動的にopengl32.dllに名前が変更されます。
inline void MesaOpenGL() {
unInitGL();
char work_path[_MAX_PATH] = { 0 };
GetModuleFileName(NULL, work_path, _MAX_PATH);
std::string str_work_path(work_path);
str_work_path = str_work_path.substr(0, str_work_path.find_last_of('\\') + 1);
#ifdef _WIN64
str_work_path = str_work_path + "..\\external\\libs\\windows-x86_64";
#else
str_work_path = str_work_path + "..\\external\\libs\\windows-x86";
#endif
SetDllDirectory(str_work_path.data());
std::string rename_path = str_work_path + "\\MesaOpengl32.dll";
str_work_path = str_work_path + "\\opengl32.dll";
// judge opengl32.dll exist or not
if (access(str_work_path.data(), 0) == -1) {
// rename and SetDllDirectory are must, because glfw loadlibrary("opengl32.dll")
if (rename(rename_path.data(), str_work_path.data()) != 0)
throw "MESA OpenGL: fail to rename opengl32.dll!";
}
GLenum err = false;
err = glfwInit();
if (!err) {
throw "MESA OpenGL: fail to init glfw";
}
// Create a offscream mode window and its OpenGL context
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
window = glfwCreateWindow(10, 10, "hello", NULL, NULL);
if (!window) {
unInitGL();
throw "MESA OpenGL: fail to create window context!";
}
// Make the window's context current
glfwMakeContextCurrent(window);
//str_work_path = str_work_path + "\\opengl32.dll";
err = gl3wInitWithPath(str_work_path.data());
if (err != GL3W_OK) {
fprintf(stderr, "gl3wInit error: %d\n", err);
throw "MESA OpenGL: fail to init gl3w\n";
}
}
3.2 OpenGLレンダリング効果の利用
3.2.1 テクスチャバンディング
画像を処理する前に、テクスチャにバインドする必要があります。 テクスチャのバインド処理は、次のように行います。
テクスチャインデックスがテクスチャオブジェクトにバインドされていない場合、テクスチャインデックスをテクスチャユニットにバインドし、glTexImage2Dを使用して2Dテクスチャを生成してバインドを完了させます。
後続のビデオフレームでは、glTexSubImage2Dを使用して既存のテクスチャを変更します。
ビデオ解像度が変更された場合は、手順2へ戻ります。
static GLboolean BindTexture(uchar *buffer, int width, int height, GLuint& texId) {
if (!glIsTexture(texId) || (gTextures.find(texId) == gTextures.end())) {
glGenTextures(1, &texId);
std::vector<int> tmp(2);
tmp[0] = width;
tmp[1] = height;
gTextures[texId] = tmp;
}
int &OldW = gTextures[texId][0];
int &OldH = gTextures[texId][1];
if (!glIsTexture(texId) || (OldW != width) || (OldH != height)) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, buffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
OldW = width;
OldH = height;
}
else if(buffer) {
glBindTexture(GL_TEXTURE_2D, texId);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA,
GL_UNSIGNED_BYTE, buffer);
}
bool tmp = glIsTexture(texId);
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
printf("error : %d\n", error);
// result = RESULT_TEXTURE_ERR;
error = GL_NO_ERROR;
return false;
}
return true;
}
process textureインターフェースを使用して、効果をレンダリングする前に入力と出力のテクスチャーをバインドします。
BindTexture(rgba_frame.data, rgba_frame.cols, rgba_frame.rows, texture_src);
BindTexture(NULL, rgba_frame.cols, rgba_frame.rows, texture_dst);
process bufferを使用したエフェクトのレンダリングでは、テクスチャのバインディングは不要で、関数内部で行われます。
3.2.2 エフェクトレンダリング
この効果はOpenGLでprocess textureインターフェイスまたはprocess bufferインターフェイスを使って、次のように描画されます。他の効果については4.4を参照してください:
//process_texture
ret = st_mobile_beautify_process_and_output_texture(handle_beautify, texture_src,
read_frame.cols, read_frame.rows,
ST_CLOCKWISE_ROTATE_0,
&human_action,texture_dst,
temp_frame.data,ST_PIX_FMT_BGR888,
NULL);
//process_buffer
ret = st_mobile_beautify_process_buffer(handle_beautify, temp_frame.data,
ST_PIX_FMT_BGR888, temp_frame.cols,
temp_frame.rows, temp_frame.step,
ST_CLOCKWISE_ROTATE_0, &human_action,
temp_frame.data, ST_PIX_FMT_BGR888, NULL);
3.3 OpenGLのクローズ
プログラムの終了時には、glfw が作成したウィンドウを閉じて、glfwを終了させる必要があります。
inline void unInitGL() {
if (window)
glfwDestroyWindow(window);
window = nullptr;
glfwTerminate();
}