Building an Image Cartoonization Web App with Python | by Ruben Winastwan | Towards Data Science

As an avid street-photography person who spend almost every weekend wandering around the city to hunt some photos, Adobe Lightroom is always my go-to software to edit my raw photos to make it more “instagrammable”.
This got me thinking, wouldn’t it be awesome if I can somehow create my own simple image editing software?
The emergence of open source computer vision library like OpenCV and open source app framework like Streamlit enables me to realize this idea. With under 100 lines of code, you can build your own image cartooning web app that somehow mimicking the functionality of Adobe Lightroom.
In this article, I want to show you how to build a simple web app to turn your image into cartoon-like image, depending on the filters, using OpenCV and Streamlit.
There are two main steps that we often need to do to convert an image into a cartoon: edge detection and region smoothing.
The main purpose of edge detection is obviously to emphasize the edge of an image, because normally a cartoon image has well-defined edges. Meanwhile, the main purpose of region smoothing is to remove the insignificant color boundaries and to reduce the noise or grain of the image, making the image less pixelated.
Depending on the filters, we can obtain different image cartoonization result. In this article, there are going to be four different filters:
Pencil SketchDetail EnhancementBilateral FilterPencil Edges
Pencil Sketch
Detail Enhancement
Bilateral Filter
Pencil Edges
Next, I want to show you how to apply each filters and what kind of result that you would expect from each of them.
With Pencil Sketch filter, your image will be converted into a sketch as if your image has been drawn using a pencil. Below is the entire code to convert your image into a pencil sketch using OpenCV.
It’s amazing that we can convert our image into a pencil sketch-like picture with only three lines of code using OpenCV. Now let me explain what actually happens with our image line-by-line.
In the first line, we convert our image from color into grayscale with cvtColor() function from OpenCV. I think this is pretty straightforward that the result from this line is that our image turns into its grayscale representation.
Next, we blur the image using Gaussian Blur. By blurring the grayscale image, we are basically smoothing the image, trying to reduce the noise of the image. Plus, blurring is the necessary step for us to detect the edge of the image.
To blur the image, we can use GaussianBlur() function from OpenCV. The (25, 25) that I put in the GaussianBlur() function there is the size of the kernel.
Since we use Gaussian Blur, the distribution of the pixel value in a kernel follows a normal distribution. The larger the number the kernel, the larger the standard deviation would be and hence, the stronger the blurring effect will be. Below is the example of the blurring result with different kernel size.
The final step would be dividing the original grayscale image with the blurred grayscale image. Dividing the image gives us a ratio of change between each pixel of two images. The stronger the blurry effects, the more the value of each pixel changes with respect to its origin and hence, it gives us sharper pencil sketch.
Below is the result of using Pencil Sketch filter.
In short, Detail Enhancement filter gives us a cartoon effect by sharpening the image, smoothing the colors, and enhancing the edges. Below is the entire code to convert your image into cartoon with this filter.
The first step is the same as before, that we need to convert the image into a grayscale image.
Next, instead of using Gaussian Blur, Median Blur is applied. To do this, we use medianBlur() function from OpenCV. Median Blur works by computing the median of pixel values that overlaps with the kernel, and then replacing its central pixel value with the computed median value. However, you can use Gaussian Blur if you wish.
Next, we need to detect the edges of the image. To do this, adaptive threshold is applied with adaptiveThreshold() function from OpenCV. The main goal of adaptive threshold is to convert each pixel values in each region of an image into either completely black or completely white, depending on the mean value of the pixel overlapped by the kernel.
Below is the visualization of what adaptive threshold does to the blurred image.
To make the image looks sharper, we can use detailEnhance() filter from OpenCV. There are two parameters that you need to specify as an argument:
sigma_s: This controls the size of the neighborhood that the value will be weighted to replace a pixel value in an image. The higher the value, the bigger the neighborhood. This leads to smoother image.
sigma_r: This is important if you want to preserve edges while smoothing the image. Small value yields in only very similar colors to be averaged (i.e. smoothed), while colors that differ much will stay intact.
Finally, we use the result of adaptive threshold as a mask. Then, merge the result of detail enhancement based on the value of the mask to create a sharp effect with well-defined edge.
Below is the example result of Detail Enhancement filters.
One big advantage of using Bilateral Filter is that we can smooth the image and the colors while preserving the edge at the same time. Below is the entire code to convert your image into cartoon-like with bilateral filtering.
If you take a closer look, all of the steps are similar with the one from Detail Enhancement filter, however this time instead of using detailEnhance() function, we use bilateralFilter()function from openCV.
The argument that you need to pass when you call this function is the same as the one with detailEnhance(), with only one additional parameter, which is the kernel size d. First, you need to specify the image source, then d, sigma_s, and sigma_r values that controls the smoothing effect and to preserve the edge.
Below is the result example of using Bilateral Filter.
Pencil Edges filter creates a new image that contains only significant edges and white background. To apply this filter, below is the complete code.
The first two steps are the same as the other filters. First, we convert the image into a grayscale image. Next, we blur the image with the kernel size of 25.
Now what’s different is the next step, in which we apply Laplacian filter to detect the edges. Depending on the size of the kernel, the value in a Laplacian filter can be different. If you want to know more about the value of Laplacian filter with different kernel size, you can read it here.
What Laplacian filter does is that it will emphasize the edge of an object with brighter and darker intensity than the gray-level inside of the object and the background of the image. Below is the example of before and after the application of Laplacian filter.
Next, we invert the result of Laplacian filter so that the darker gray color become brighter and vice versa. Finally, by applying the threshold() function from openCV, we can convert the grayscale image into either completely black or completely white, depending on the threshold value that we specified.
Below is the result example of Pencil Edge filter.
After we created the code for image cartoonization filters, now we are ready to create an image cartoonization web app.
As a first step, we need to put all of the codes for image cartoonization filters we have created into one function for an easy access. Notice that so far, we have hard-coded every parameter values like the size of the kernel and so on.
Now instead of hard-coding every parameter values, we can actually let the user to specify a value based on their preference using a slider. To do this, we can use streamlit.slider() function from Streamlit. Below is the example of its implementation.
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)scale_val = st.slider('Tune the brightness of your sketch (the higher the value, the brighter your sketch)', 0.0, 300.0, 250.0)kernel = st.slider('Tune the boldness of the edges of your sketch (the higher the value, the bolder the edges)', 1, 99, 25, step=2)gray_blur = cv2.GaussianBlur(gray, (kernel, kernel), 0)cartoon = cv2.divide(gray, gray_blur, scale= scale_val)
With this slider, you can create an interactive image cartoonization web app, just like Adobe Lightroom. The result of your image cartoonization will be changed and updated in real-time every time you tweak the value of the kernel and other parameters.
We can apply this streamlit.slider() in each of image cartoonization filter we have created above to replace the hard-coded parameter values.
Next, we need to add a widget such that the user can upload their own image that they want to convert into a cartoon. For this purpose, we can use streamlit.file_uploader() function from Streamlit. To add some texts into our web app, we can use streamlit.text() or streamlit.write() function from Streamlit.
After the user have uploaded their image, now we need to display the image, edit the image using one of the image cartoonization filter, and display the cartoonized image so that the user knows whether they want to tweak the slider further. To display the image, we can use streamlit.image() function from Streamlit.
Below is the implementation of how you can build an image cartoonization web app in under 100 lines of code.
Now you can open your Prompt, then head up into the working directory of your Python file which contains the code above. Next, you need to run the code with the following command.
streamlit run your_app_name.py
Finally, you’re able to play around with your image cartoonization web app on your local computer! Below is the sample of the web app.
This section is totally optional, but if you want to deploy your web app such that other people can also access your web app, you can deploy your web app using Heroku.
To deploy your web app to Heroku, the first thing is to create a Heroku account for free and then download the Heroku CLI.
The next thing you need to do is to create four additional files in the same directory as your Python file, which are:
requirements.txt: this is the text file to tell Heroku which dependencies you need to build the web app. Since in our web app we use four different libraries: opencv, numpy , Pillow , and streamlit , then we can write all of these libraries and their versions into requirements.txt
opencv-python==4.3.0.36streamlit==0.63.0Pillow==7.0.0numpy==1.18.1
setup.sh: this is the file to setup your configuration on Heroku. Write the following for this setup.sh file.
mkdir -p ~/.streamlit/                       echo "\                       [server]\n\                       headless = true\n\                       port = $PORT\n\                       enableCORS = false\n\                       \n\                       " > ~/.streamlit/config.toml
Procfile: this is the file to tell Heroku which and how the files should be executed. Write the following for the Procfile.
web: sh setup.sh && streamlit run cartoon_app.py
Aptfile: this is the file for Heroku buildpacks to enable OpenCV to operate within Heroku. Write the following for the Aptfile.
libsm6                       libxrender1                       libfontconfig1                       libice6
Next, open your Command Prompt, then heads up to the working directory of your Python file and these four additional files. In it, type heroku login in order for you to be logged into your Heroku account.
Now you can create a new app by typing heroku create your-app-name . To make sure that you’re inside of your newly created app, type the following:
heroku git:remote -a your-app-name
Next, you need to add a buildpack in your newly created app for OpenCV to be able to operate on Heroku. To add the necessary buildpack, type the following on your Heroku CLI.
heroku create --buildpack https://github.com/heroku/heroku-buildpack-apt.git
Now you are set. Next you need to initialize an empty git by typing git init followed by git add ., git commit , and git push heroku master commands.
git initgit add .git commit -m "Add your messages"git push heroku master
After that, the deployment process will begin and it may take some time to wait for this deployment process. At the end, Heroku will generate an URL of your newly deployed web app.
And that’s it! Now you’ve built your own image cartoonization web app that mimicking the functionality of Adobe Lightroom.
You can see the example of the deployed version of this image cartoonization web app here
or you can see the full code of this image cartoonization on my GitHub page.