-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcartoon.cpp
More file actions
156 lines (138 loc) · 6.39 KB
/
Copy pathcartoon.cpp
File metadata and controls
156 lines (138 loc) · 6.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//
// cartoon.cpp
// opencvtest
//
// Created by Coralie DAVID on 26/10/2017.
// Copyright © 2017 Coralie DAVID. All rights reserved.
//
/*****************************************************************************
* cartoon.cpp
* Create a cartoon-like or painting-like image filter.
******************************************************************************
* by Shervin Emami, 5th Dec 2012 (shervin.emami@gmail.com)
* http://www.shervinemami.info/
******************************************************************************
* Ch1 of the book "Mastering OpenCV with Practical Computer Vision Projects"
* Copyright Packt Publishing 2012.
* http://www.packtpub.com/cool-projects-with-opencv/book
*****************************************************************************/
#include "cartoon.h"
// 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 debugType to 1 to show where skin color is taken from, and 2 to show the skin mask in a new window (for desktop).
Mat cartoonifyImage(Mat srcColor, bool sketchMode, bool evilMode, int debugType)
{
Mat dst;
// Convert from BGR color to Grayscale
Mat srcGray;
cvtColor(srcColor, srcGray, CV_BGR2GRAY);
// Remove the pixel noise with a good Median filter, before we start detecting edges.
medianBlur(srcGray, srcGray, 7);
Size size = srcColor.size();
Mat mask = Mat(size, CV_8U);
Mat edges = Mat(size, CV_8U);
// Generate a nice edge mask, similar to a pencil line drawing.
Laplacian(srcGray, edges, CV_8U, 5);
threshold(edges, mask, 80, 255, THRESH_BINARY_INV);
// Mobile cameras usually have lots of noise, so remove small
// dots of black noise from the black & white edge mask.
removePepperNoise(mask);
//imshow("edges", edges); waitKey(0);
//imshow("mask", mask); waitKey(0);
// For sketch mode, we just need the mask!
if (sketchMode) {
// The output image has 3 channels, not a single channel.
cvtColor(mask, dst, CV_GRAY2BGR);
return dst;
}
// Do the bilateral filtering at a shrunken scale, since it
// runs so slowly but doesn't need full resolution for a good effect.
Size smallSize;
smallSize.width = size.width/4;
smallSize.height = size.height/4;
Mat smallImg = Mat(smallSize, CV_8UC3);
resize(srcColor, smallImg, smallSize, 0,0, INTER_LINEAR);
// Perform many iterations of weak bilateral filtering, to enhance the edges
// while blurring the flat regions, like a cartoon.
Mat tmp = Mat(smallSize, CV_8UC3);
int repetitions = 20; // Repetitions for strong cartoon effect.
for (int i=0; i<repetitions; i++) {
int size = 9; // Filter size. Has a large effect on speed.
double sigmaColor = 9; // Filter color strength.
double sigmaSpace = 7; // Positional strength. Effects speed.
bilateralFilter(smallImg, tmp, size, sigmaColor, sigmaSpace);
bilateralFilter(tmp, smallImg, size, sigmaColor, sigmaSpace);
}
// Go back to the original scale.
resize(smallImg, srcColor, size, 0,0, INTER_LINEAR);
// Clear the output image to black, so that the cartoon line drawings will be black (ie: not drawn).
memset((char*)dst.data, 0, dst.step * dst.rows);
Laplacian(srcGray, edges, CV_8U, 5);
threshold(edges, mask, 80, 255, THRESH_BINARY_INV);
dilate(mask, mask, Mat());
// Use the blurry cartoon image, except for the strong edges that we will leave black.
srcColor.copyTo(dst, mask);
//imshow("mask", mask);
return dst;
}
// Remove black dots (upto 4x4 in size) of noise from a pure black & white image.
// ie: The input image should be mostly white (255) and just contains some black (0) noise
// in addition to the black (0) edges.
void removePepperNoise(Mat &mask)
{
// For simplicity, ignore the top & bottom row border.
for (int y=2; y<mask.rows-2; y++) {
// Get access to each of the 5 rows near this pixel.
uchar *pThis = mask.ptr(y);
uchar *pUp1 = mask.ptr(y-1);
uchar *pUp2 = mask.ptr(y-2);
uchar *pDown1 = mask.ptr(y+1);
uchar *pDown2 = mask.ptr(y+2);
// For simplicity, ignore the left & right row border.
pThis += 2;
pUp1 += 2;
pUp2 += 2;
pDown1 += 2;
pDown2 += 2;
for (int x=2; x<mask.cols-2; x++) {
uchar v = *pThis; // Get the current pixel value (either 0 or 255).
// If the current pixel is black, but all the pixels on the 2-pixel-radius-border are white
// (ie: it is a small island of black pixels, surrounded by white), then delete that island.
if (v == 0) {
bool allAbove = *(pUp2 - 2) && *(pUp2 - 1) && *(pUp2) && *(pUp2 + 1) && *(pUp2 + 2);
bool allLeft = *(pUp1 - 2) && *(pThis - 2) && *(pDown1 - 2);
bool allBelow = *(pDown2 - 2) && *(pDown2 - 1) && *(pDown2) && *(pDown2 + 1) && *(pDown2 + 2);
bool allRight = *(pUp1 + 2) && *(pThis + 2) && *(pDown1 + 2);
bool surroundings = allAbove && allLeft && allBelow && allRight;
if (surroundings == true) {
// Fill the whole 5x5 block as white. Since we know the 5x5 borders
// are already white, just need to fill the 3x3 inner region.
*(pUp1 - 1) = 255;
*(pUp1 + 0) = 255;
*(pUp1 + 1) = 255;
*(pThis - 1) = 255;
*(pThis + 0) = 255;
*(pThis + 1) = 255;
*(pDown1 - 1) = 255;
*(pDown1 + 0) = 255;
*(pDown1 + 1) = 255;
}
// Since we just covered the whole 5x5 block with white, we know the next 2 pixels
// won't be black, so skip the next 2 pixels on the right.
pThis += 2;
pUp1 += 2;
pUp2 += 2;
pDown1 += 2;
pDown2 += 2;
}
// Move to the next pixel.
pThis++;
pUp1++;
pUp2++;
pDown1++;
pDown2++;
}
}
}