int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);

    QGuiApplication app(argc, argv);
    Q_UNUSED(app);

    QSurfaceFormat format;
    format.setMajorVersion(3);
    format.setMinorVersion(0);

    QOpenGLContext context;
    context.setFormat(format);
    context.create();
    if (!context.isValid()) return 1;
    qDebug() << QString::fromLatin1("Context created.");

    QOffscreenSurface surface;
    surface.setFormat(format);
    surface.create();
    if(!surface.isValid()) return 2;
    qDebug() << QString::fromLatin1("Surface created.");

    context.makeCurrent(&surface);

    return RUN_ALL_TESTS();
}
Example #2
0
void QEglFSWindow::create()
{
    if (m_flags.testFlag(Created))
        return;

    QEGLPlatformWindow::create();

    m_flags = Created;

    if (window()->type() == Qt::Desktop)
        return;

    // Stop if there is already a window backed by a native window and surface. Additional
    // raster windows will not have their own native window, surface and context. Instead,
    // they will be composited onto the root window's surface.
    QEglFSScreen *screen = this->screen();
    if (screen->primarySurface() != EGL_NO_SURFACE) {
        if (isRaster() && screen->compositingWindow())
            return;

#if !defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)
        // We can have either a single OpenGL window or multiple raster windows.
        // Other combinations cannot work.
        qFatal("EGLFS: OpenGL windows cannot be mixed with others.");
#endif

        return;
    }

    m_flags |= HasNativeWindow;
    setGeometry(QRect()); // will become fullscreen
    QWindowSystemInterface::handleExposeEvent(window(), geometry());

    EGLDisplay display = static_cast<QEglFSScreen *>(screen)->display();
    QSurfaceFormat platformFormat = QEglFSHooks::hooks()->surfaceFormatFor(window()->requestedFormat());
    m_config = QEglFSIntegration::chooseConfig(display, platformFormat);
    m_format = q_glFormatFromConfig(display, m_config, platformFormat);

    resetSurface();

    screen->setPrimarySurface(m_surface);

    if (isRaster()) {
        QOpenGLContext *context = new QOpenGLContext(QGuiApplication::instance());
        context->setFormat(window()->requestedFormat());
        context->setScreen(window()->screen());
        if (!context->create())
            qFatal("EGLFS: Failed to create compositing context");
        screen->setRootContext(context);
        screen->setRootWindow(this);
    }
}
Example #3
0
static QSurfaceFormat* getFirstSupported(std::vector<QSurfaceFormat> &formats)
{
  QOpenGLContext context;
  for (QSurfaceFormat &format : formats)
  {
    context.setFormat(format);
    if (context.create())
    {
      if (checkVersion(context, format)) return &format;
    }
  }
  return NULL;
}
Example #4
0
void TestWindow::initGl() {
    _glContext.setFormat(format());
    if (!_glContext.create() || !_glContext.makeCurrent(this)) {
        qFatal("Unable to intialize Window GL context");
    }
    gl::initModuleGl();

    _glf.initializeOpenGLFunctions();
    _glf.glGenFramebuffers(1, &_fbo);

    if (!_sharedContext.create(&_glContext) || !_sharedContext.makeCurrent()) {
        qFatal("Unable to intialize Shared GL context");
    }
    hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext());
    _discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda();
}
Example #5
0
void Thumbnail::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *widget)
{
	QRectF parentRect = parentItem()->sceneBoundingRect();

	// Skip drawing thumbnails outside their parents
	if (!sceneBoundingRect().intersects(parentRect)) return;

    prePaint(painter, widget);

    // Draw image
    if(!img.isNull())
    {
        auto imgRect = QRectF(img.rect());
        imgRect.moveCenter(rect.center());
        painter->drawImage(imgRect.topLeft(), img);
    }

    // Draw 3D mesh
    if(mesh.points.size() || auxMeshes.size())
	{
        if (img.isNull() || isTempImage)
		{
			auto glwidget = (Viewer*)widget;
			if (glwidget)
			{
				QOpenGLContext context;
				context.setShareContext(glwidget->context());
				context.setFormat(glwidget->format());
				context.create();

				QOffscreenSurface m_offscreenSurface;
				m_offscreenSurface.setFormat(context.format());
				m_offscreenSurface.create();

				context.makeCurrent(&m_offscreenSurface);

				QOpenGLFramebufferObjectFormat fboformat;
				fboformat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
				QOpenGLFramebufferObject renderFbo(rect.width() * 2, rect.height() * 2, fboformat);
				renderFbo.bind();

				glwidget->glEnable(GL_DEPTH_TEST);
				glwidget->glEnable(GL_BLEND);
				glwidget->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				glwidget->glCullFace(GL_BACK);

				glwidget->glClearColor(0,0,0,0);
				glwidget->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
				glwidget->glViewport(0, 0, renderFbo.size().width(), renderFbo.size().height());
				
                if(mesh.points.size())
                    glwidget->drawTriangles(mesh.color, mesh.points, mesh.normals, pvm);

				// Draw aux meshes
				for (auto auxmesh : auxMeshes)
					glwidget->drawTriangles(auxmesh.color, auxmesh.points, auxmesh.normals, pvm);
				
				glwidget->glDisable(GL_DEPTH_TEST);
				glwidget->glFlush();

				renderFbo.release();
                this->setImage( renderFbo.toImage().scaledToWidth(rect.width(), Qt::SmoothTransformation) );

				// Thanks for sharing!
				glwidget->makeCurrent();
			}

			/*painter->beginNativePainting();

			auto glwidget = (Viewer*)widget;
			if (glwidget)
			{
				// Draw mesh
				auto r = sceneBoundingRect();
				auto v = scene()->views().first();
				QPoint viewDelta = v->mapFromScene(r.topLeft());
				if (viewDelta.manhattanLength() > 5) r.moveTopLeft(viewDelta);

				glwidget->eyePos = eye;
				glwidget->pvm = pvm;
				glwidget->glViewport(r.left(), v->height() - r.height() - r.top(), r.width(), r.height());

				// Clipping OpenGL
				glwidget->glEnable(GL_SCISSOR_TEST);
				glwidget->glScissor(parentRect.x(), v->height() - parentRect.height() - parentRect.top(), parentRect.width(), parentRect.height());

				glwidget->glClear(GL_DEPTH_BUFFER_BIT);
				glwidget->drawTriangles(mesh.color, mesh.points, mesh.normals, pvm);

				// Draw aux meshes
				for (auto auxmesh : auxMeshes)
				{
					glwidget->drawTriangles(auxmesh.color, auxmesh.points, auxmesh.normals, pvm);
				}

				glwidget->glDisable(GL_SCISSOR_TEST);
			}

			painter->endNativePainting();*/
		}        
    }
    // Draw the caption
    if(caption.size())
    {
        painter->setPen(QPen(Qt::white,1));
        auto textRect = rect;
        textRect.setHeight(painter->fontMetrics().height() * 1.25);
        textRect.moveBottom(rect.height() - textRect.height() * 0.5);
        painter->drawText(textRect, caption, Qt::AlignVCenter | Qt::AlignHCenter);
    }

    postPaint(painter, widget);
}
Example #6
0
bool OculusWin32DisplayPlugin::start() {
    {
        ovrInitParams initParams; memset(&initParams, 0, sizeof(initParams));
#ifdef DEBUG
        initParams.Flags |= ovrInit_Debug;
#endif
        ovrResult result = ovr_Initialize(&initParams);
        Q_ASSERT(OVR_SUCCESS(result));
        result = ovr_Create(&_hmd, &_luid);
		_hmdDesc = ovr_GetHmdDesc(_hmd);

        for_each_eye([&](ovrEyeType eye) {
            _eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
            _eyeProjections[eye] = toGlm(ovrMatrix4f_Projection(_eyeFovs[eye],
                0.01f, 100.0f, ovrProjection_RightHanded));
            ovrEyeRenderDesc erd = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
            _eyeOffsets[eye] = erd.HmdToEyeViewOffset;
        });
        _eyeTextureSize = ovr_GetFovTextureSize(_hmd, ovrEye_Left, _eyeFovs[ovrEye_Left], 1.0f);
        _renderTargetSize = { _eyeTextureSize.w * 2, _eyeTextureSize.h };

        ovr_ConfigureTracking(_hmd,
            ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection,
            ovrTrackingCap_Orientation);

    }



    Q_ASSERT(!_window);
    Q_ASSERT(_shareContext);
    _window = new QWindow;
    _window->setSurfaceType(QSurface::OpenGLSurface);
    _window->setFormat(getDesiredSurfaceFormat());
    _context = new QOpenGLContext;
    _context->setFormat(getDesiredSurfaceFormat());
    _context->setShareContext(_shareContext);
    _context->create();

    _window->show();
    _window->setGeometry(QRect(100, -800, 1280, 720));

    bool result = _context->makeCurrent(_window);

#if defined(Q_OS_WIN)
    if (wglewGetExtension("WGL_EXT_swap_control")) {
        wglSwapIntervalEXT(0);
        int swapInterval = wglGetSwapIntervalEXT();
        qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
    } 
#elif defined(Q_OS_LINUX)
#else
    qCDebug(interfaceapp, "V-Sync is FORCED ON on this system\n");
#endif

    Q_ASSERT(result);
    {
        // The geometry and shader for rendering the 2D UI surface when needed
        _program = oria::loadProgram(
            Resource::SHADERS_TEXTURED_VS,
            Resource::SHADERS_TEXTURED_FS);
        _quad = oria::loadPlane(_program, 1.0f);

        glClearColor(0, 1, 1, 1);
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        _mirrorFbo = new MirrorFramebufferWrapper(_hmd);
        _sceneSwapFbo = new SwapFramebufferWrapper(_hmd);
        _sceneSwapFbo->Init(toGlm(_renderTargetSize));
        _uiSwapFbo = new SwapFramebufferWrapper(_hmd);
        _uiSwapFbo->Init(_uiSize);
        _mirrorFbo->Init(uvec2(100, 100));
        _context->doneCurrent();
    }

    _sceneLayer.ColorTexture[0] = _sceneSwapFbo->color;
    _sceneLayer.ColorTexture[1] = nullptr;
    _sceneLayer.Viewport[0].Pos = { 0, 0 };
    _sceneLayer.Viewport[0].Size = _eyeTextureSize;
    _sceneLayer.Viewport[1].Pos = { _eyeTextureSize.w, 0 };
    _sceneLayer.Viewport[1].Size = _eyeTextureSize;
    _sceneLayer.Header.Type = ovrLayerType_EyeFov;
    _sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
    for_each_eye([&](ovrEyeType eye) {
        _eyeViewports[eye] = _sceneLayer.Viewport[eye];
        _sceneLayer.Fov[eye] = _eyeFovs[eye];
    });

    _uiLayer.ColorTexture = _uiSwapFbo->color;
    _uiLayer.Header.Type = ovrLayerType_QuadInWorld;
    _uiLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
    _uiLayer.QuadPoseCenter.Orientation = { 0, 0, 0, 1 };
    _uiLayer.QuadPoseCenter.Position = { 0, 0, -1 }; 
    _uiLayer.QuadSize = { aspect(_uiSize), 1.0f };
    _uiLayer.Viewport = { { 0, 0 }, { (int)_uiSize.x, (int)_uiSize.y } };

    _timer.start(0);

    connect(_window, &QWindow::widthChanged, this, &OculusWin32DisplayPlugin::resizedMirror);
    connect(_window, &QWindow::heightChanged, this, &OculusWin32DisplayPlugin::resizedMirror);
    resizedMirror();
    return true;
}
void ExploreLiveView::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * widget)
{
    if(!isReady) return;

	prePaint(painter);

	postPaint(painter);

	auto glwidget = (Viewer*)widget;
    if (glwidget && meshes.size())
	{
		QRectF parentRect = parentItem()->sceneBoundingRect();

        if (isCacheImage && cachedImage.isNull())
        {
            QOpenGLContext context;
            context.setShareContext(glwidget->context());
            context.setFormat(glwidget->format());
            context.create();

            QOffscreenSurface m_offscreenSurface;
            m_offscreenSurface.setFormat(context.format());
            m_offscreenSurface.create();

            context.makeCurrent(&m_offscreenSurface);

            QOpenGLFramebufferObjectFormat fboformat;
            fboformat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
            QOpenGLFramebufferObject renderFbo(cacheImageSize*1.5, cacheImageSize, fboformat);
            renderFbo.bind();

            glwidget->glEnable(GL_DEPTH_TEST);
            glwidget->glEnable(GL_BLEND);
            glwidget->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glwidget->glCullFace(GL_BACK);

            glwidget->glClearColor(0,0,0,0);
            glwidget->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glwidget->glViewport(0, 0, cacheImageSize*1.5, cacheImageSize);

	// XXX Fix
            // glwidget->glPointSize(10);	

            // Draw aux meshes
            for (auto mesh : meshes)
            {
                if (mesh.isPoints)
                    glwidget->drawOrientedPoints(mesh.points, mesh.normals, mesh.color, glwidget->pvm);
                else
                    glwidget->drawTriangles(mesh.color, mesh.points, mesh.normals, glwidget->pvm);
            }

            glwidget->glDisable(GL_DEPTH_TEST);
            glwidget->glFlush();

            renderFbo.release();

            cachedImage = renderFbo.toImage();
            isReady = true;

            // Thanks for sharing!
            glwidget->makeCurrent();
        }

        // Draw as image
        if(isCacheImage)
        {
            int w = shapeRect.width();
            painter->drawImage(w * -0.5, w * -0.5, cachedImage.scaledToWidth(w));
        }

        if(!isCacheImage)
        {
            auto r = shapeRect;

            // scale view
            double s = 1.5;
            r.setWidth(r.width() * s);
            r.setHeight(r.height() * s);

            r.moveCenter(this->mapToScene(boundingRect().center()));

            painter->beginNativePainting();

            auto v = scene()->views().first();
            QPoint viewDelta = v->mapFromScene(r.topLeft());
            if (viewDelta.manhattanLength() > 5) r.moveTopLeft(viewDelta);

            auto camera = ExploreProcess::defaultCamera(document->extent().length());

            glwidget->eyePos = camera.first;
            glwidget->pvm = camera.second;
            glwidget->glViewport(r.left(), v->height() - r.height() - r.top(), r.width(), r.height());

            // Clipping OpenGL
            glwidget->glEnable(GL_SCISSOR_TEST);
            glwidget->glScissor(parentRect.x(), v->height() - parentRect.height() - parentRect.top(), parentRect.width(), parentRect.height());

            glwidget->glClear(GL_DEPTH_BUFFER_BIT);

	// FIX  XXX
            // glwidget->glPointSize(2);

            // Draw aux meshes
            for (auto mesh : meshes)
            {
                if (mesh.isPoints)
                    glwidget->drawOrientedPoints(mesh.points, mesh.normals, mesh.color, glwidget->pvm);
                else
                    glwidget->drawTriangles(mesh.color, mesh.points, mesh.normals, glwidget->pvm);
            }

            glwidget->glDisable(GL_SCISSOR_TEST);

            painter->endNativePainting();
        }
	}
}
Example #8
0
int main(int argc, char* argv[])
{
	// Parse command line arguments
    std::vector<string> input_paths, seg_paths;
	string output_path, landmarks_path;
	string model_3dmm_h5_path, model_3dmm_dat_path;
	string reg_model_path, reg_deploy_path, reg_mean_path;
	string seg_model_path, seg_deploy_path;
    string cfg_path;
    bool generic, with_expr, with_gpu;
    unsigned int gpu_device_id, verbose;
	try {
		options_description desc("Allowed options");
		desc.add_options()
			("help,h", "display the help message")
            ("verbose,v", value<unsigned int>(&verbose)->default_value(0), "output debug information [0, 4]")
			("input,i", value<std::vector<string>>(&input_paths)->required(), "image paths [source target]")
			("output,o", value<string>(&output_path)->required(), "output path")
            ("segmentations,s", value<std::vector<string>>(&seg_paths), "segmentation paths [source target]")
			("landmarks,l", value<string>(&landmarks_path)->required(), "path to landmarks model file")
            ("model_3dmm_h5", value<string>(&model_3dmm_h5_path)->required(), "path to 3DMM file (.h5)")
            ("model_3dmm_dat", value<string>(&model_3dmm_dat_path)->required(), "path to 3DMM file (.dat)")
            ("reg_model,r", value<string>(&reg_model_path)->required(), "path to 3DMM regression CNN model file (.caffemodel)")
            ("reg_deploy,d", value<string>(&reg_deploy_path)->required(), "path to 3DMM regression CNN deploy file (.prototxt)")
            ("reg_mean,m", value<string>(&reg_mean_path)->required(), "path to 3DMM regression CNN mean file (.binaryproto)")
			("seg_model", value<string>(&seg_model_path), "path to face segmentation CNN model file (.caffemodel)")
			("seg_deploy", value<string>(&seg_deploy_path), "path to face segmentation CNN deploy file (.prototxt)")
            ("generic,g", value<bool>(&generic)->default_value(false), "use generic model without shape regression")
            ("expressions,e", value<bool>(&with_expr)->default_value(true), "with expressions")
			("gpu", value<bool>(&with_gpu)->default_value(true), "toggle GPU / CPU")
			("gpu_id", value<unsigned int>(&gpu_device_id)->default_value(0), "GPU's device id")
            ("cfg", value<string>(&cfg_path)->default_value("face_swap_image.cfg"), "configuration file (.cfg)")
			;
		variables_map vm;
		store(command_line_parser(argc, argv).options(desc).
			positional(positional_options_description().add("input", -1)).run(), vm);

        if (vm.count("help")) {
            cout << "Usage: face_swap_image [options]" << endl;
            cout << desc << endl;
            exit(0);
        }

        // Read config file
        std::ifstream ifs(vm["cfg"].as<string>());
        store(parse_config_file(ifs, desc), vm);

        notify(vm);

        if(input_paths.size() != 2) throw error("Both source and target must be specified in input!");
        if (!is_regular_file(input_paths[0])) throw error("source input must be a path to an image!");
        if (!is_regular_file(input_paths[1])) throw error("target input target must be a path to an image!");
        if (seg_paths.size() > 0 && !is_regular_file(seg_paths[0]))
            throw error("source segmentation must be a path to an image!");
        if (seg_paths.size() > 1 && !is_regular_file(seg_paths[1]))
            throw error("target segmentation must be a path to an image!");
		if (!is_regular_file(landmarks_path)) throw error("landmarks must be a path to a file!");
        if (!is_regular_file(model_3dmm_h5_path)) throw error("model_3dmm_h5 must be a path to a file!");
        if (!is_regular_file(model_3dmm_dat_path)) throw error("model_3dmm_dat must be a path to a file!");
        if (!is_regular_file(reg_model_path)) throw error("reg_model must be a path to a file!");
        if (!is_regular_file(reg_deploy_path)) throw error("reg_deploy must be a path to a file!");
        if (!is_regular_file(reg_mean_path)) throw error("reg_mean must be a path to a file!");
		if (!seg_model_path.empty() && !is_regular_file(seg_model_path))
			throw error("seg_model must be a path to a file!");
		if (!seg_deploy_path.empty() && !is_regular_file(seg_deploy_path))
			throw error("seg_deploy must be a path to a file!");
	}
	catch (const error& e) {
        cerr << "Error while parsing command-line arguments: " << e.what() << endl;
        cerr << "Use --help to display a list of options." << endl;
		exit(1);
	}

	try
	{
        // Intialize OpenGL context
        QApplication a(argc, argv);

        QSurfaceFormat surfaceFormat;
        surfaceFormat.setMajorVersion(1);
        surfaceFormat.setMinorVersion(5);

        QOpenGLContext openGLContext;
        openGLContext.setFormat(surfaceFormat);
        openGLContext.create();
        if (!openGLContext.isValid()) return -1;

        QOffscreenSurface surface;
        surface.setFormat(surfaceFormat);
        surface.create();
        if (!surface.isValid()) return -2;

        openGLContext.makeCurrent(&surface);

        // Initialize GLEW
        GLenum err = glewInit();
        if (GLEW_OK != err)
        {
            // Problem: glewInit failed, something is seriously wrong
            fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
            throw std::runtime_error("Failed to initialize GLEW!");
        }

        // Initialize face swap
        face_swap::FaceSwap fs(landmarks_path, model_3dmm_h5_path, model_3dmm_dat_path,
            reg_model_path, reg_deploy_path, reg_mean_path, generic, with_expr,
			with_gpu, (int)gpu_device_id);

        // Read source and target images
        cv::Mat source_img = cv::imread(input_paths[0]);
        cv::Mat target_img = cv::imread(input_paths[1]);

        // Read source and target segmentations or initialize segmentation model
        cv::Mat source_seg, target_seg;
		if (seg_model_path.empty() || seg_deploy_path.empty())
		{
			if (seg_paths.size() > 0) 
				source_seg = cv::imread(seg_paths[0], cv::IMREAD_GRAYSCALE);
			if (seg_paths.size() > 1) 
				target_seg = cv::imread(seg_paths[1], cv::IMREAD_GRAYSCALE);
		}
		else fs.setSegmentationModel(seg_model_path, seg_deploy_path);

        // Set source and target
        if (!fs.setSource(source_img, source_seg))
            throw std::runtime_error("Failed to find faces in source image!");
        if (!fs.setTarget(target_img, target_seg))
            throw std::runtime_error("Failed to find faces in target image!");

        // Do face swap
        cv::Mat rendered_img = fs.swap();
        if (rendered_img.empty())
            throw std::runtime_error("Face swap failed!");

        // Write output to file
        path out_file_path = output_path;
		path out_dir_path = output_path;
        if (is_directory(output_path))
        {
            path outputName = (path(input_paths[0]).stem() += "_") += 
                (path(input_paths[1]).stem() += ".jpg");
			out_file_path = path(output_path) /= outputName;
        }
		else out_dir_path = path(output_path).parent_path();
        cv::imwrite(out_file_path.string(), rendered_img);

        // Debug
        if (verbose > 0)
        {
			// Write rendered image
			path debug_render_path = out_dir_path /
				(out_file_path.stem() += "_render.jpg");
			cv::Mat debug_render_img = fs.debugRender();
			cv::imwrite(debug_render_path.string(), debug_render_img); 
        }
		if (verbose > 1)
		{
			// Write projected meshes
			path debug_src_mesh_path = out_dir_path /
				(out_file_path.stem() += "_src_mesh.jpg");
			cv::Mat debug_src_mesh_img = fs.debugSourceMesh();
			cv::imwrite(debug_src_mesh_path.string(), debug_src_mesh_img);

			path debug_tgt_mesh_path = out_dir_path /
				(out_file_path.stem() += "_tgt_mesh.jpg");
			cv::Mat debug_tgt_mesh_img = fs.debugTargetMesh();
			cv::imwrite(debug_tgt_mesh_path.string(), debug_tgt_mesh_img);
		}
		if (verbose > 2)
		{
			// Write landmarks render
			path debug_src_lms_path = out_dir_path /
				(out_file_path.stem() += "_src_landmarks.jpg");
			cv::Mat debug_src_lms_img = fs.debugSourceLandmarks();
			cv::imwrite(debug_src_lms_path.string(), debug_src_lms_img);

			path debug_tgt_lms_path = out_dir_path /
				(out_file_path.stem() += "_tgt_landmarks.jpg");
			cv::Mat debug_tgt_lms_img = fs.debugTargetLandmarks();
			cv::imwrite(debug_tgt_lms_path.string(), debug_tgt_lms_img);
		}
		if (verbose > 3)
		{
			// Write meshes
			path debug_src_ply_path = out_dir_path /
				(out_file_path.stem() += "_src_mesh.ply");
			path debug_tgt_ply_path = out_dir_path /
				(out_file_path.stem() += "_tgt_mesh.ply");
			face_swap::Mesh::save_ply(fs.getSourceMesh(), debug_src_ply_path.string());
			face_swap::Mesh::save_ply(fs.getTargetMesh(), debug_tgt_ply_path.string());
		}
	}
	catch (std::exception& e)
	{
		cerr << e.what() << endl;
		return 1;
	}

	return 0;
}