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

利用OpenCV做个熊猫表情包 二

之前写了一篇

利用OpenCV做个熊猫表情包吧_Leen的博客-CSDN博客

回想起来觉得有点太弱了,意犹未尽,每次使用需要自己去手动截取人脸,清除黑边什么的才能使用demo去合成表情,无奈之前由于安装的vs,opencv版本都比较低,也懒得再折腾。

恰逢前些天电脑硬盘坏了,数据丢了,一切都要重装,那直接高配走起,VS2022+OpenCV4.8,既然环境都有了,于是有空的时候就改进了一下,让它利用opencv,做简单的人脸识别,自动去图片中识别、提取人脸,同时去做黑边清理工作,自动化程度更高,用起来更省事儿~

原理呢就是在处理原始图片的流程中加入了面部识别,将面部单独切出来,同时对面部图片做黑边清晰处理,然后再进行表情的合成工作,下面介绍一下具体过程:

首先是识别到用户输入的原图

利用opencv进行面部识别

灰度化图片后提取面部,并清理黑边

再将面部跟熊猫脸进行融合

下面介绍关键步骤的代码:

初始化面部识别

int InitFaceDetect()
{
	if (!faceCascade.load("D:\\Workspace\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_default.xml")) {
		cout << "人脸检测级联分类器没找到!!" << endl;
		return -1;
	}
	if (!eyes_Cascade.load("D:\\Workspace\\opencv\\build\\etc\\haarcascades\\haarcascade_eye_tree_eyeglasses.xml")) {
		cout << "眼睛检测级联分类器没找到!!" << endl;
		return -1;
	}

	return 0;
}

用到的两个xml特征文件均为openCV提供。

清楚灰度图像中的深色边角区域

/************************************************************************/
/* 消除图片四周的黑色边角区域                                           */
/************************************************************************/

Mat RemoveBlackCorner(Mat img)
{
	int i, j;
	int h = img.size().height;
	int w = img.size().width;

	if (img.channels() == 1)	//灰度图片
	{
		for (j = 0; j < h; j++)
		{
			for (i = 0; i < w; i++)
			{
				if (img.at<uchar>(j, i) < 110)
				{
					img.at<uchar>(j, i) = 255;
				}
				else
				{
					break;
				}
			}
			for (i = w - 1; i >= 0; i--)
			{
				if (img.at<uchar>(j, i) < 110)
				{
					img.at<uchar>(j, i) = 255;
				}
				else
				{
					break;
				}
			}
		}
		for (i = 0; i < w; i++)
		{
			for (j = 0; j < h; j++)
			{
				if (img.at<uchar>(j, i) < 110)
				{
					img.at<uchar>(j, i) = 255;
				}
				else
				{
					break;
				}
			}
			for (j = h - 1; j >= 0; j--)
			{
				if (img.at<uchar>(j, i) < 110)
				{
					img.at<uchar>(j, i) = 255;
				}
				else
				{
					break;
				}
			}
		}
	}

	return img;
}


人脸识别以及将加工后的人脸存成临时文件

bool parse_cmd(int argc, char* argv[])
{
	if (argc < 3)
	{
		return false;
	}

	g_str_src = string(argv[1]);
	g_str_bg = string(argv[2]);

	return true;
}

string GetFolderFromFile(string strFile)
{
	size_t last_slash = strFile.find_last_of("\\");
	std::string directory = strFile.substr(0, last_slash);
	return directory;
}


int DetectFace(Mat img, Mat imgGray) {
	namedWindow("src", WINDOW_AUTOSIZE);
	vector<Rect> faces, eyes;
	faceCascade.detectMultiScale(imgGray, faces, 1.2, 5, 0, Size(30, 30));
	int retVal = -1;
	//目前只取一个脸
	if (faces.size() > 0) {
		for (size_t i = 0; i < faces.size(); i++) {
			//框出人脸位置
			rectangle(img, Point(faces[i].x+ faces[i].width / 8, faces[i].y+faces[i].height / 8), 
				Point(faces[i].x + faces[i].width*7/8, faces[i].y + faces[i].height * 7 / 8), Scalar(0, 0, 255), 1, 8);
			cout << faces[i] << endl;
			//将人脸从灰度图中抠出来
			Mat face_ = imgGray(faces[i]);
			//缩小一点,默认取的矩形比较大
			Rect rect(Point(faces[i].width / 8, faces[i].height / 8),
				Point(faces[i].width * 7 / 8,  faces[i].height * 7/ 8));
			Mat ROI = face_(rect);
			//RemoveBlackBorder(ROI, ROI);
			Mat imgOut = RemoveBlackCorner(ROI);
			//RemoveBlackBorder(ROI, imgOut);
			imwrite(g_str_face, imgOut);
			retVal = 0;
			eyes_Cascade.detectMultiScale(face_, eyes, 1.2, 2, 0, Size(30, 30));
			for (size_t j = 0; j < eyes.size(); j++) {
				Point eye_center(faces[i].x + eyes[j].x + eyes[j].width / 2, faces[i].y + eyes[j].y + eyes[j].height / 2);
				int radius = cvRound((eyes[j].width + eyes[j].height) * 0.25);
				circle(img, eye_center, radius, Scalar(65, 105, 255), 4, 8, 0);
			}
		}
	}
	imshow("src", img);
	return retVal;
}

主逻辑流程


int main(int argc, char* argv[])
{
	if (!parse_cmd(argc, argv))
	{
		cout << "command error" << endl;
		return -1;
	}
	
	if (InitFaceDetect() != 0)
	{
		return -1;
	}
	
	//
	string strDirBase = GetFolderFromFile(g_str_src);
	Mat img_src = imread(g_str_src);
	Mat img_background = imread(g_str_bg);
	g_str_face = strDirBase + "\\tmp_face.jpg";
#ifdef _DBG_SHOW
	namedWindow("img_src");
	imshow("img_src", img_src);
#endif

	Mat img_gray;
	cvtColor(img_src, img_gray, COLOR_BGR2GRAY); //图像灰度化

	int nFace = DetectFace(img_src, img_gray);
	waitKey(3000);
#ifdef _DBG_SHOW
	namedWindow("gray", WINDOW_NORMAL);
	imshow("gray", img_gray);
#endif
	// 按照背景图大小等比缩放
	Size dsize = Size(img_background.cols * 0.55, img_background.rows * 0.55);
	//判断一下是否自动检测到了人脸
	Mat img_face;
	if (nFace == 0) 
	{
		cout << "opencv find face,get face." << endl;
		img_face = imread(g_str_face);
	}
	else
	{
		cout << "can not find face.use image user input." << endl;
		img_face = img_gray;
	}
	resize(img_face, img_face, dsize, 1, 1, INTER_AREA);

	//输出缩放后效果图并重新加载
	Mat img_face2;
	threshold(img_face, img_face2, 105, 255, THRESH_BINARY);
	imwrite(strDirBase + "\\tmp.jpg", img_face2);
	//imshow("img_face2", img_face2);
	Mat img_face3 = imread(strDirBase + "\\tmp.jpg");
	//居中粘合两图
	Rect roi_rect = Rect((img_background.cols - img_face3.cols) / 2, (img_background.rows - img_face3.rows) / 2
		, img_face3.cols, img_face3.rows);
	img_face3.copyTo(img_background(roi_rect));
	//显示并输出
	imshow("mixed", img_background);

	imwrite(g_str_src + ".emoji.jpg", img_background);

	waitKey(5000);

	destroyAllWindows();
	return 0;
}


http://www.kler.cn/news/135176.html

相关文章:

  • 单链表相关面试题--2.反转一个单链表
  • 深入理解MongoDB的CRUD操作
  • 五分钟,Docker安装kafka 3.5,kafka-map图形化管理工具
  • Golang中读写CSV文件的全面指南
  • 和鲸科技创始人范向伟受邀出席“凌云出海,来中东吧”2023华为云上海路演活动
  • NX二次开发UF_CAM_ask_cutter_db_object 函数介绍
  • CDN加速在社会发展中的挑战与机遇
  • 【Machine Learning in R - Next Generation • mlr3】
  • PDF/X、PDF/A、PDF/E:有什么区别,为什么有这么多格式?
  • 树莓派4b编译FFmpeg支持硬件编解码
  • 极智嘉(Geek+)官宣重磅合作伙伴,再度赋能仓储自动化解决方案落地
  • 查询数据库DQL
  • 不爱的人在一起会幸福吗
  • 网络安全(大厂面试真题集)
  • JOSEF 同步检查继电器 JT-1 额定电压100V 柜内固定安装,板前接线
  • List 函数排序操作,用对方法事半功倍!
  • 【Linux】-进程间通信-命名管道文件(没有关系的进程间进行通信),以及写一个日志模板
  • VSCode打开Json 文件格式化
  • CDN的发展和作用。当前网站被攻击适用什么样的CDN。
  • websocket详解
  • 一图多码如何分解?快速做二维码解码的方法
  • 2023 年最新 MySQL 数据库 Windows 本地安装、Centos 服务器安装详细教程
  • 若依ruoyi框架增加2个以上的数据源配置
  • Vue3-shallowRef 和 shallowReactive函数(浅层次的响应式)
  • uC/OS-Il---多级中断机制
  • Linux shell编程学习笔记26:stty(set tty)
  • 头歌 MySQL数据库 - 初识MySQL
  • 这个技术也能管理教学质量,厉害了!
  • 什么是静态代理、JDK动态代理、cglib动态代理?
  • SpringBoot2—运维实用篇