本节全部的算法均由类cartoon中的函数cartoonTransform()来实现:
//Frame:输入每一帧图像output:输出图像cartoonTransform(cv::Mat&Frame,cv::Mat&output)兴许将使用很多其它的OpenCV技巧实现很多其它功能,并将该应用移植到Android系统上。
一、使用OpenCV訪问摄像头
OpenCV提供了一个简便易用的框架以提取视频文件和USB摄像头中的图像帧。假设你仅仅是想读取某个视频,你仅仅须要创建一个cv::VideoCapture实例,然后在循环中提取每一帧。这里须要訪问摄像头,因此须要创建一个cv::VideoCapture对象,简单调用对象的open()方法。这里訪问摄像头的函数例如以下,首先在Qt中创建控制台项目。在main函数中加入:
intcameraNumber=0;//设定摄像头编号为0if(argc>1)cameraNumber=atoi(argv[1]);//开启摄像头cv::VideoCapturecamera;camera.open(cameraNumber);if(!camera.isOpened()){qDebug()<<"Error:Couldnotopenthecamera.";exit(1);}//调整摄像头的输出分辨率camera.set(CV_CAP_PROP_FRAME_WIDTH,640);camera.set(CV_CAP_PROP_FRAME_HEIGHT,480);在摄像头被初始化后,能够使用C++流运算符将cv::VideoCapture对象转换成cv::Mat对象,这样能够获取视频的每一帧图像。
二、将帧图像转换为素描效果图片
要将一幅图像转换为素描效果图,能够使用不同的边缘检測算法实现。如经常使用的基于Sobel、Canny、Robert、Prewitt、Laplacian等算子的滤波器均能够实现这一操作,但处理效果各异。
1.Sobel算子:边缘检測中最经常使用的一种方法,在技术上它是以离散型的差分算子,用来运算图像亮度函数的梯度的近似值,缺点是Sobel算子并没有将图像的主题与背景严格地区分开来,换言之就是Sobel算子并没有基于图像灰度进行处理,因为Sobel算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人惬意。
2.Robert算子:依据任一相互垂直方向上的差分都用来预计梯度。Robert算子採用对角方向相邻像素之差。
3.Prewitt算子:该算子与Sobel算子相似。仅仅是权值有所变化,但两者实现起来功能还是有差距的,据经验得知Sobel要比Prewitt更能准确检測图像边缘。
4.Laplacian算子:该算子是一种二阶微分算子,若仅仅考虑边缘点的位置而不考虑周围的灰度差时可用该算子进行检測。
对于阶跃状边缘。其二阶导数在边缘点出现零交叉,并且边缘点两旁的像素的二阶导数异号。
5.Canny算子:该算子的基本性能比前面几种要好。可是相对来说算法复杂。
Canny算子是一个具有滤波。增强,检測的多阶段的优化算子。在进行处理前。Canny算子先利用高斯平滑滤波器来平滑图像以除去噪声,Canny切割算法採用一阶偏导的有限差分来计算梯度幅值和方向,在处理过程中。Canny算子还将经过一个非极大值抑制的过程。最后Canny算子还採用两个阈值来连接边缘。
相比Sobel等其它算子。Canny和Laplacian算子能得到更清晰的素描效果,而Laplacian的噪声抑制要优于Canny边缘检測,而其实素描边缘在不同帧之间经常有剧烈的变化,因此我们选择Laplacian边缘滤波器进行图像处理。
一般在进行Laplacian检測之前,须要对图像进行的预操作有:
voidcartoon::cartoonTransform(cv::Mat&Frame,cv::Mat&output){cv::MatgrayImage;cv::cvtColor(Frame,grayImage,CV_BGR2GRAY);//设置中值滤波器參数cv::medianBlur(grayImage,grayImage,7);//Laplacian边缘检測cv::Matedge;//用于存放边缘检測输出结果cv::Laplacian(grayImage,edge,CV_8U,5);//对边缘检測结果进行二值化cv::MatBinaryzation;//用于存放二值化输出结果cv::threshold(edge,Binaryzation,80,255,cv::THRESH_BINARY_INV);}生成的素描效果:
三、将图像卡通化
在项目中调用一些运算量大的算法时,通常须要考虑到效率问题,比方这里将要用到的双边滤波器。
这里我们利用双边滤波器的平滑区域及保持边缘锐化的特性,将其运用到卡通图片效果生成中。
而考虑到双边滤波器执行效率较低,因此考虑在更低的分辨率中使用,这对效果影响不大,可是执行速度大大加快。
这里使用的策略是将要处理的图像的宽度和高度缩小为原来的1/2。经过双边滤波器处理后,再将其恢复为原来的尺寸。在函数cartoonTransform()中加入下面代码:
//採用双边滤波器//因为算法复杂,因此需降低图像尺寸cv::Sizesize=Frame.size();cv::SizereduceSize;reduceSize.width=size.width/2;reduceSize.height=size.height/2;cv::MatreduceImage=cv::Mat(reduceSize,CV_8UC3);cv::resize(Frame,reduceImage,reduceSize);//双边滤波器实现过程cv::Mattmp=cv::Mat(reduceSize,CV_8UC3);intrepetitions=7;for(inti=0;i cv::Matdst;dst.setTo(0);magnifyImage.copyTo(dst,Binaryzation);//output=dst;//输出卡通效果,阈值各方面有待优化: 四、简单地生成“怪物”形象 这里是结合了边缘滤波器和中值滤波器的还有一个小应用。即通过小的边缘滤波器找到图像中的各处边缘。之后使用中值滤波器来合并这些边缘。具体实现过程例如以下: 具体代码例如以下,相同在函数cartoonTransform()中加入: //怪物模式cv::Matgray,maskMonster;cv::cvtColor(Frame,gray,CV_BGR2GRAY);//先对输入帧进行中值滤波cv::medianBlur(gray,gray,7);//Scharr滤波器cv::Matedge1,edge2;cv::Scharr(gray,edge1,CV_8U,1,0);cv::Scharr(gray,edge2,CV_8U,1,0,-1);edge1+=edge2;//合并x和y方向的边缘cv::threshold(edge1,maskMonster,12,255,cv::THRESH_BINARY_INV);cv::medianBlur(maskMonster,maskMonster,3);output=maskMonster;//输出 五、人脸肤色变换 皮肤检測算法有非常多种,比方基于RGBcolorspace、Ycrcb之cr分量+otsu阈值化、基于混合模型的复杂机器学习算法等。 因为这里仅仅是一个轻量级的应用,因此不考虑使用太复杂的算法。 考虑到未来要将这些图像处理算法移植到安卓上,而移动设备上的微型摄像头传感器对颜色的反应往往差异非常大,并且要在没有标定的情况下对不同肤色的人进行皮肤检測,因此对算法的鲁棒性要求较高。 这里使用了一个技巧,即在图像中规定一个区域,用户须要将脸部放到指定区域中来确定人脸在图像中的位置(其实有些手机应用也会採取这样的方法),对于移动设备来说这不是一件难事。 因此,我们须要规定人脸的区域,相同在函数cartoonTransform()中加入下面代码: 皮肤变色器的实现基于OpenCV的floodFill()函数,该函数相似于一些画图软件中的颜料桶(颜色填充)工具。因为规定屏幕中间椭圆区域就是皮肤像素,因此仅仅须要对该区域的像素进行各种颜色的漫水填充就可以。 这里处理的图像是彩色图,而对于RGB格式的图像,改变颜色的效果不会太好,因为改变颜色须要脸部图像的亮度变化,而皮肤颜色也不能变化太大。这里使用YCrCb颜色空间来进行处理。在YCrCb颜色空间中,能够直接获得亮度值,并且通常的皮肤颜色取值唯一。 脸部不在识别区域内时: 脸部进入识别区域内时: 以上实现了几种图片卡通化效果,接着在学有余力时要对各种算法的效果进行优化。同一时候加入GUI界面,并将应用移植到移动设备上。 參考资料:《深入理解OpenCV:有用计算机视觉项目解析》 完整代码: cartoon.h: #ifndefCARTOON_H#defineCARTOON_H#include