Repository: candycat1992/PencilDrawing
Branch: master
Commit: bca965d1c92a
Files: 6
Total size: 8.2 KB
Directory structure:
gitextract__zisyvfe/
├── GenPencil.m
├── GenStroke.m
├── GenToneMap.m
├── PencilDrawing.m
├── README.md
└── demo.m
================================================
FILE CONTENTS
================================================
================================================
FILE: GenPencil.m
================================================
function T = GenPencil(im, P, J)
% ==============================================
% Compute the pencil map 'T'
%
% Paras:
% @im : input image ranging value from 0 to 1.
% @P : the pencil texture.
% @J : the tone map.
%
%% Parameters
theta = 0.2;
[H, W, ~] = size(im);
%% Initialization
P = imresize(P, [H, W]);
P = reshape(P, H*W, 1);
logP = log(P);
logP = spdiags(logP, 0, H*W, H*W);
J = imresize(J, [H, W]);
J = reshape(J, H*W, 1);
logJ = log(J);
e = ones(H*W, 1);
Dx = spdiags([-e, e], [0, H], H*W, H*W);
Dy = spdiags([-e, e], [0, 1], H*W, H*W);
%% Compute matrix A and b
A = theta * (Dx * Dx' + Dy * Dy') + (logP)' * logP;
b = (logP)' * logJ;
%% Conjugate gradient
beta = pcg(A, b, 1e-6, 60);
%% Compute the result
beta = reshape(beta, H, W);
P = reshape(P, H, W);
T = P .^ beta;
end
================================================
FILE: GenStroke.m
================================================
function S = GenStroke(im, ks, width, dirNum)
% ==============================================
% Compute the stroke structure 'S'
%
% Paras:
% @im : input image ranging value from 0 to 1.
% @ks : kernel size.
% @width : width of the strocke
% @dirNum : number of directions.
%
%% Initialization
[H, W, ~] = size(im);
%% Smoothing
im = medfilt2(im, [3 3]);
%% Image gradient
imX = [abs(im(:,1:(end-1)) - im(:,2:end)),zeros(H,1)];
imY = [abs(im(1:(end-1),:) - im(2:end,:));zeros(1,W)];
imEdge = imX + imY;
%% Convolution kernel with horizontal direction
kerRef = zeros(ks*2+1);
kerRef(ks+1,:) = 1;
%% Classification
response = zeros(H,W,dirNum);
for n = 1 : dirNum
ker = imrotate(kerRef, (n-1)*180/dirNum, 'bilinear', 'crop');
response(:,:,n) = conv2(imEdge, ker, 'same');
end
[~, index] = max(response,[], 3);
%% Create the stroke
C = zeros(H, W, dirNum);
for n = 1 : dirNum
C(:,:,n) = imEdge .* (index == n);
end
kerRef = zeros(ks*2+1);
kerRef(ks+1,:) = 1;
for n = 1 : width
if (ks+1-n) > 0
kerRef(ks+1-n,:) = 1;
end
if (ks+1+n) < (ks*2+1)
kerRef(ks+1+n,:) = 1;
end
end
Spn = zeros(H, W, dirNum);
for n = 1 : dirNum
ker = imrotate(kerRef, (n-1)*180/dirNum, 'bilinear', 'crop');
Spn(:,:,n) = conv2(C(:,:,n), ker, 'same');
end
Sp = sum(Spn, 3);
Sp = (Sp - min(Sp(:))) / (max(Sp(:)) - min(Sp(:)));
S = 1 - Sp;
end
================================================
FILE: GenToneMap.m
================================================
function J = GenToneMap(im)
% ==============================================
% Compute the tone map 'T'
%
% Paras:
% @im : input image ranging value from 0 to 1.
%
%% Parameters
Ub = 225;
Ua = 105;
Mud = 90;
DeltaB = 9;
DeltaD = 11;
% groups from dark to light
% 1st group
Omega1 = 42;
Omega2 = 29;
Omega3 = 29;
% 2nd group
Omega1 = 52;
Omega2 = 37;
Omega3 = 11;
% 3rd group
Omega1 = 76;
Omega2 = 22;
Omega3 = 2;
%% Compute the target histgram
histgramTarget = zeros(256, 1);
total = 0;
for ii = 0 : 255
if ii < Ua || ii > Ub
p = 0;
else
p = 1 / (Ub - Ua);
end
histgramTarget(ii+1, 1) = (...
Omega1 * 1/DeltaB * exp(-(255-ii)/DeltaB) + ...
Omega2 * p + ...
Omega3 * 1/sqrt(2 * pi * DeltaD) * exp(-(ii-Mud)^2/(2*DeltaD^2))) * 0.01;
total = total + histgramTarget(ii+1, 1);
end
histgramTarget(:, 1) = histgramTarget(:, 1)/total;
% %% Smoothing
% im = medfilt2(im, [5 5]);
%% Histgram matching
J = histeq(im, histgramTarget);
%% Smoothing
G = fspecial('average', 10);
J = imfilter(J, G,'same');
end
================================================
FILE: PencilDrawing.m
================================================
function I = PencilDrawing(im, ks, width, dirNum, gammaS, gammaI)
% ==============================================
% Generate the pencil drawing I based on the method described in
% "Combining Sketch and Tone for Pencil Drawing Production" Cewu Lu, Li Xu, Jiaya Jia
% International Symposium on Non-Photorealistic Animation and Rendering (NPAR 2012), June, 2012
%
% Paras:
% @im : the input image.
% @ks : the length of convolution line.
% @width : the width of the stroke.
% @dirNum : the number of directions.
% @gammaS : the darkness of the stroke.
% @gammaI : the darkness of the resulted image.
%
%% Read the image
im = im2double(im);
[H, W, sc] = size(im);
%% Convert from rgb to yuv when nessesary
if (sc == 3)
yuvIm = rgb2ycbcr(im);
lumIm = yuvIm(:,:,1);
else
lumIm = im;
end
%% Generate the stroke map
S = GenStroke(lumIm, ks, width, dirNum) .^ gammaS; % darken the result by gamma
% figure, imshow(S)
%% Generate the tone map
J = GenToneMap(lumIm) .^ gammaI; % darken the result by gamma
% figure, imshow(J)
%% Read the pencil texture
P = im2double(imread('pencils/pencil0.jpg'));
P = rgb2gray(P);
%% Generate the pencil map
T = GenPencil(lumIm, P, J);
% figure, imshow(T)
%% Compute the result
lumIm = S .* T;
if (sc == 3)
yuvIm(:,:,1) = lumIm;
% resultIm = lumIm;
I = ycbcr2rgb(yuvIm);
else
I = lumIm;
end
end
================================================
FILE: README.md
================================================
# Introduction
Implement the algorithm presented in [1]. You can use the code like below to get a pencil drawing production:
```C++
I = PencilDrawing(im, ks, width, dirNum, gammaS, gammaI);
```
See demo.m for example.
# Parameters
I added some more parameters for better control of the final image.
* **width**
Control the width of the strokes. See Figure 1 for example.
 
**Figure 1**: Width of the strokes. left: width = 0; right: width = 2.
* **gammaS**
Control the darkness of the strokes. See Figure 2 for example.
 
**Figure 2**: Gamma of the strokes. left: gammaS = 1.0; right: gammaS = 2.0.
* **gammaI**
Control the darkness of the final image. See Figure 3 for example.
 
**Figure 3**: Gamma of the final image. left: gammaI = 1.0; right: gammaI = 0.4.
## Tone Transfer Weights
The paper presented three groups of weights for tone map generation.
1. 
2. 
3. 
These weights will determine the histogram of the tone map. I use the third group by default, which makes the final image lighter. You can change to other groups in GenToneMap.m.
# Folder Organization
* **inputs**
Include some test images from the website of the publishers and other images.
* **pencils**
Include 5 pencil textures for demonstration. I use “pencils/pencil0.jpg” by default. You can change it in PencilDrawing.m.
* **results**
Include the results generated by my code. Each image may use different parameters.
* **resultsFromPaper**
Include the results of the paper for comparison. I grabbed them in their website.
# Reference
[1] Lu C, Xu L, Jia J. Combining sketch and tone for pencil drawing production[C]//Proceedings of the Symposium on Non-Photorealistic Animation and Rendering. Eurographics Association, 2012: 65-73.
================================================
FILE: demo.m
================================================
im = imread('inputs/15--298.jpg');
I = PencilDrawing(im, 8, 1, 8, 1.0, 1.0);
figure, imshow(I)