OpenCV实时美颜摄像并生成H264视频流
为什么美颜摄像这么简单的功能,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
OpenCV实时美颜摄像并生成H264视频流的更多相关文章
- 基于GPUImage的实时美颜滤镜
1.背景 前段时间由于项目需求,做了一个基于GPUImage的实时美颜滤镜.现在各种各样的直播.视频App层出不穷,美颜滤镜的需求也越来越多.为了回馈开源,现在我把它放到了GitHub https:/ ...
- IOS-实战分享:实时美颜滤镜是怎样炼成的
作者:琨君 原文链接:http://www.jianshu.com/p/945fc806a9b4 本文获作者授权转载 背景 前段时间由于项目需求,做了一个基于GPUImage的实时美颜滤镜.现在各种各 ...
- 从wireshark数据中分析rtmp协议,并提取出H264视频流
我写的小工具 rtmp_parse.exe 使用用法如先介绍下: -sps [文件路径] 解析 sps 数据 文件当中的内容就是纯方本的hexstring: 如 42 E0 33 8D 68 05 ...
- 在iOS平台使用ffmpeg解码h264视频流(转)
在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下. 对于视频文件和rtsp之类的主流视频传输协议,ffmpeg提供avformat_open_input接口,直接将文件路径或UR ...
- Android平台Camera实时滤镜实现方法探讨(十一)--实时美颜滤镜
上一章完毕了对图片的磨皮处理.经过简单算法流程优化,能够达到非常快的速度.可是不能用于实时美颜.经实验,若採用仅仅处理Y信号的方案.半径极限大约是5-10,超过10则明显感受到卡顿.但对于1920X1 ...
- 在iOS平台使用ffmpeg解码h264视频流
来源:http://www.aichengxu.com/view/37145 在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下. 对于视频文件和rtsp之类的主流视频传输协议,f ...
- 利用flask将opencv实时视频流输出到浏览器
opencv通过webcam可以获取本地实时视频流,但是如果需要将视频流共享给其他机器调用,就可以将利用flask框架构建一个实时视频流服务器,然后其他机器可以通过向这个服务器发送请求来获取这台机器上 ...
- 基于OpenCV 的美颜相机推送直播流
程序流程: 1.图像采集 先从opencv(2.4.10版本)采集回来摄像头的图像,是一帧一帧的 每一帧图像是一个矩阵,opencv中的mat 数据结构. 2.人脸的美化 人脸美化,我们用的皮肤检测, ...
- 利用FFmpge进行视频压缩(从图像到H264视频流)
对于FFmpeg相信做视频或图像处理这一块的都不会陌生,在网上也能找到非常多相关的代码.但因为版本号不同等原因.往往找到的代码都是须要自行改动才干够用,为此本人希望能尽绵薄之力,将开发包和自行编写的代 ...
随机推荐
- bzoj 2436: [Noi2011]Noi嘉年华
Description NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不同的地点举办.每个嘉年华可能包含很多个活动, ...
- 4999: This Problem Is Too Simple!
Description 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: C i x(0<=x<2^31) 表示将i节点的值改为x. Q i j x(0<=x<2^31 ...
- hdu 1166 线段树(sum+单点修改)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- [bzoj4923]K小值查询
来自FallDream的博客,未经允许,请勿转载,谢谢. 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有 ...
- HTML的基本介绍
HTML(HyperText Markup Language): 超文本标记语言,超文本就是指页面内可以包含图片.链接,甚至音乐.程序等非文字元素. HTML是标记语言!!!!! HTML是标记语言! ...
- c++中双冒号的作用
双冒号(::)用法 参考链接:https://segmentfault.com/a/1190000000345680 1.表示“域操作符”例:声明了一个类A,类A里声明了一个成员函数void f(), ...
- c语言的第三次作业
(一)改错题 计算f(x)的值:输入实数x,计算并输出下列分段函数f(x)的值,输出时保留1位小数. 输入输出样例1: Enterr x: 10.0 f(10.0) = 0.1 输入输出样例2: En ...
- eclipse中安装freemarker插件及ftl使用freemarker编辑器
http://www.07net01.com/2015/08/895212.html eclipse中安装freemarker插件及ftl使用freemarker编辑器 在线安装的方法是:Help – ...
- 使用Aes对称加密解密Web.Config数据库连接串
现在很多公司开始为了保证数据库的安全性,通常会对Web.Config的数据库连接字符串进行加密.本文将介绍学习使用Aes加密解密数据库连接字符串.本文采用MySql数据库. AES概念简述 AES 是 ...
- MFC多线程
当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力.用进程和线程的观点来研究软 ...