人脸识别示例(四)——人脸融合

效果图

在人脸检测的基础上,基于泊松融合实现了一个简单的人脸融合器,效果如下。

在这里插入图片描述

程序下载地址

使用说明

一:命令行参数
1.把命令行定位在本目录下

2.运行merge -imagePath1 <你的目标图像路径> -imagePath2 <你的源图像路径> -cacadePath <你的cacade分类器路径>
-blackPath <全黑图像路径> -whitePath <全白图像路径>

注意:1).要么不指定参数值,如果需要指定,就按这个顺序指定各个参数值

      2).目录下的data文件复制opencv源码下的data文件,里面包含了opencv训练好的各个分类器
      
      3).目录下的test目录用于放置测试的图片,你也可以指定该目录外的其他图像路径。

二:其他参数设置以及效果显示

  1. 程序运行后,会有一次退出提示,你可以在正式操作人脸融合之前选择退出程序,按“q”或者“Q”退出程序。如果继续按其他字母键。

在这里插入图片描述

  1. 接着会提示设置各个参数

在这里插入图片描述

a) 源脸宽度缩放比例:是指从源图像中提取出的人脸,在复制到目标区域前,脸宽缩放的比例,一般这个比例事0.7左右(这个参数根据源脸脸型和目标脸脸型调整。比如如果源脸脸型比较胖,目标脸型比较窄,可以适当调小这个值;反之,需要调高,最高不得大于1.5)

b) 源脸高度缩放比例:是指从源图像中提取出的人脸,在复制到目标区域前,脸高缩放的比例,一般这个比例事0.8左右(参数调整原理同上。比如源脸脸型比较扁,目标脸型较长,可以适当调高这个值;反之调小)。

c) 源脸宽度裁剪比例;是指在源图像在人脸定位矩形的基础上,为了更精准地位五官,放大或缩小裁剪宽度,一般这个比例是0.6左右。具体效果可以看显示出的“源face”画面。

d) 源脸高度裁剪比例:是指在源图像在人脸定位矩形的基础上,为了更精准地位五官,放大或缩小裁剪高度,一般这个比例是0.8左右。具体效果可以看显示出的“源face”画面。

  1. 显示源图像,激活图像,按回车。继续。

在这里插入图片描述

  1. 显示人脸复制mask(这个可以忽略,调试用)按回车。继续。

在这里插入图片描述

  1. 显示裁剪缩放后的源人脸。按回车。继续

在这里插入图片描述

  1. 显示目标图像。按回车。继续

在这里插入图片描述

  1. 显示融合图像。按回车。继续(此时需要判断对该结果是否满意,如果不满意,想好下一步调整方案)

在这里插入图片描述

三、继续调整,接收结果或退出
1.最后一次按回车将关闭所有窗口,控制台提示是否满意结果。如果满意输入“y”

在这里插入图片描述

2.如果不满意,则将重新循环至“二,参数设置”。(显然上次结果,源脸图像需要调小缩放宽度)
在这里插入图片描述

3,第二次效果

在这里插入图片描述

4.满意,输入y,将提示输入保存文件名

在这里插入图片描述

5.输入文件名“liying_baby”。程序结束。结果将保存在运行目录下的” liying_baby.png”中

源代码

// merge1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define _CRT_SECURE_NO_WARNINGS
#define X_CLIP 0.6
#define Y_CLIP 0.8
#define X_ZOOM 0.7
#define Y_ZOOM 0.8


#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;





int main(int argc, char** argv)
{
	CommandLineParser parser(argc, argv,
		"{@imagePath1|./test/merge/baby.png|}"
		"{@imagePath2|./test/merge/liying3.png|}"
		"{@cascadePath|./data/haarcascades/haarcascade_frontalface_alt_tree.xml|}"
		"{@blackPath|./test/merge/black.png|}"
		"{@whitePath|./test/merge/white.png|}");
	string imagePath1 = parser.get<string>("@imagePath1");
	string imagePath2 = parser.get<string>("@imagePath2");
	string cascadePath = parser.get<string>("@cascadePath");
	string blackPath = parser.get<string>("@blackPath");
	string whitePath = parser.get<string>("@whitePath");

	CascadeClassifier cascade;
	if (!cascade.load(cascadePath))
	{
		cout << "很抱歉,不能加载分类器,请确保路径是否正确"<<endl;
		return -1;
	}

	

	Mat image1 = imread(imagePath1);
	if (image1.empty())
	{
		cout << "不能正确加载目标图像,请确文件保路径是否正确" << endl;
		return -1;
	}

	Mat image2 = imread(imagePath2);
	if (image2.empty())
	{
		cout << "不能正确加载源图像,请确文件保路径是否正确" << endl;
		return -1;
	}

	vector<Rect> faces1, faces2;
	cascade.detectMultiScale(image1, faces1);
	cascade.detectMultiScale(image2, faces2);
	if (faces1.size() == 0 )
	{
		cout << "目标图像检测人脸失败,请更换图片或分类器模型试试" << endl;
		return -1;
	}
	if (faces2.size() == 0)
	{
		cout << "源图像检测人脸失败,,请更换图片或分类器模型试试" << endl;
		return -1;
	}

	Mat wImage = imread(whitePath);
	if (wImage.empty())
	{
		cout << "白色图像加载失败,请确文件保路径是否正确" << endl;
		return -1;
	}
	Mat bImage = imread(blackPath);
	if (bImage.empty())
	{
		cout << "黑色图像加载失败,请确文件保路径是否正确" << endl;
		return -1;
	}

	char q;
	cout << "开始进行人脸融合操作,推出请安“q”,继续请安其他任意键" << endl;
	cin >> q;
	if (q == 'q')
	{
		return 0;
	}
	float xZoom = X_ZOOM;
	float yZoom = Y_ZOOM;
	float xClip = X_CLIP;
	float yClip = Y_CLIP;

	float t1,t2,t3,t4;

	while (true)
	{
		t1 = t2 = t3 = t4 = 0;
		cout << "请输入源脸宽度缩放比例,输入0使用默认设置(默认比例0.7)" << endl;
		cin >> t1;
		if (t1 != 0)
		{
			if (t1 < 0 || t1>2)
			{
				cout << "请输入0-2之间小数" << endl;
				continue;
			}

			xZoom = t1;
		}
		cout << "请输入源脸高度缩放比例,输入0使用默认设置(默认比例0.8)" << endl;
		cin >> t2;
		if (t2 != 0)
		{
			if (t2 < 0 || t2>2)
			{
				cout << "请输入0-2之间小数" << endl;
				continue;
			}

			yZoom = t2;
		}
		cout << "请输入源脸宽度裁剪比例,输入0使用默认设置(默认比例0.6)" << endl;
		cin >> t3;
		if (t3 != 0)
		{
			if (t3 < 0 || t3 > 2)
			{
				cout << "请输入0-2之间小数" << endl;
				continue;
			}

			xClip = t3;
		}

		cout << "请输入源脸高度裁剪比例,输入0使用默认设置(默认比例0.8)" << endl;
		cin >> t4;
		if (t4 != 0)
		{
			if (t4 < 0 || t4 > 2)
			{
				cout << "请输入0-2之间小数" << endl;
				continue;
			}

			yClip = t4;
		}


		Rect r = faces2[0];
		r.x = r.x + (int)(r.width * (1 - xClip) / 2);
		r.y = r.y + (int)(r.height * (1 - yClip) / 2);
		r.width = (int)(r.width * xClip);
		r.height = (int)(r.height * yClip);

		Mat tmpImage = image2(r);
		Mat srcImage, srcMask;
		resize(tmpImage, srcImage, Size(faces1[0].width * xZoom, faces1[0].height * yZoom));
		resize(wImage, srcMask, Size(srcImage.cols, srcImage.rows));

		int gap = srcMask.cols / 4;
		int h = srcMask.rows;
		Rect rb1, rb2, rb3, rb4, rb5, rb6;
		rb1.x = 0;
		rb1.y = h - gap;
		rb1.width = gap;
		rb1.height = gap;
		Mat gap1 = srcMask(rb1);
		resize(bImage, gap1, Size(gap, gap));

		rb2.x = 3 * gap;
		rb2.y = h - gap;
		rb2.width = gap;
		rb2.height = gap;
		Mat gap2 = srcMask(rb2);
		resize(bImage, gap2, Size(gap, gap));

		rb5.x = 0;
		rb5.y = h - 3 * gap / 2;
		rb5.width = gap / 2;
		rb5.height = gap / 2;
		Mat gap5 = srcMask(rb5);
		resize(bImage, gap5, Size(gap / 2, gap / 2));

		rb6.x = 7 * gap / 2;
		rb6.y = h - 3 * gap / 2;
		rb6.width = gap / 2;
		rb6.height = gap / 2;
		Mat gap6 = srcMask(rb6);
		resize(bImage, gap6, Size(gap / 2, gap / 2));

		rb3.x = 0;
		rb3.y = 0;
		rb3.width = gap;
		rb3.height = gap / 2;
		Mat gap3 = srcMask(rb3);
		resize(bImage, gap3, Size(gap, gap / 2));
		rb4.x = 3 * gap;
		rb4.y = 0;
		rb4.width = gap;
		rb4.height = gap / 2;
		Mat gap4 = srcMask(rb4);
		resize(bImage, gap4, Size(gap, gap / 2));

		imshow("源image", image2);
		waitKey();
		imshow("源mask", srcMask);
		waitKey();

		imshow("源face", srcImage);
		waitKey();

		imshow("目的image", image1);
		waitKey();
		Mat blend;
		try {
			seamlessClone(srcImage, image1, srcMask, Point(faces1[0].x + faces1[0].width / 2, faces1[0].y + faces1[0].height / 2), blend, NORMAL_CLONE);
		}
		catch (Exception e)
		{
			cout << e.msg << endl;
		}
		medianBlur(blend, blend, 5);
		imshow("融合图像", blend);
		waitKey(0);
		destroyAllWindows();
		char s;
		cout << "您对融合效果满意吗?满意请输入“y”,不满意请输入其他继续" << endl;
		cin >> s;
		if (s == 'q' || s == 'Q')
		{
			return 0;
		}
		if (s == 'y')
		{
			string filename = "";
			cout << "请输入将要保存的图像文件名称" << endl;
			cin >> filename;
			filename += ".png";
			cout << "将为您保存融合后图像至“" << filename <<"”" <<endl;
			imwrite(filename, blend);

			break;
		}
	}
	

}


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值