537 lines
28 KiB
C++
537 lines
28 KiB
C++
#include "time_helper.h"
|
|
#include "window_helper.h"
|
|
#include "cartesian_coordinate_system_helper.h"
|
|
#include "common_utils.h"
|
|
#include "dlib_detector.h"
|
|
#include "style_transfer.h"
|
|
#include "imgwarp_mls_similarity.h"
|
|
#include "imgwarp_mls_rigid.h"
|
|
#include "imgwarp_piecewiseaffine.h"
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include <opencv2/core.hpp>
|
|
|
|
#include <opengl_main.hpp>
|
|
|
|
|
|
|
|
|
|
int xxxmain() {
|
|
// Don't forget to change this path before you start.
|
|
const std::string root = "C:\\Work\\TestDataset\\Videos";
|
|
const std::string drawablePath = "..\\app\\src\\main\\res\\drawable\\";
|
|
//const std::string root = std::filesystem::current_path().string();
|
|
|
|
std::string styleName = "watercolorgirl.png"; // name of a style image from dir root/styles with extension
|
|
std::string videoName = "target6.mp4"; // name of a target video with extension
|
|
const int NNF_patchsize = 1; // voting patch size (0 for no voting)
|
|
const bool transparentBG = false; // choice of background (true = transparent bg, false = target image bg)
|
|
//const bool frontCamera = true; // camera facing
|
|
// *********************************************************************************************************************************
|
|
|
|
std::string styleNameNoExtension = styleName.substr(0, styleName.find_last_of("."));
|
|
|
|
// --- READ STYLE FILES --------------------------------------------------
|
|
cv::Mat styleImg = cv::imread(root + "\\styles\\" + styleName);
|
|
std::ifstream styleLandmarkFile(root + "\\styles\\" + styleNameNoExtension + "_lm.txt");
|
|
cv::Mat styleImgBody_full = cv::imread(drawablePath + styleNameNoExtension + "_body.png", cv::IMREAD_UNCHANGED);
|
|
cv::Mat styleImgExternalMask = cv::imread(drawablePath + styleNameNoExtension + "_mask.png");
|
|
cv::Mat styleImgHairMask = cv::imread(drawablePath + styleNameNoExtension + "_hair_mask.png");
|
|
|
|
// Removing hair from the head mask.
|
|
cv::subtract(styleImgExternalMask, styleImgHairMask, styleImgExternalMask);
|
|
|
|
//cv::Mat styleImgExternalMask = cv::imread(root + "\\styles\\" + styleNameNoExtension + "_mask_mock.png");
|
|
cv::Mat styleImgBackground = cv::imread(drawablePath + styleNameNoExtension + "_bg.png");
|
|
std::string styleLandmarkStr((std::istreambuf_iterator<char>(styleLandmarkFile)), std::istreambuf_iterator<char>());
|
|
styleLandmarkFile.close();
|
|
cv::Mat lookUpCube = loadLookUpCube(root + "\\styles\\" + styleNameNoExtension + "_lut.bytes");
|
|
// -----------------------------------------------------------------------
|
|
cv::Mat stylePosGuide = getGradient(styleImg.cols, styleImg.rows, false); // G_pos
|
|
|
|
cv::Mat styleAppGuide = getAppGuide(styleImg, true); // G_app
|
|
std::vector<cv::Point2i> styleLandmarks = getLandmarkPointsFromString(styleLandmarkStr.c_str());
|
|
|
|
// Circle
|
|
int radius = (styleLandmarks[45].x - styleLandmarks[36].x) * 0.7f; // distance of outer eye corners + 80%
|
|
//styleLandmarks.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(styleLandmarks.begin() + 48, styleLandmarks.begin() + 67)), radius, 45.0));
|
|
styleLandmarks.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(styleLandmarks.begin() + 48, styleLandmarks.begin() + 67)), radius, 90.0));
|
|
//styleLandmarks.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(styleLandmarks.begin() + 48, styleLandmarks.begin() + 67)), radius, 135.0));
|
|
// Add 2 landmarks into the bottom corners - to prevent the body moving during MLS deformation
|
|
//styleLandmarks.push_back(cv::Point2i(0, styleImg.rows)); // left bottom corner
|
|
//styleLandmarks.push_back(cv::Point2i(styleImg.cols, styleImg.rows)); // right bottom corner
|
|
|
|
float x_max = 0.0f;
|
|
float y_max = 0.0f;
|
|
float x_min = 1.0f;
|
|
float y_min = 1.0f;
|
|
|
|
for (int k = 60; k < styleLandmarks.size(); ++k) {
|
|
float x = float(styleLandmarks[k].x) / styleImg.cols;
|
|
float y = -1.0f * (float(styleLandmarks[k].y) / styleImg.rows) + 1.0f;
|
|
|
|
if (x_max < x) x_max = x;
|
|
if (x_min > x) x_min = x;
|
|
if (y_max < y) y_max = y;
|
|
if (y_min > y) y_min = y;
|
|
}
|
|
|
|
// Open a video file
|
|
cv::VideoCapture cap(root + "\\" + videoName);
|
|
if (!cap.isOpened()) {
|
|
std::cout << "Unable to open the video." << std::endl;
|
|
system("pause");
|
|
return 1;
|
|
}
|
|
|
|
// Get the width/height and the FPS of the video
|
|
int width = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
|
|
int height = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
|
|
double FPS = cap.get(cv::CAP_PROP_FPS) / 2;
|
|
|
|
// Create output directory
|
|
std::string outputDirPath = root + "\\out_" + videoName.substr(0, videoName.find_last_of(".")) + "\\" + styleNameNoExtension + "_" + std::to_string(NNF_patchsize) + "nnf_" + std::to_string((int)std::ceil(FPS)) + "fps";
|
|
makeDir(outputDirPath);
|
|
|
|
// Open a video file for writing (the MP4V codec works on OS X and Windows)
|
|
cv::VideoWriter out(outputDirPath + "\\result.mp4", cv::VideoWriter::fourcc('m', 'p', '4', 'v'), FPS, cv::Size(styleImg.cols*3, styleImg.rows));
|
|
if (!out.isOpened()) {
|
|
std::cout << "Error! Unable to open video file for output." << std::endl;
|
|
system("pause");
|
|
return 1;
|
|
}
|
|
|
|
// Load facemark models
|
|
//FacemarkDetector faceDetector("facemark_models\\lbpcascade_frontalface.xml", 1.1, 3, "facemark_models\\lbf_landmarks.yaml");
|
|
DlibDetector faceDetector("facemark_models\\shape_predictor_68_face_landmarks.dat");
|
|
std::pair<cv::Rect, std::vector<cv::Point2i>> faceDetResult; // face and its landmarks
|
|
std::vector<cv::Point2i> targetLandmarks;
|
|
|
|
cv::Mat frame;
|
|
int i = 0;
|
|
|
|
// *********************************************************************************************************************************
|
|
// OPENGL INITIALIZATION
|
|
if (!FB_OpenGL::init()) {
|
|
std::cout << "Something is wrong!" << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
FB_OpenGL::Shader debug_shader = FB_OpenGL::Shader("..\\app\\src\\main\\opengl\\shader\\pass_tex_coords.vert", "..\\app\\src\\main\\opengl\\shader\\pass_tex.frag");
|
|
FB_OpenGL::Shader styleblit_shader = FB_OpenGL::Shader("..\\app\\src\\main\\opengl\\shader\\pass_tex.vert", "..\\app\\src\\main\\opengl\\shader\\styleblit_main.frag");
|
|
FB_OpenGL::Shader blending_shader = FB_OpenGL::Shader("..\\app\\src\\main\\opengl\\shader\\pass_tex.vert", "..\\app\\src\\main\\opengl\\shader\\styleblit_blend.frag");
|
|
FB_OpenGL::Shader mixing_shader = FB_OpenGL::Shader("..\\app\\src\\main\\opengl\\shader\\pass_tex.vert", "..\\app\\src\\main\\opengl\\shader\\blending.frag");
|
|
debug_shader.init();
|
|
styleblit_shader.init();
|
|
blending_shader.init();
|
|
mixing_shader.init();
|
|
|
|
FB_OpenGL::StyblitBlender quad = FB_OpenGL::StyblitBlender(&blending_shader);
|
|
FB_OpenGL::StyblitRenderer styleblit_main = FB_OpenGL::StyblitRenderer(&styleblit_shader);
|
|
FB_OpenGL::Grid grid = FB_OpenGL::Grid(32, 32, 0.0f, 1.0f, 0.0f, 0.5f, &debug_shader); // FIXNUTÉ NATVRDO!
|
|
FB_OpenGL::Grid grid_hair = FB_OpenGL::Grid(32, 32, 0.1f, 0.9f, 0.2f, 0.98f, &debug_shader); // FIXNUTÉ NATVRDO!
|
|
FB_OpenGL::Blending mixer = FB_OpenGL::Blending(&mixing_shader);
|
|
|
|
styleblit_main.setWidthHeight( styleImg.cols, styleImg.rows);
|
|
quad.setWidthHeight(styleImg.cols, styleImg.rows);
|
|
mixer.setWidthHeight(styleImg.cols, styleImg.rows);
|
|
|
|
//GLuint quad_texture = FB_OpenGL::makeTexture(styleImg);
|
|
//quad.setTextureID( &quad_texture );
|
|
|
|
// ************************ OPENGL VARIABLES ******************
|
|
GLuint stylePosGuide_texture = 0;
|
|
GLuint targetPosGuide_texture = 0;
|
|
GLuint styleAppGuide_texture = 0;
|
|
GLuint targetAppGuide_texture = 0;
|
|
GLuint styleImg_texture = 0;
|
|
GLuint lookUpTableTexture = FB_OpenGL::make3DTexture(lookUpCube);
|
|
GLuint faceMask_texture = 0;
|
|
GLuint bodyMask_texture = 0;
|
|
GLuint body_texture = 0;
|
|
GLuint externalMask_texture = 0;
|
|
GLuint background_texture = 0;
|
|
GLuint hairmask_texture = 0;
|
|
|
|
// Setup framebuffer for styleblit to make it render to a texture which we can further process.
|
|
GLuint styleblit_frame_buffer, styleblit_render_buffer_depth_stencil, styleblit_tex_color_buffer;
|
|
FB_OpenGL::makeFrameBuffer(globalOpenglData.w_width, globalOpenglData.w_height, styleblit_frame_buffer, styleblit_render_buffer_depth_stencil, styleblit_tex_color_buffer);
|
|
GLuint blending_frame_buffer, blending_render_buffer_depth_stencil, blending_tex_color_buffer;
|
|
FB_OpenGL::makeFrameBuffer(globalOpenglData.w_width, globalOpenglData.w_height, blending_frame_buffer, blending_render_buffer_depth_stencil, blending_tex_color_buffer);
|
|
GLuint body_frame_buffer, body_render_buffer_depth_stencil, body_tex_color_buffer;
|
|
FB_OpenGL::makeFrameBuffer(globalOpenglData.w_width, globalOpenglData.w_height, body_frame_buffer, body_render_buffer_depth_stencil, body_tex_color_buffer);
|
|
GLuint bodymask_frame_buffer, bodymask_render_buffer_depth_stencil, bodymask_tex_color_buffer;
|
|
FB_OpenGL::makeFrameBuffer(globalOpenglData.w_width, globalOpenglData.w_height, bodymask_frame_buffer, bodymask_render_buffer_depth_stencil, bodymask_tex_color_buffer);
|
|
GLuint facemask_frame_buffer, facemask_render_buffer_depth_stencil, facemask_tex_color_buffer;
|
|
FB_OpenGL::makeFrameBuffer(globalOpenglData.w_width, globalOpenglData.w_height, facemask_frame_buffer, facemask_render_buffer_depth_stencil, facemask_tex_color_buffer);
|
|
GLuint hair_frame_buffer, hair_render_buffer_depth_stencil, hair_tex_color_buffer;
|
|
FB_OpenGL::makeFrameBuffer(globalOpenglData.w_width, globalOpenglData.w_height, hair_frame_buffer, hair_render_buffer_depth_stencil, hair_tex_color_buffer);
|
|
GLuint hairmask_frame_buffer, hairmask_render_buffer_depth_stencil, hairmask_tex_color_buffer;
|
|
FB_OpenGL::makeFrameBuffer(globalOpenglData.w_width, globalOpenglData.w_height, hairmask_frame_buffer, hairmask_render_buffer_depth_stencil, hairmask_tex_color_buffer);
|
|
|
|
|
|
quad.setTextures(&styleblit_tex_color_buffer, &styleImg_texture);
|
|
// GLuint frame_as_texture = 0;
|
|
|
|
// Generate jitter table
|
|
cv::Mat gaussian_noise = cv::Mat::zeros(styleImg.rows, styleImg.cols, CV_8UC2);
|
|
cv::randu(gaussian_noise, 0, 255);
|
|
|
|
GLuint jitter_table_texture = FB_OpenGL::makeJitterTable(gaussian_noise.clone());
|
|
styleblit_main.setJitterTable(&jitter_table_texture);
|
|
|
|
TimeMeasure tm;
|
|
tm.reset();
|
|
|
|
std::vector<int> landmarkControlPointsIDs;
|
|
std::vector<int> hairControlPointsIDs;
|
|
|
|
for (int k = 0; k < 32; ++k) {
|
|
grid.vertexDeformations[k].fixed = true;
|
|
}
|
|
|
|
// Currently used landmarks are the bottom left and bottom right corners, three points of the notional circle, and the mouth facial landmarks.
|
|
for (int k = 0; k < styleLandmarks.size(); ++k) {
|
|
//for (auto landmark : styleLandmarks) {
|
|
cv::Point2i landmark = styleLandmarks[k];
|
|
int cp = grid.getNearestControlPointID( 2.0f * (float(landmark.x) / float(styleImg.cols)) - 1.0f, -1.0f * (2.0f * (float(landmark.y) / float(styleImg.rows)) - 1.0f));
|
|
// grid.vertexDeformations[cp].fixed = true;
|
|
landmarkControlPointsIDs.push_back(cp);
|
|
}
|
|
|
|
std::vector<int> selected_landmark_ids;
|
|
selected_landmark_ids.push_back(30); // nose tip
|
|
selected_landmark_ids.push_back(60); // lip left corner
|
|
selected_landmark_ids.push_back(64); // lip right corner
|
|
selected_landmark_ids.push_back(68); // throat
|
|
|
|
std::vector<cv::Point2i> hair_markers;
|
|
/*hair_markers.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(styleLandmarks.begin() + 48, styleLandmarks.begin() + 67)), radius, 45.0));
|
|
hair_markers.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(styleLandmarks.begin() + 48, styleLandmarks.begin() + 67)), radius, 90.0));
|
|
hair_markers.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(styleLandmarks.begin() + 48, styleLandmarks.begin() + 67)), radius, 135.0));*/
|
|
std::vector<cv::Point2i> toAverage;
|
|
toAverage.push_back(styleLandmarks[36]);
|
|
toAverage.push_back(styleLandmarks[45]);
|
|
radius = (styleLandmarks[45].x - styleLandmarks[36].x) * 1.0f;
|
|
hair_markers.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(toAverage), radius, 270.0));
|
|
hair_markers.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(toAverage), radius, 315.0));
|
|
hair_markers.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(toAverage), radius, 225.0));
|
|
|
|
hair_markers.push_back(styleLandmarks[0]);
|
|
hair_markers.push_back(styleLandmarks[1]);
|
|
hair_markers.push_back(styleLandmarks[2]);
|
|
hair_markers.push_back(styleLandmarks[3]);
|
|
hair_markers.push_back(styleLandmarks[4]);
|
|
hair_markers.push_back(styleLandmarks[5]);
|
|
|
|
hair_markers.push_back(styleLandmarks[11]);
|
|
hair_markers.push_back(styleLandmarks[12]);
|
|
hair_markers.push_back(styleLandmarks[13]);
|
|
hair_markers.push_back(styleLandmarks[14]);
|
|
hair_markers.push_back(styleLandmarks[15]);
|
|
hair_markers.push_back(styleLandmarks[16]);
|
|
|
|
// Currently used landmarks are the bottom left and bottom right corners, three points of the notional circle, and the mouth facial landmarks.
|
|
for (int k = 0; k < hair_markers.size(); ++k) {
|
|
//for (auto landmark : styleLandmarks) {
|
|
cv::Point2i landmark = hair_markers[k];
|
|
int cp = grid_hair.getNearestControlPointID(2.0f * (float(landmark.x) / float(styleImg.cols)) - 1.0f, -1.0f * (2.0f * (float(landmark.y) / float(styleImg.rows)) - 1.0f));
|
|
// grid.vertexDeformations[cp].fixed = true;
|
|
hairControlPointsIDs.push_back(cp);
|
|
}
|
|
// Image processing
|
|
|
|
cv::Mat bodyChannels[4];
|
|
cv::split(styleImgBody_full, bodyChannels);
|
|
|
|
cv::Mat styleImgBody;
|
|
cv::Mat styleImgBodyMask;
|
|
cv::cvtColor(styleImgBody_full, styleImgBody, cv::COLOR_BGRA2BGR);
|
|
cv::cvtColor(bodyChannels[3], styleImgBodyMask, cv::COLOR_GRAY2BGR, 3);
|
|
|
|
/*Window::imgShow("Result", styleImgBodyMask);
|
|
cv::waitKey(0);*/
|
|
|
|
/*std::vector<cv::Mat> mask_vector;
|
|
mask_vector.push_back(bodyChannels[3]);
|
|
mask_vector.push_back(bodyChannels[3]);
|
|
mask_vector.push_back(bodyChannels[3]);
|
|
|
|
cv::merge(mask_vector, styleImgBodyMask);*/
|
|
// Window::imgShow("Result", styleImgBody);
|
|
|
|
|
|
/*int bot_left = grid.getNearestControlPointID(-1.0f,-1.0f);
|
|
int bot_right = grid.getNearestControlPointID(1.0f, -1.0f);
|
|
grid.vertexDeformations[bot_left].fixed = true;
|
|
grid.vertexDeformations[bot_right].fixed = true;
|
|
grid.vertexDeformations[150].fixed = true; // Debug.*/
|
|
|
|
while (true) {
|
|
cap >> frame;
|
|
if (frame.empty()) {
|
|
std::cout << "No more frames." << std::endl;
|
|
break;
|
|
}
|
|
|
|
if (i % 2 == 1) // skip odd frames when we want the half FPS (not in case of target12!!!)
|
|
{
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
faceDetector.detectFacemarks(frame, faceDetResult);
|
|
std::vector<cv::Point2i> targetLandmarks_temp = targetLandmarks;
|
|
targetLandmarks = faceDetResult.second;
|
|
// alignTargetToStyle(frame, targetLandmarks, styleLandmarks);
|
|
|
|
|
|
|
|
// Add 3 landmarks on the bottom of the notional circle
|
|
radius = (targetLandmarks[45].x - targetLandmarks[36].x) * 1.0f; // distance of outer eye corners + 80%
|
|
//targetLandmarks.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(targetLandmarks.begin() + 48, targetLandmarks.begin() + 67)), radius, 45.0));
|
|
targetLandmarks.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(targetLandmarks.begin() + 48, targetLandmarks.begin() + 67)), radius, 90.0));
|
|
//targetLandmarks.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(std::vector<cv::Point2i>(targetLandmarks.begin() + 48, targetLandmarks.begin() + 67)), radius, 135.0));
|
|
|
|
if (i > 1) {
|
|
targetLandmarks[targetLandmarks.size() - 1].x = (targetLandmarks[targetLandmarks.size() - 1].x + targetLandmarks_temp[targetLandmarks.size() - 1].x) / 2;
|
|
targetLandmarks[targetLandmarks.size() - 1].y = (targetLandmarks[targetLandmarks.size() - 1].y + targetLandmarks_temp[targetLandmarks.size() - 1].y) / 2;
|
|
}
|
|
|
|
//targetLandmarks.push_back(cv::Point2i(0, frame.rows)); // left bottom corner
|
|
//targetLandmarks.push_back(cv::Point2i(frame.cols, frame.rows)); // right bottom corner
|
|
|
|
//std::cout << landmarkControlPointsIDs.size() << " " << targetLandmarks.size() << std::endl;
|
|
//for (int k = 60; k < landmarkControlPointsIDs.size(); ++k) {
|
|
for (int k : selected_landmark_ids) {
|
|
int cp = landmarkControlPointsIDs[k];
|
|
grid.vertexDeformations[cp].fixed = true;
|
|
grid.vertexDeformations[cp].x = 2.0f * (float(targetLandmarks[k].x) / float(frame.cols)) - 1.0f;
|
|
grid.vertexDeformations[cp].y = -1.0 * (2.0f * (float(targetLandmarks[k].y) / float(frame.rows)) - 1.0f);
|
|
}
|
|
|
|
//hair_markers.clear();
|
|
std::vector<cv::Point2i> hair_markers_target;
|
|
toAverage.clear();
|
|
toAverage.push_back(targetLandmarks[36]);
|
|
toAverage.push_back(targetLandmarks[45]);
|
|
radius = (targetLandmarks[45].x - targetLandmarks[36].x) * 0.9f;
|
|
|
|
float angle_of_eyes = cv::fastAtan2(targetLandmarks[45].x - targetLandmarks[36].x, targetLandmarks[45].y - targetLandmarks[36].y);
|
|
// std::cout << angle_of_eyes - 90.0f << std::endl;
|
|
//(targetLandmarks[45].x - targetLandmarks[36].x)/(targetLandmarks[45].y - targetLandmarks[36].y)
|
|
|
|
hair_markers_target.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(toAverage), radius, 270.0 - angle_of_eyes + 90.0f));
|
|
hair_markers_target.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(toAverage), radius, 315.0 - angle_of_eyes + 90.0f));
|
|
hair_markers_target.push_back(CartesianCoordinateSystem::getPointLyingOnCircle(CartesianCoordinateSystem::getAveragePoint(toAverage), radius, 225.0 - angle_of_eyes + 90.0f));
|
|
|
|
|
|
hair_markers_target.push_back(targetLandmarks[0]);
|
|
hair_markers_target.push_back(targetLandmarks[1]);
|
|
hair_markers_target.push_back(targetLandmarks[2]);
|
|
hair_markers_target.push_back(targetLandmarks[3]);
|
|
hair_markers_target.push_back(targetLandmarks[4]);
|
|
hair_markers_target.push_back(targetLandmarks[5]);
|
|
|
|
hair_markers_target.push_back(targetLandmarks[11]);
|
|
hair_markers_target.push_back(targetLandmarks[12]);
|
|
hair_markers_target.push_back(targetLandmarks[13]);
|
|
hair_markers_target.push_back(targetLandmarks[14]);
|
|
hair_markers_target.push_back(targetLandmarks[15]);
|
|
hair_markers_target.push_back(targetLandmarks[16]);
|
|
|
|
for (int k = 0; k < hair_markers_target.size(); ++k) {
|
|
int cp = hairControlPointsIDs[k];
|
|
grid_hair.vertexDeformations[cp].fixed = true;
|
|
grid_hair.vertexDeformations[cp].x = 2.0f * (float(hair_markers_target[k].x) / float(frame.cols)) - 1.0f;
|
|
grid_hair.vertexDeformations[cp].y = -1.0 * (2.0f * (float(hair_markers_target[k].y) / float(frame.rows)) - 1.0f);
|
|
}
|
|
|
|
cv::Mat faceMask = getSkinMask(frame, targetLandmarks);
|
|
|
|
// Generate target guidance channels
|
|
cv::Mat targetPosGuide = MLSDeformation(stylePosGuide, styleLandmarks, targetLandmarks); // G_pos
|
|
cv::Mat targetAppGuide = getAppGuide(frame, false); // G_app
|
|
targetAppGuide = grayHistMatching(targetAppGuide, styleAppGuide);
|
|
|
|
cv::Mat stylizedImg = styleBlit(stylePosGuide, targetPosGuide, styleAppGuide, targetAppGuide, lookUpCube, styleImg, cv::Rect2i(0, 0, frame.cols, frame.rows));
|
|
cv::Mat stylizedMaskCPU = styleBlit(stylePosGuide, targetPosGuide, styleAppGuide, targetAppGuide, lookUpCube, styleImgExternalMask, cv::Rect2i(0, 0, frame.cols, frame.rows));
|
|
|
|
i++;
|
|
|
|
// std::cout << i << std::endl;
|
|
|
|
// Make the Opencv Mat images into OpenGL accepted textures. On the 1st frame create the textures from scratch, after that we only need to update them to save memory.
|
|
if (i > 2) {
|
|
FB_OpenGL::updateTexture(targetPosGuide_texture, targetPosGuide.clone());
|
|
FB_OpenGL::updateTexture(targetAppGuide_texture, targetAppGuide.clone());
|
|
FB_OpenGL::updateTexture(faceMask_texture, faceMask.clone());
|
|
}
|
|
else {
|
|
stylePosGuide_texture = FB_OpenGL::makeTexture(stylePosGuide.clone());
|
|
targetPosGuide_texture = FB_OpenGL::makeTexture(targetPosGuide.clone());
|
|
styleAppGuide_texture = FB_OpenGL::makeTexture(styleAppGuide.clone());
|
|
targetAppGuide_texture = FB_OpenGL::makeTexture(targetAppGuide.clone());
|
|
styleImg_texture = FB_OpenGL::makeTexture(styleImg.clone());
|
|
styleblit_main.setTextures(&stylePosGuide_texture, &targetPosGuide_texture, &styleAppGuide_texture, &targetAppGuide_texture, &styleImg_texture, &lookUpTableTexture);
|
|
faceMask_texture = FB_OpenGL::makeTexture(faceMask.clone());
|
|
bodyMask_texture = FB_OpenGL::makeTexture(styleImgBodyMask.clone());
|
|
body_texture = FB_OpenGL::makeTexture(styleImgBody.clone());
|
|
externalMask_texture = FB_OpenGL::makeTexture(styleImgExternalMask.clone());
|
|
background_texture = FB_OpenGL::makeTexture(styleImgBackground.clone());
|
|
hairmask_texture = FB_OpenGL::makeTexture(styleImgHairMask.clone());
|
|
|
|
}
|
|
FB_OpenGL::updateTexture(blending_tex_color_buffer, stylizedImg.clone());
|
|
FB_OpenGL::updateTexture(facemask_tex_color_buffer, stylizedMaskCPU.clone());
|
|
|
|
// Bind the StyleBlit framebuffer, which will make all operations render in a texture.
|
|
/*glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, styleblit_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
styleblit_main.draw();
|
|
|
|
// ********************************************************************************************************************
|
|
// ***************************************** STYLEBLIT NNF ************************************************************
|
|
|
|
// Re-bind the default framebuffer.
|
|
glBindFramebuffer(GL_FRAMEBUFFER, blending_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
quad.setTextures(&styleblit_tex_color_buffer, &styleImg_texture);
|
|
quad.draw();
|
|
|
|
// *************************************************************************************************************************
|
|
// ***************************************** STYLEBLIT BLENDING ************************************************************
|
|
// Re-bind the default framebuffer.
|
|
glBindFramebuffer(GL_FRAMEBUFFER, facemask_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
quad.setTextures(&styleblit_tex_color_buffer, &externalMask_texture);
|
|
quad.draw();*/
|
|
|
|
|
|
// ***********************************************************************************************************************
|
|
// ***************************************** BODY DEFORMATION ************************************************************
|
|
// Re-bind the default framebuffer.
|
|
glBindFramebuffer(GL_FRAMEBUFFER, body_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// grid.vertexDeformations[150].x = sin(tm.elapsed_milliseconds() / 1000.0f);
|
|
|
|
|
|
grid.setTextureID(&body_texture);
|
|
grid.deformGrid(200);
|
|
grid.draw(); // Draw grid with deformations.
|
|
|
|
|
|
// ****************************************************************************************************************************
|
|
// ***************************************** BODY DEFORMATION MASK ************************************************************
|
|
glBindFramebuffer(GL_FRAMEBUFFER, bodymask_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// grid.vertexDeformations[150].x = sin(tm.elapsed_milliseconds() / 1000.0f);
|
|
|
|
|
|
grid.setTextureID(&bodyMask_texture);
|
|
grid.draw(); // Draw grid with deformations.
|
|
|
|
|
|
// ***********************************************************************************************************************
|
|
// ***************************************** HAIR DEFORMATION ************************************************************
|
|
glBindFramebuffer(GL_FRAMEBUFFER, hair_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// grid.vertexDeformations[150].x = sin(tm.elapsed_milliseconds() / 1000.0f);
|
|
|
|
grid_hair.setTextureID(&styleImg_texture);
|
|
grid_hair.deformGrid(300);
|
|
grid_hair.draw(); // Draw grid with deformations.
|
|
|
|
// ****************************************************************************************************************************
|
|
// ***************************************** HAIR DEFORMATION MASK ************************************************************
|
|
glBindFramebuffer(GL_FRAMEBUFFER, hairmask_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// grid.vertexDeformations[150].x = sin(tm.elapsed_milliseconds() / 1000.0f);
|
|
|
|
grid_hair.setTextureID(&hairmask_texture);
|
|
grid_hair.draw(); // Draw grid with deformations.
|
|
|
|
|
|
// ***************************************************************************************************************************************
|
|
// ***************************************** FINAL MIXING AND PRINT TO SCREEN ************************************************************
|
|
// Re-bind the default framebuffer.
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
mixer.draw(body_tex_color_buffer, blending_tex_color_buffer, bodymask_tex_color_buffer, facemask_tex_color_buffer, background_texture, faceMask_texture, hair_tex_color_buffer, hairmask_tex_color_buffer);
|
|
|
|
|
|
// ******************************************************************************************************************************************
|
|
// ***************************************** FINAL MIXING AND PRINT INTO TEXTURE ************************************************************
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, styleblit_frame_buffer);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glViewport(0, 0, globalOpenglData.w_width, globalOpenglData.w_height);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
mixer.draw(body_tex_color_buffer, blending_tex_color_buffer, bodymask_tex_color_buffer, facemask_tex_color_buffer, background_texture, faceMask_texture, hair_tex_color_buffer, hairmask_tex_color_buffer);
|
|
|
|
SDL_GL_SwapWindow(globalOpenglData.mainWindow);
|
|
|
|
// Uncomment if you want to save the output NNF. Current implementation is leaking a bit, so be careful.
|
|
//if ( i == 99) {
|
|
cv::Mat out_image = FB_OpenGL::get_ocv_img_from_gl_img(styleblit_tex_color_buffer);
|
|
cv::Mat out_image_concat;
|
|
std::vector<cv::Point2i> selected_markers;
|
|
for (auto landmark_id : selected_landmark_ids) {
|
|
selected_markers.push_back(targetLandmarks[landmark_id]);
|
|
}
|
|
//CartesianCoordinateSystem::drawRainbowLandmarks(frame, hair_markers_target);
|
|
cv::hconcat(styleImg, out_image, out_image_concat);
|
|
cv::hconcat(out_image_concat, frame, out_image_concat);
|
|
out << out_image_concat;
|
|
Window::imgShow("Result", out_image_concat);
|
|
//cv::imwrite(root + "\\styles\\" + std::to_string(i) + "_body_stylized.png", out_image);
|
|
//}
|
|
|
|
}
|
|
|
|
return 0;
|
|
} |