输入一幅图像,得到其素描(黑白图画)效果,将素描叠加到彩色图像上,得到一种卡通效果。
显示素描模式、卡通模式、外星人模式和怪物模式。
实验代码:
<span style="font-size:18px;">#include "cv.h"
#include "highgui.h"
#include "opencv.hpp"
#include "stdio.h"
#include "stdlib.h"
using namespace cv;
using namespace std;
const int NUM_STICK_FIGURE_ITERATIONS = 40;
void cartoonifyImage(Mat srcColor, Mat dst, bool sketchMode, bool alienMode, bool evilMode);
void changefacialcolor (Mat smallImg, Size smallsize, Mat edges);
void drawface(Mat dst,Size size);
int main(int argc,char *argv[])
{
int cameraNumber = 0;
int m_stickFigureIterations;
if (argc > 1)
cameraNumber = atoi(argv[1]);
//get access to the camera
cv::VideoCapture camera;
//camera.open(cameraNumber);
//video from file
//camera.open("e:/test.avi");
/*
if (!camera.isOpened())
{
std::cerr << "ERROR: Could not access the camera or video!" << std::endl;
exit(1);
}
*/
//Try to set the camera resolution.
camera.set(CV_CAP_PROP_FRAME_WIDTH,640);
camera.set(CV_CAP_PROP_FRAME_HEIGHT,480);
//read picture
while (true)
{
//Grab the next camera frame;
cv::Mat cameraFrame;
/*
camera >>cameraFrame;
if (cameraFrame.empty())
{
std::cerr << "Error: couldn't grab a camera frame." << std::endl;
exit(1);
}
*/
// Create a blank output image, that we will draw onto.
cameraFrame = imread("e:/lena.jpg");
cv::Mat displayedFrame (cameraFrame.size(),CV_8UC3);
//Run the cartoonifier filter on the camer frame.
//cartoonifyImage(cameraFrame,displayedFrame);
//cartoonifyImage( cameraFrame, displayedFrame, 1, 0, 0) ;// 显示素描模式
//cartoonifyImage( cameraFrame, displayedFrame, 0, 0, 1) ; //显示怪物模式
//cartoonifyImage( cameraFrame, displayedFrame, 0, 0, 0) ; //显示卡通模式
// Show a stick-figure outline of a face for a short duration, so the user knows where to put their face.
m_stickFigureIterations = NUM_STICK_FIGURE_ITERATIONS;
if (m_stickFigureIterations > 0) {
Size size = displayedFrame.size();
drawface(cameraFrame,size);
m_stickFigureIterations--;
}
cartoonifyImage( cameraFrame, displayedFrame, 0, 1, 0) ;//显示外星人模式
//Display the processed image onto the screen.
imshow ("Cartoonifier",displayedFrame);
//Important: wait for at least 20 milliseconds,
//so that the image can be displayed on the screen!
// Also checks if a key was pressed in the GUI window.
//Note that it should be a "char" to support Linux.
char keypress = cv::waitKey(0);
if ( keypress == 27) // Escape key
{
break;// Quit the program!
}
} // end of while
system("pause");
return 0;
}
// Convert the given photo into a cartoon-like or painting-like image.
// Set sketchMode to true if you want a line drawing instead of a painting.
// Set alienMode to true if you want alien skin instead of human.
// Set evilMode to true if you want an "evil" character instead of a "good" character.
// Set cartoon mode to true if you want an cartoon-like image.
// Set debugType to 1 to show where skin color is taken from, and 2 to show the skin mask in a new window (for desktop).
void cartoonifyImage(Mat srcColor, Mat dst, bool sketchMode, bool alienMode, bool evilMode) //素描模式、外星人模式、怪物模式
{
Mat gray;
cvtColor(srcColor,gray,CV_BGR2GRAY);
const int MEDIAN_BLUR_FILTER_SIZE = 7;
medianBlur(gray,gray,MEDIAN_BLUR_FILTER_SIZE);
Mat edges;
Mat mask;
dst.setTo(0);
if (!evilMode)
{
const int LAPLACIAN_FILTER_SIZE = 5;
Laplacian(gray,edges,CV_8U,LAPLACIAN_FILTER_SIZE);
const int EDGES_THRESHOLD = 80;
threshold(edges,mask,EDGES_THRESHOLD,255,THRESH_BINARY_INV);
}
else
{
Mat edges,edges2;
Scharr(gray, edges, CV_8U, 1, 0);
Scharr(gray, edges2, CV_8U, 1, 0, -1);
edges += edges2; // Cobine the x & y edges together
const int EVIL_EDGE_THRESHOLD = 12;
threshold (edges, mask, EVIL_EDGE_THRESHOLD, 255, THRESH_BINARY_INV);
medianBlur( mask, mask, 3);
imshow("edge",edges);
imshow("evilMask",mask); //怪物掩码
srcColor.copyTo(dst,mask); //将掩码叠加到原图像上
imshow("evilMode",dst);
return;
}
if (sketchMode)
{
cvtColor(mask,dst,CV_GRAY2BGR);
//imshow("sketch",mask);
return;
}
// resize the src image
Size size = srcColor.size();
Size smallSize;
smallSize.width = size.width / 2;
smallSize.height = size.height / 2;
Mat smallImg= Mat (smallSize, CV_8UC3);
resize (srcColor, smallImg, smallSize, 0, 0, INTER_LINEAR);//缩小图片,降低分辨率
//创建双边滤波器所需的临时Mat变量
Mat temp = Mat (smallSize, CV_8UC3);
int repetitions = 7;//Repretitions for strong cartoon effect.重复计数
for (int i=0; i < repetitions; i++)
{
int ksize = 9; //Filter size.Has a large effect on speed.
double sigmaColor = 9; //Filter color strength.
double sigmaSpace = 7; //Spatial strength. Affects speed.
bilateralFilter(smallImg, temp, ksize, sigmaColor, sigmaSpace);
bilateralFilter(temp, smallImg, ksize, sigmaColor, sigmaSpace);
}
if (alienMode)
{
// Apply an "alien" filter, when given a shrunken image and the full-res edge mask.
// Detects the color of the pixels in the middle of the image, then changes the color of that region to green.
changefacialcolor(smallImg, smallSize, edges);
}
Mat bigImg;
resize(smallImg, bigImg, size, 0, 0, INTER_LINEAR);
bigImg.copyTo(dst,mask);
//imshow("crtoon",dst);//显示卡通模式(彩色图像+素描掩码)
}
//下列函数确定用户放置脸位置
// Draw an anti-aliased face outline, so the user knows where to put their face.
// Note that the skin detector for "alien" mode uses points around the face based on the face
// dimensions shown by this function.
void drawface(Mat dst,Size size)
{
//draw the color face onto a black background
Mat faceOutline = Mat::zeros( size, CV_8UC3);
Scalar color = CV_RGB(255,255,0); //Yellow.
int thickness = 4;
// use 70% of the screen height as the face height.
int sw = size.width;
int sh = size.height;
int faceH = sh / 2 * 0.7;
// Scalar the width to be the same for any screen width.
int faceW = faceH * 0.72;
//Draw the faces outline
ellipse(faceOutline,Point(sw/2, sh/2),Size(faceW,faceH), 0, 0, 360, color, thickness, CV_AA);
// Draw the eye outlines, as 2 arcs per eye.
int eyeW = faceW * 0.23;
int eyeH = faceH * 0.11;
int eyeX = faceW * 0.48;
int eyeY = faceH * 0.13;
Size eyeSize = Size (eyeW, eyeH);
// set the angle and shift for the eye half ellipses.
// 使用上椭圆作为眼睛的上半部分,下椭圆作为眼睛的下半部分。
int eyeA = 15; // angle in degress.
int eyeYshift = 11;
// Draw the top of the right eye.
ellipse( faceOutline, Point (sw/2 - eyeX, sh/2 - eyeY), eyeSize, 0, 180 + eyeA, 360 - eyeA, color, thickness, CV_AA);
//Draw the bottom of the right eye.
ellipse( faceOutline, Point(sw/2 - eyeX, sh/2 - eyeY - eyeYshift), eyeSize, 0, 0 + eyeA, 180 - eyeA, color, thickness, CV_AA);
//Draw the top left eye.
ellipse(faceOutline,Point(sw/2 + eyeX, sh/2 - eyeY),eyeSize, 0, 180+eyeA, 360-eyeA, color, thickness, thickness,CV_AA);
//draw the bottom left eye.
ellipse(faceOutline, Point(sw/2 + eyeX, sh/2- eyeY- eyeYshift), eyeSize, 0, 0+eyeA, 180-eyeA, color, thickness, CV_AA);
//draw the bottom lip of the mouth
int mouthY = faceH * 0.48;
int mouthW = faceW * 0.45;
int mouthH = faceH * 0.06;
ellipse(faceOutline, Point(sw/2, sh/2 + mouthY), Size(mouthW, mouthH), 0, 0, 180, color, thickness, CV_AA);
//提示用户将脸放在指定位置上。
//draw anti_aliased text.
int fontFace = FONT_HERSHEY_COMPLEX;
float fontScalar = 1.0f;
int fontThickness = 2;
char *szMsg = "Put your face here";
putText(faceOutline,szMsg,Point(sw * 0.23,sh * 0.1),fontFace, fontScalar,color,fontThickness,CV_AA);
//利用alpha融合卡通图像与人脸轮廓,dst是卡通处理后的图像
addWeighted(dst, 1.0, faceOutline, 0.7, 0, dst, CV_8UC3);
imshow("脸轮廓",dst);
}
// Apply an "alien" filter, when given a shrunken BGR image and the full-res edge mask.
// Detects the color of the pixels in the middle of the image, then changes the color of that region to green.
void changefacialcolor (Mat smallImg, Size smallsize, Mat edges)
{
//change rgb into yuv space.
Mat yuv = Mat(smallsize,CV_8UC3);
cvtColor(smallImg, yuv, CV_BGR2YCrCb);
// The floodFill mask has to be 2 pixels wider and 2 pixels taller than the small image.
// The edge mask is the full src image size, so we will shrink it to the small size,
// storing into the floodFill mask data.
int sw = smallsize.width;
int sh = smallsize.height;
Mat mask,maskPlusBorder;
maskPlusBorder = Mat ::zeros(sh+2, sw+2, CV_8UC1);
mask = maskPlusBorder (Rect(1,1,sw,sh));
resize(edges,mask,smallsize); //put edges in both of them.
// Make the mask values just 0 or 255, to remove weak edges.
const int EDGES_THRESHOLD = 80;
threshold(mask,mask,EDGES_THRESHOLD,255,THRESH_BINARY);
dilate(mask, mask, Mat());
erode(mask, mask, Mat());
int const NUM_SKIN_POINT = 6;
Point skinPts[NUM_SKIN_POINT];
skinPts[0] = Point (sw/2, sh/2 - sh/6);
skinPts[1] = Point (sw/2 - sw/11, sh/2 - sh/6);
skinPts[2] = Point (sw/2 + sw/11, sh/2 -sh/6);
skinPts[3] = Point (sw/2, sh/2 + sh/16);
skinPts[4] = Point (sw/2- sw/9, sh/2 + sh/16);
skinPts[5] = Point (sw/2 + sw/9, sh/2 + sh/16);
const int LOWER_Y = 60;
const int UPPER_Y = 80;
const int LOWER_Cr = 25;
const int UPPER_Cr = 15;
const int LOWER_Cb = 20;
const int UPPER_Cb = 15;
Scalar lowerDiff = Scalar (LOWER_Y, LOWER_Cr, LOWER_Cb);
Scalar upperDiff = Scalar (UPPER_Y, UPPER_Cr, UPPER_Cb);
const int CONNECTED_COMPONENTS = 4;//To fill diagonally, use 8.
const int flags = CONNECTED_COMPONENTS | FLOODFILL_FIXED_RANGE| FLOODFILL_MASK_ONLY;
Mat edegMask = mask.clone(); // keep a copy of the edge mask.
// "maskPlusBorder" is initialized with edges to block floodFill().
for ( int i = 0; i < NUM_SKIN_POINT; i++)
{
floodFill( yuv, maskPlusBorder, skinPts[i], Scalar(), NULL, lowerDiff, upperDiff, flags);
}
mask -= edegMask;
int Red = 0;
int Green = 70;
int Blue = 0;
add(smallImg, Scalar(Red,Green,Blue), smallImg, mask);
}
</span>
实验结果:
素描模式
卡通模式
怪物模式
外星人模式
这里的外星人模式,因为获得人脸轮廓跟图像有差异,导致图像效果不对,应该能通过修改参数,调整。原程序主要是应用于摄像头输入图片的。