[
  {
    "path": "GenPencil.m",
    "content": "function T = GenPencil(im, P, J)\n% ==============================================\n%   Compute the pencil map 'T'\n%  \n%   Paras:\n%   @im        : input image ranging value from 0 to 1.\n%   @P         : the pencil texture.\n%   @J         : the tone map.\n%\n\n    %% Parameters\n    theta = 0.2;\n    \n    [H, W, ~] = size(im);\n\n    %% Initialization\n    P = imresize(P, [H, W]);\n    P = reshape(P, H*W, 1);\n    logP = log(P);\n    logP = spdiags(logP, 0, H*W, H*W);\n    \n    J = imresize(J, [H, W]);\n    J = reshape(J, H*W, 1);\n    logJ = log(J);\n    \n    e = ones(H*W, 1);\n    Dx = spdiags([-e, e], [0, H], H*W, H*W);\n    Dy = spdiags([-e, e], [0, 1], H*W, H*W);\n    \n    %% Compute matrix A and b\n    A = theta * (Dx * Dx' + Dy * Dy') + (logP)' * logP;\n    b = (logP)' * logJ;\n    \n    %% Conjugate gradient\n    beta = pcg(A, b, 1e-6, 60);\n    \n    %% Compute the result\n    beta = reshape(beta, H, W);\n    \n    P = reshape(P, H, W);\n    \n    T = P .^ beta;\nend"
  },
  {
    "path": "GenStroke.m",
    "content": "function S = GenStroke(im, ks, width, dirNum)\r\n% ==============================================\r\n%   Compute the stroke structure 'S'\r\n%  \r\n%   Paras:\r\n%   @im        : input image ranging value from 0 to 1.\r\n%   @ks        : kernel size.\r\n%   @width     : width of the strocke\r\n%   @dirNum    : number of directions.\r\n%\r\n    \r\n    %% Initialization\r\n    [H, W, ~] = size(im);\r\n    \r\n    %% Smoothing\r\n    im = medfilt2(im, [3 3]);\r\n    \r\n    %% Image gradient\r\n    imX = [abs(im(:,1:(end-1)) - im(:,2:end)),zeros(H,1)];\r\n    imY = [abs(im(1:(end-1),:) - im(2:end,:));zeros(1,W)];  \r\n    imEdge = imX + imY;\r\n\r\n    %% Convolution kernel with horizontal direction \r\n    kerRef = zeros(ks*2+1);\r\n    kerRef(ks+1,:) = 1;\r\n\r\n    %% Classification \r\n    response = zeros(H,W,dirNum);\r\n    for n = 1 : dirNum\r\n        ker = imrotate(kerRef, (n-1)*180/dirNum, 'bilinear', 'crop');\r\n        response(:,:,n) = conv2(imEdge, ker, 'same');\r\n    end\r\n\r\n    [~, index] = max(response,[], 3); \r\n\r\n    %% Create the stroke\r\n    C = zeros(H, W, dirNum);\r\n    for n = 1 : dirNum\r\n        C(:,:,n) = imEdge .* (index == n);\r\n    end\r\n\r\n    kerRef = zeros(ks*2+1);\r\n    kerRef(ks+1,:) = 1;\r\n    for n = 1 : width\r\n        if (ks+1-n) > 0\r\n            kerRef(ks+1-n,:) = 1;\r\n        end\r\n        if (ks+1+n) < (ks*2+1)\r\n            kerRef(ks+1+n,:) = 1;\r\n        end\r\n    end\r\n    \r\n    Spn = zeros(H, W, dirNum);\r\n    for n = 1 : dirNum\r\n        ker = imrotate(kerRef, (n-1)*180/dirNum, 'bilinear', 'crop');\r\n        Spn(:,:,n) = conv2(C(:,:,n), ker, 'same');\r\n    end\r\n\r\n    Sp = sum(Spn, 3);\r\n    Sp = (Sp - min(Sp(:))) / (max(Sp(:)) - min(Sp(:)));\r\n    S = 1 - Sp;\r\nend"
  },
  {
    "path": "GenToneMap.m",
    "content": "function J = GenToneMap(im)\n% ==============================================\n%   Compute the tone map 'T'\n%  \n%   Paras:\n%   @im        : input image ranging value from 0 to 1.\n%\n    \n    %% Parameters\n    Ub = 225;\n    Ua = 105;\n    \n    Mud = 90;\n    \n    DeltaB = 9;\n    DeltaD = 11;\n    \n    % groups from dark to light\n    % 1st group\n    Omega1 = 42;\n    Omega2 = 29;\n    Omega3 = 29;\n    % 2nd group\n    Omega1 = 52;\n    Omega2 = 37;\n    Omega3 = 11;\n    % 3rd group\n    Omega1 = 76;\n    Omega2 = 22;\n    Omega3 = 2;\n    \n    %% Compute the target histgram\n    histgramTarget = zeros(256, 1);\n    total = 0;\n    for ii = 0 : 255\n        if ii < Ua || ii > Ub\n            p = 0;\n        else\n            p = 1 / (Ub - Ua);\n        end\n        \n        histgramTarget(ii+1, 1) = (...\n            Omega1 * 1/DeltaB * exp(-(255-ii)/DeltaB) + ...\n            Omega2 * p + ...\n            Omega3 * 1/sqrt(2 * pi * DeltaD) * exp(-(ii-Mud)^2/(2*DeltaD^2))) * 0.01;\n        \n        total = total + histgramTarget(ii+1, 1);\n    end\n    histgramTarget(:, 1) = histgramTarget(:, 1)/total;\n\n%     %% Smoothing\n%     im = medfilt2(im, [5 5]);\n    \n    %% Histgram matching\n    J = histeq(im, histgramTarget);\n    \n    %% Smoothing\n    G = fspecial('average', 10);\n    J = imfilter(J, G,'same');\nend"
  },
  {
    "path": "PencilDrawing.m",
    "content": "function I = PencilDrawing(im, ks, width, dirNum, gammaS, gammaI)\r\n% ==============================================\r\n%   Generate the pencil drawing I based on the method described in\r\n%   \"Combining Sketch and Tone for Pencil Drawing Production\" Cewu Lu, Li Xu, Jiaya Jia \r\n%   International Symposium on Non-Photorealistic Animation and Rendering (NPAR 2012), June, 2012\r\n%  \r\n%   Paras:\r\n%   @im        : the input image.\r\n%   @ks        : the length of convolution line.\r\n%   @width     : the width of the stroke.\r\n%   @dirNum    : the number of directions.\r\n%   @gammaS    : the darkness of the stroke.\r\n%   @gammaI    : the darkness of the resulted image.\r\n%\r\n\r\n    %% Read the image\r\n    im = im2double(im);\r\n    [H, W, sc] = size(im);\r\n\r\n    %% Convert from rgb to yuv when nessesary\r\n    if (sc == 3)\r\n        yuvIm = rgb2ycbcr(im);\r\n        lumIm = yuvIm(:,:,1);\r\n    else\r\n        lumIm = im;\r\n    end\r\n\r\n    %% Generate the stroke map\r\n    S = GenStroke(lumIm, ks, width, dirNum) .^ gammaS; % darken the result by gamma\r\n%     figure, imshow(S)\r\n\r\n    %% Generate the tone map\r\n    J = GenToneMap(lumIm) .^ gammaI; % darken the result by gamma\r\n%     figure, imshow(J)\r\n\r\n    %% Read the pencil texture\r\n    P = im2double(imread('pencils/pencil0.jpg'));\r\n    P = rgb2gray(P);\r\n\r\n    %% Generate the pencil map\r\n    T = GenPencil(lumIm, P, J);\r\n%     figure, imshow(T)\r\n\r\n    %% Compute the result\r\n    lumIm = S .* T;\r\n\r\n    if (sc == 3)\r\n        yuvIm(:,:,1) = lumIm;\r\n    %     resultIm = lumIm;\r\n        I = ycbcr2rgb(yuvIm);\r\n    else\r\n        I = lumIm;\r\n    end\r\nend\r\n\r\n"
  },
  {
    "path": "README.md",
    "content": "\n# Introduction\n\nImplement the algorithm presented in [1]. You can use the code like below to get a pencil drawing production:\n\n```C++\nI = PencilDrawing(im, ks, width, dirNum, gammaS, gammaI);\n```\n\nSee demo.m for example.\n\n# Parameters\n\nI added some more parameters for better control of the final image.\n\n* **width**\n\n\tControl the width of the strokes. See Figure 1 for example.\n\n\t![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/results/width0.png) ![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/results/width1.png)\n\n\t**Figure 1**: Width of the strokes. left: width = 0; right: width = 2.\n\n* **gammaS**\n\n\tControl the darkness of the strokes. See Figure 2 for example.\n\n\t![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/results/stroke_dark0.png) ![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/results/stroke_dark1.png)\n\n\t**Figure 2**: Gamma of the strokes. left: gammaS = 1.0; right: gammaS = 2.0.\n\n* **gammaI**\n\n\tControl the darkness of the final image. See Figure 3 for example.\n\n\t![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/results/image_dark0.png) ![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/results/image_dark1.png)\n\n\t**Figure 3**: Gamma of the final image. left: gammaI = 1.0; right: gammaI = 0.4.\n\n## Tone Transfer Weights\n\nThe paper presented three groups of weights for tone map generation.\n\n1. ![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/project_images/Tex2Img_1448535264.jpg)\n\n2. ![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/project_images/Tex2Img_1448535306.jpg)\n\n3. ![alt text](https://github.com/candycat1992/PencilDrawing/blob/master/project_images/Tex2Img_1448535007.jpg)\n\nThese 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.\n\n# Folder Organization\n\n* **inputs**\n\n\tInclude some test images from <a href=\"http://www.cse.cuhk.edu.hk/leojia/projects/pencilsketch/pencil_drawing.htm\" target=\"_blank\">the website </a> of the publishers and other images.\n\n* **pencils**\n\t\n\tInclude 5 pencil textures for demonstration. I use “pencils/pencil0.jpg” by default. You can change it in PencilDrawing.m.\n\n* **results**\n\n\tInclude the results generated by my code. Each image may use different parameters.\n\n* **resultsFromPaper**\n\n\tInclude the results of the paper for comparison. I grabbed them in <a href=\"http://www.cse.cuhk.edu.hk/leojia/projects/pencilsketch/pencil_drawing.htm\" target=\"_blank\">their website</a>.\n\n# Reference\n\n[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.\n"
  },
  {
    "path": "demo.m",
    "content": "\nim = imread('inputs/15--298.jpg');\n\nI = PencilDrawing(im, 8, 1, 8, 1.0, 1.0);\n\nfigure, imshow(I)"
  }
]