OpenCV实时美颜摄像并生成H264视频流

简介:         为什么美颜摄像这么简单的功能,OpenCV这个开源项目网上很少有代码呢?对于在windows平台下,生成h264视频流也比价麻烦,没有现成的api可以使用,需要借助MinGw编译libx264,或者ffmpeg才能使用。

        为什么美颜摄像这么简单的功能,OpenCV这个开源项目网上很少有代码呢?对于在windows平台下,生成h264视频流也比价麻烦,没有现成的api可以使用,需要借助MinGw编译libx264,或者ffmpeg才能使用。

最近有个小需求,要推送直播视频流,我在网上查了一下有live555或者用librtmp来推送,但是前者还需要修改源代码,也挺麻烦的,现在先做到了下面几个步骤:

1.OpenCV捕捉摄像头的图像

2.进行识别需要美颜的部分(人脸识别,肤色识别)

3.进行美颜(提升亮度,直方图均衡,滤波)

4.生成YUV视频

5.生成h264


现在用librtmp时候,出现了

ERROR:RTMP_Connect0,failed to connect socket,10061(unknow error)

不知道是咋回事了,怀疑是1935端口被禁,但是一时半会儿不知道咋弄。


主要功能代码:


/** Global variables */
//-- Note, either copy these two files from opencv/data/haarscascades to your current folder, or change these locations
string face_cascade_name = "haarcascade_frontalface_alt.xml";

CascadeClassifier face_cascade;
CascadeClassifier eyes_cascade;
string window_name_onlyface = "Capture - only Face";
string window_name_face = "Capture - Face ";


/**
* @function detectAndDisplay
*/
void detectAndenhance( Mat &frame )
{
	std::vector<Rect> faces;
	Mat frame_gray;
	Mat hatAlpha;

	//hatAlpha = imread("2.png",-1);//圣诞帽的图片

	cvtColor( frame, frame_gray, COLOR_BGR2GRAY );
	//equalizeHist( frame_gray, frame_gray );
	//-- Detect faces
	face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );

	for( size_t i = 0; i < faces.size(); i++ )
	{
		Rect face(faces[i].x,faces[i].y,faces[i].x + faces[i].width,faces[i].y + faces[i].height);
		cvSetImageROI(&IplImage(frame),face);

		

		// Do the porcess
		blur(frame,frame,Size(7,7),Point(-1,-1));
		//////////////////////////////////////////////
		cvResetImageROI( &IplImage(frame) );
		Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );
		ellipse( frame, center, Size( faces[i].width/2, faces[i].height/2), 0, 0, 360, Scalar( 255, 0, 255 ), 2, 8, 0 );

		// line(frame,Point(faces[i].x,faces[i].y),center,Scalar(255,0,0),5);

		Mat faceROI = frame_gray( faces[i] );
		std::vector<Rect> eyes;

		imshow( window_name_onlyface, faceROI );
		
	}
	//-- Show what you got
	imshow( window_name_face, frame );
	//imwrite("merry christmas.jpg",frame);
}

/** @函数 detectAndDisplay */
void detectAndDisplay( Mat frame )
{
	std::vector<Rect> faces;
	Mat frame_gray;

	cvtColor( frame, frame_gray, CV_BGR2GRAY );
	equalizeHist( frame_gray, frame_gray );

	//-- 多尺寸检测人脸
	face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );

	for( int i = 0; i < faces.size(); i++ )
	{
		Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 );
		ellipse( frame, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );

		Mat faceROI = frame_gray( faces[i] );
		std::vector<Rect> eyes;

		//-- 在每张人脸上检测双眼
		eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CV_HAAR_SCALE_IMAGE, Size(30, 30) );

		for( int j = 0; j < eyes.size(); j++ )
		{
			Point center( faces[i].x + eyes[j].x + eyes[j].width*0.5, faces[i].y + eyes[j].y + eyes[j].height*0.5 );
			int radius = cvRound( (eyes[j].width + eyes[i].height)*0.25 );
			circle( frame, center, radius, Scalar( 255, 0, 0 ), 4, 8, 0 );
		}
	}
	//-- 显示结果图像
	imshow( window_name_face, frame );
}



// add by shiter 2016/3/3

Mat equalizeChannelHist(const Mat & inputImage)  
{  
	if( inputImage.channels() >= 3 )  
	{  
		vector<Mat> channels;  
		split(inputImage, channels);  

		Mat B,G,R;  

		equalizeHist( channels[0], B );  
		equalizeHist( channels[1], G );  
		equalizeHist( channels[2], R );  

		vector<Mat> combined;  
		combined.push_back(B);  
		combined.push_back(G);  
		combined.push_back(R);  

		Mat result;  
		merge(combined, result);  

		return result;  
	}
	else{return inputImage;}

	return inputImage;  
}  


Mat equalizeIntensityHist(const Mat & inputImage)  
{  
	if(inputImage.channels() >= 3)  
	{  
		Mat ycrcb;  

		cvtColor(inputImage, ycrcb, COLOR_BGR2YCrCb);  

		vector<Mat> channels;  
		split(ycrcb, channels);  

		equalizeHist(channels[0], channels[0]);  

		Mat result;  
		merge(channels,ycrcb);  

		cvtColor(ycrcb, result, COLOR_YCrCb2BGR);  

		return result;  
	}  

	return Mat();  
}  



//皮肤检测,并针对皮肤进行增强,模糊
void MySkinEnhance(Mat &frame)
{
	Mat input_image =frame;  
	Mat output_mask;  
	Mat output_image;  
	Mat mask;  
	//肤色椭圆  
	/*椭圆皮肤模型*/  
	Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);  
	ellipse(skinCrCbHist, Point(113, 155.6), Size(23.4, 15.2), 43.0, 0.0, 360.0, Scalar(255, 255, 255), -1);  

	Mat element = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1) );  

	if(input_image.empty())  
		return ;  

	Mat ycrcb_image;  
	output_mask = Mat::zeros(input_image.size(), CV_8UC1);  
	cvtColor(input_image, ycrcb_image, CV_BGR2YCrCb); //首先转换成到YCrCb空间  

	for(int i = 0; i < input_image.rows; i++) //利用椭圆皮肤模型进行皮肤检测  
	{  
		uchar* p = (uchar*)output_mask.ptr<uchar>(i);  
		Vec3b* ycrcb = (Vec3b*)ycrcb_image.ptr<Vec3b>(i);  
		for(int j = 0; j < input_image.cols; j++)  
		{  
			if(skinCrCbHist.at<uchar>(ycrcb[j][1], ycrcb[j][2]) > 0)  
				p[j] = 255;  
		}  
	}     

	//morphologyEx(output_mask,output_mask,MORPH_CLOSE,element);  

	 //output_mask.setTo(0);  
		
		dilate(output_mask,output_mask,Mat(32,32,CV_8U),Point(-1,-1),2);
		//imwrite("dilate.jpg",dst);
	// output_image.setTo(0);  
		input_image.copyTo(output_image, output_mask);  
		
		Mat enhance = output_image;
		medianBlur(output_image,enhance,11);
		//blur(enhance,enhance,Size(4,4),Point(-1,-1),4);
		imshow("blur face",enhance);
		for(int i = 0; i < output_image.rows; i++) //
		{  
			uchar* p = (uchar*)output_mask.ptr<uchar>(i);  

			for(int j = 0; j < output_image.cols; j++)  
			{  
				if((enhance.at<Vec3b>(i,j)[0] < 50) && (enhance.at<Vec3b>(i,j)[1] < 50)&& (enhance.at<Vec3b>(i,j)[2] < 50) ) 
				{
					//不是纯黑的
					
					
				}
				else
				{

					frame.at<Vec3b>(i,j)[0] =  enhance.at<Vec3b>(i,j)[0];

					frame.at<Vec3b>(i,j)[1] = enhance.at<Vec3b>(i,j)[1];
					frame.at<Vec3b>(i,j)[2] = enhance.at<Vec3b>(i,j)[2];
				}
			}  
		}     
		// 图像融合
		//addWeighted(input_image, 0.95, enhance, 0.05, 0.0, input_image);  
		imshow("ouput image",frame);

}


//提高亮度对比度
void highlight(Mat &frame)
{
	Mat src,dst;  
	double alpha =1.5;  
	double beta = 20;  

	src = frame; 
	if(!src.data)  
	{  
		cout<<"Failed to load image!"<<endl;  
		return ;  
	}  



	//dst = Mat::zeros(src.size(),src.type());  
	for (int i = 0;i<src.rows;++i) 
	{
		//uchar* inData=src.ptr<uchar>(i);
		
		for(int j= 0;j<src.cols;++j) 
		{
			 
				/*src.at<Vec3b>(i,j)[0] = saturate_cast<uchar>(src.at<Vec3b>(i,j)[0]*alpha+beta); 
				src.at<Vec3b>(i,j)[1] = saturate_cast<uchar>(src.at<Vec3b>(i,j)[1]*alpha+beta); 
				src.at<Vec3b>(i,j)[2] = saturate_cast<uchar>(src.at<Vec3b>(i,j)[2]*alpha+beta); */
				//上面的效率低,下面的有越界
				src.at<Vec3b>(i,j)[0] = (src.at<Vec3b>(i,j)[0]*alpha+beta); 
				src.at<Vec3b>(i,j)[1] = (src.at<Vec3b>(i,j)[1]*alpha+beta); 
				src.at<Vec3b>(i,j)[2] = (src.at<Vec3b>(i,j)[2]*alpha+beta);
			
		}
	}

	namedWindow("Handled Image");  
	imshow("Handled Image",src);  
	//waitKey();  
}




实现效果:实时实现的话我只加了肤色检测和简单的滤波,具体美化还需要进一步调试

参数和算法 的组合可以在代码中调整参数实现,可以把膨胀的参数调大一点这个整个人脸就差不多可以经过肤色检测全部搞出来。

完整工程代码:http://download.csdn.net/detail/wangyaninglm/9453146



参考文献:


肤色检测:http://blog.csdn.net/yangtrees/article/details/8269984

人像优化:http://blog.csdn.net/u011630458/article/details/46275469

肤色检测:http://blog.csdn.net/wj080211140/article/details/23384927

改变对比读:http://blog.csdn.net/ubunfans/article/details/24373811


直接推送直播流:http://blog.csdn.net/wangyaninglm/article/details/51056101


相关文章
|
3月前
|
计算机视觉 索引
OpenCV4学习笔记(2):显示相机视频流的帧率
这篇文章是OpenCV4学习笔记的第二部分,介绍了如何通过OpenCV4在显示相机视频流时计算并显示其帧率,使用`getTickCount`和`getTickFrequency`函数来测量帧时间,并用`putText`在图像上绘制帧率信息。
OpenCV4学习笔记(2):显示相机视频流的帧率
|
1月前
|
编解码 计算机视觉 Python
Opencv学习笔记(九):通过CV2将摄像头视频流保存为视频文件
使用OpenCV库通过CV2将摄像头视频流保存为视频文件,包括定义视频编码格式、设置保存路径、通过write写入视频文件,并提供了相应的Python代码示例。
124 0
|
缓存 编解码 前端开发
基于OpenCv+Django的网络实时视频流传输(前后端分离)
基于OpenCv+Django的网络实时视频流传输(前后端分离)
708 0
|
缓存 监控 计算机视觉
基于OpenCv的视频流处理方法
基于OpenCv的视频流处理方法
524 0
|
计算机视觉
Opencv生成视频转h264格式
Opencv生成视频转h264格式
361 0
|
机器学习/深度学习 存储 人工智能
能去码也能打码!OpenCV实时检测视频流人脸并马赛克之,视频后期福音
能去码也能打码!OpenCV实时检测视频流人脸并马赛克之,视频后期福音
550 0
能去码也能打码!OpenCV实时检测视频流人脸并马赛克之,视频后期福音
|
机器学习/深度学习 存储 人工智能
能去码也能打码!OpenCV实时检测视频流人脸并马赛克之,视频后期福音
我们现在经常用到的马赛克其实起源于建筑上的图案装饰,如今马赛克常用于图像或视频的模糊处理。随着技术的进步,打码与去码变成了一种常见的技术研究方向,同时也掀起了一场技术与道德的“战争”。
|
计算机视觉
【OpenCV学习】基于轮廓寻找的视频流运动检测
作者:gnuhpc 出处:http://www.cnblogs.com/gnuhpc/ #include "cv.h" #include "highgui.h" #include #include #include #include #include // vari...
786 0
|
1月前
|
计算机视觉
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
这篇文章详细介绍了OpenCV库中的图像二值化函数`cv2.threshold`,包括二值化的概念、常见的阈值类型、函数的参数说明以及通过代码实例展示了如何应用该函数进行图像二值化处理,并展示了运行结果。
335 0
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解