Create an Image to Pencil Sketch Converter in Python

Comparison image showcasing a split frame with 'Original' and 'Sketch' labels. On the left, an original photo of a girl is displayed, while the right side features a pencil sketch version of the same image. In the centre, a Python language logo is visible.

Introduction

Have you ever tried sketching someone with a pencil and paper? If you have, then you know how much practice it takes to get a really good result. An Artist who can draw anything with just pencils often gets a lot of praise for their work.

But things have changed a bit now. Nowadays, many online tools and mobile apps can do the same thing with an image, and you might already know about them. In this tutorial, we will create such an Image to Pencil Sketch Converter using Python language with the help of OpenCV and Tkinter libraries.

Before moving to the Project discussion, we need to understand the logic behind this Image manipulation concept. So, I divided the whole topic into two parts. In the first part, youā€™ll learn how to convert an image to a pencil sketch step-by-step through a Python program.

In the second part, weā€™ll apply the same logic to create a Python application that will help to achieve the Sketch effect in just a few clicks. It would be so easy to use that application by anyone.

So, letā€™s not waste any more time and turn this idea into reality!

Requirements

First, we need to install the OpenCV library (Documentation). To get it, please use the following command:

pip install opencv-python

Donā€™t worry about Tkinter Library (Documentation). It comes pre-installed with most Python installations. In case if you donā€™t have it, you can install it using the following command:

pip install tk

Understanding the Program

Letā€™s break down the concept of image to a pencil sketch conversion using a Python program, step by step:

Importing the module

I recommended you create a separate folder named ā€œSketch-Makerā€ for this project and declare a Python file within it as ā€œsketch_maker.pyā€œ.

Now, letā€™s start by importing theĀ cv2Ā module.

import cv2

Opening an Image

Letā€™s open your gallery and pick a good-quality photo of yourself. Now keep it in the project folder.

Mention the name of the image in a variable called ā€˜img_pathā€˜ and load the image usingĀ cv2.imread function (Get the image used in the program from here: ā€˜woman.jpgā€˜).

img = cv2.imread("woman.jpg")

Resizing and Displaying the Image

Not all computer or laptop users have the same screen sizes, which means the displayed image may not appear perfectly for everyone.

To make sure the image looks consistent for everyone, itā€™s important to adjust its size uniformly. You can achieve this by utilizing theĀ cv2.imreadĀ function.

img = cv2.resize(img, (740,480))

Before we move to the editing phase, letā€™s display the original image using theĀ cv2.imshowĀ function.

cv2.imshow("Result Image", img)
# Press any key to exit
cv2.waitKey()
cv2.destroyAllWindows()

Output

woman original image

Converting to Grayscale

Weā€™ll first convert the image toĀ grayscale. To do so, we will use theĀ cv2.cvtColorĀ function. Within this function, we need to specify the initial loaded image as the first parameter, followed by the ā€˜codeā€˜ value, which expects an integer input.

gray_img = cv2.cvtColor(src=img, code=cv2.COLOR_BGR2GRAY)

Remembering the numbers for different ā€˜codeā€™ options is hard. Thatā€™s why we prefer using easier-to-recall code names like cv2.COLOR_BGR2GRAY instead of dealing with the numbers.

Here is the documentation for the conversion of color spaces:Ā OpenCV Documentation.

Now, to display the transformed grayscale image, weā€™ll use the same code block utilized for display in the previous section. We just need to update the second parameter in the cv2.imshow function to ā€œgray_imgā€œ.

cv2.imshow("Result Image", gray_img)
# Press any key to exit
cv2.waitKey()
cv2.destroyAllWindows()

Output

woman gray scaled image

Inverting the Gray Image

In this section, weā€™ll take the grayscale image (ā€œgray_imgā€œ), and invert its pixel values. This means that black pixels become white and white pixels become black, effectively creating a negative image of the original grayscale image.

invert_img = cv2.bitwise_not(src=gray_img)

Letā€™s display the interverted gray-scaled image.

cv2.imshow("Result Image", invert_img)
# Press any key to exit
cv2.waitKey()
cv2.destroyAllWindows()

Output

woman inverted gray image

Smoothing the Image

Smoothing an image is an important step in the process of an image to a pencil sketch conversion.Ā It is the art of blurring an image while preserving its essential features like edges and textures. It also helps to reduce noise and simplify image details.

In this step, we will applyĀ median blurĀ to our inverted image to achieve our present goal.

smooth_img = cv2.medianBlur(src=invert_img, ksize=37)

In the code above, ā€˜ksizeā€˜ specifies the size of the kernel or window used for the median filtering operation. It determines the neighborhood over which the median value is computed for each pixel.

For example, ā€œksize=37ā€ means that a square kernel of size 37Ɨ37 pixels will be used for the median filtering operation. The median value within this 37Ɨ37 neighborhood will be computed for each pixel in the ā€œinvert_imgā€, resulting in the smoothed image stored in ā€œsmooth_imgā€.

Weā€™ll display the smoothed image using the same technique as the previous section.

cv2.imshow("Result Image", smooth_img)
# Press any key to exit
cv2.waitKey()
cv2.destroyAllWindows()

Output

woman smoothed image

Inverting the Smoothed Image

Again, we will invert the previously smoothed image (ā€œsmooth_imgā€œ) using the bitwise NOT operation.

invt_smooth_img = cv2.bitwise_not(smooth_img)

Letā€™s display the inverted image:

cv2.imshow("Result Image", invt_smooth_img)
# Press any key to exit
cv2.waitKey()
cv2.destroyAllWindows()

Output

woman inverted smooth image

Applying Stylization

We are at the last step of our image-to-pencil sketch conversion using Python. In this section, we will perform pixel-wise division between the grayscale image (ā€œgray_imgā€œ) and the smoothed image (ā€œinvt_smooth_imgā€œ) using the cv2.devide function.

sketch_img = cv2.divide(gray_img, invt_smooth_img, scale=220)

In the code above, ā€˜scaleā€˜ specifies the scaling factor applied during the division operation. For example, if ā€œscale=220ā€œ, each pixel in the resulting image will be calculated by dividing the corresponding pixels from the numerator and denominator images and then multiplying the result by 1/220.

This scaling factor can help adjust the brightness or contrast of the resulting image after the division operation.

Our sketch image is ready now, letā€™s display it.

cv2.imshow("Result Image", sketch_img)
# Press any key to exit
cv2.waitKey()
cv2.destroyAllWindows()

Output

Pencil Sketch of a Woman, displaying on an application window

Save the Image

Add this code snippet at the end of the program to save the sketched image in the current working directory.

cv2.imwrite("result.jpg", sketch_img)

Make the Program an Application

Letā€™s improve the user experience of the above program by giving a touch of Graphical Interface using the Tkinter library. We will use Python to create an application for converting an image into a pencil sketch.

The core logic of the program will be the same. In this case, the user interface will ease the appā€™s use. You can

  • Select your favorite image.
  • Display the original image.
  • Select the intensity of the sketching quality.
  • Save the resulting image.

So, Before you copy the entire code, create a Python file as ā€˜app.pyā€˜ within the ā€œCartoon-Makerā€ folder.

import cv2
import pathlib
import pyautogui
from tkinter import *
from PIL import ImageTk, Image
from tkinter import filedialog

class SketchImage:
    def __init__(self, root):
        self.window = root
        self.window.geometry("940x580")
        self.window.title('Sketch Creator')
        self.window.resizable(width = False, height = False)

        self.width = 700
        self.height = 440

        self.Image_Path = ''
        self.SketchImg = ''

        # ==============================================
        # ================Menubar Section===============
        # ==============================================
        # Creating Menubar
        self.menubar = Menu(self.window)

        # Adding Edit Menu and its sub menus
        edit = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label='Open', menu=edit)
        edit.add_command(label='Open Image',command=self.Open_Image)
        

        sketch = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label='Sketch', menu=sketch)
        sketch.add_command(label='Create Sketch', command=self.CreateSketch)
    
        save = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label='Save', menu=save)
        save.add_command(label='Save Image', command=self.Save_Image)

        # Exit the Application
        exit = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label='Exit', menu=exit)
        exit.add_command(label='Exit', command=self.Exit)

        # Configuring the menubar
        self.window.config(menu=self.menubar)
        # ===================End=======================

        # Creating a Frame
        self.frame = Frame(self.window, 
        width=self.width,height=self.height)
        self.frame.pack()
        self.frame.place(anchor='center', relx=0.5, rely=0.5)

        # The Scale widget to select the intensity of the
        # sketch quality
        self.intensity = Scale(self.window, from_=5, to=155, resolution=2, orient=HORIZONTAL, length= 300)
        self.intensity.set(37)
        self.intensity.place(x=320, y=520)

    # Open an Image through filedialog
    def Open_Image(self):
        self.Clear_Screen()
        self.Image_Path = filedialog.askopenfilename(title = "Select an Image", filetypes = (("Image files", "*.jpg *.jpeg *.png"),))
        if len(self.Image_Path) != 0:
            self.Show_Image(self.Image_Path)
    
    # Display the Image
    def Show_Image(self, Img):
        # opening the image
        image = Image.open(Img)
        # resize the image, so that it fits to the screen
        resized_image = image.resize((self.width, self.height))

        # Create an object of tkinter ImageTk
        self.img = ImageTk.PhotoImage(resized_image)

        # A Label Widget for displaying the Image
        label = Label(self.frame, image=self.img)
        label.pack()

    def CreateSketch(self):
        # storing the image path to a variable
        self.ImgPath = self.Image_Path

        # If any image is not selected 
        if len(self.ImgPath) == 0:
            pass
        else:
            Img = cv2.imread(self.ImgPath)

            Img = cv2.resize(Img, (740,480))

            GrayImg = cv2.cvtColor(src=Img, code=cv2.COLOR_BGR2GRAY)

            InvertImg = cv2.bitwise_not(GrayImg)

            SmoothImg = cv2.medianBlur(src=InvertImg, ksize=self.intensity.get())

            IvtSmoothImg = cv2.bitwise_not(SmoothImg)

            self.SketchImg = cv2.divide(GrayImg, IvtSmoothImg, scale=250)

            cv2.imshow("Result Image", self.SketchImg)
            # Press any key to exit
            cv2.waitKey()
            cv2.destroyAllWindows()

    def Save_Image(self):
        if len(self.SketchImg) == 0:
            pass
        else:
            # Get the file name to be saved after making the sketch
            filename = pyautogui.prompt("Enter the filename to be saved")
            # Filename with the extension(extension of the original image)
            filename = filename + pathlib.Path(self.ImgPath).suffix
            # Saving the resulting file(self.SketchImg)
            cv2.imwrite(filename, self.SketchImg)
    
    # Remove all widgets from the frame
    def Clear_Screen(self):
        for widget in self.frame.winfo_children():
            widget.destroy()

    def Exit(self):
        self.window.destroy()


if __name__ == "__main__":
    root = Tk()
    obj = SketchImage(root)
    root.mainloop()

Output

Python image to pencil sketch converter app

Summary

In this tutorial, we explore the creation of an Image to Pencil Sketch Converter using Python and the OpenCV library.

This tutorial is divided into two parts, the first part focuses on the core program, breaking down each step, from importing the OpenCV module to transforming an image into a pencil sketch. The process includes image loading, resizing, grayscale conversion, inverting, smoothing, and stylization.

Thatā€™s not all. In the second part, we enhance the program by adding a graphical user interface (GUI) using the Tkinter library. It creates an application for image-to-pencil sketch conversion.

Try this application to sketchify yourself and share your feedback! If you have any queries, let me know in the comments ā€“ Iā€™m here to chat!

Ready to explore moreĀ Python Projects? Visit our separate page packed with unique ideas. Here are a few examples to spark your interest:

Happy Sketching!

Share your love
Subhankar Rakshit
Subhankar Rakshit

Hey there! Iā€™m Subhankar Rakshit, the brains behind PySeek. Iā€™m a Post Graduate in Computer Science. PySeek is where I channel my love for Python programming and share it with the world through engaging and informative blogs.

Articles:Ā 194