<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hwei Geok Ng &#8211; Be on the Right Side of Change</title>
	<atom:link href="https://blog.finxter.com/author/hweigeokng/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.finxter.com</link>
	<description></description>
	<lastBuildDate>Tue, 03 Jan 2023 15:27:59 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://blog.finxter.com/wp-content/uploads/2020/08/cropped-cropped-finxter_nobackground-32x32.png</url>
	<title>Hwei Geok Ng &#8211; Be on the Right Side of Change</title>
	<link>https://blog.finxter.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>PixelLib &#8211; Segmenting Objects and Videos in Python</title>
		<link>https://blog.finxter.com/pixellib-segmenting-objects-and-videos-in-python/</link>
		
		<dc:creator><![CDATA[Hwei Geok Ng]]></dc:creator>
		<pubDate>Sat, 16 Oct 2021 19:42:53 +0000</pubDate>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=36809</guid>

					<description><![CDATA[This tutorial is about applying object segmentation in five lines of code. We show a beginner-friendly code implementation using the PixelLib library on Google Colab. Does machine learning have to be hard? Well, back then when everything must be coded from scratch, yes. But today, we are stepping on the shoulders of giants. Lots of ... <a title="PixelLib &#8211; Segmenting Objects and Videos in Python" class="read-more" href="https://blog.finxter.com/pixellib-segmenting-objects-and-videos-in-python/" aria-label="Read more about PixelLib &#8211; Segmenting Objects and Videos in Python">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This tutorial is about applying object segmentation in five lines of code. We show a beginner-friendly code implementation using the PixelLib library on Google Colab.</p>



<p class="wp-block-paragraph">Does machine learning have to be hard? Well, back then when everything must be coded from scratch, yes. But today, we are stepping on the shoulders of giants. Lots of libraries are available out there to help us apply machine learning easier, so we don&#8217;t have to reinvent the wheel. In this tutorial, we are going to look at PixelLib. It is an easy-to-use library that segments objects in images and videos with five lines of code. Five lines of code for deep learning? Exactly. This article will bring you from A to Z with code snippets and video complements. So read on and see it for yourself <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">You can also check out this machine learning project with a <strong>video guide</strong> and advanced tips and tricks on the <a href="https://academy.finxter.com/university/object-segmentation-pixellib/" target="_blank" rel="noreferrer noopener" title="https://academy.finxter.com/university/object-segmentation-pixellib/">Finxter Computer Science Academy</a>:</p>



<div class="wp-block-image"><figure class="aligncenter size-full is-resized"><a href="https://academy.finxter.com/university/object-segmentation-pixellib/" target="_blank" rel="noopener"><img fetchpriority="high" decoding="async" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-37.png" alt="" class="wp-image-36820" width="272" height="488" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-37.png 363w, https://blog.finxter.com/wp-content/uploads/2021/10/image-37-168x300.png 168w" sizes="(max-width: 272px) 100vw, 272px" /></a></figure></div>



<h2 class="wp-block-heading">Install and Import Modules</h2>



<p class="wp-block-paragraph">Feel free to download the script for this tutorial from <a href="https://github.com/finxter/object-segmentation-pixellib/blob/main/Object_Segmentation_PixelLib.ipynb" target="_blank" rel="noreferrer noopener" title="https://github.com/finxter/object-segmentation-pixellib/blob/main/Object_Segmentation_PixelLib.ipynb">this GitHub repo</a>. We will execute it on Google Colab and use some free GPU resources for model training. If you would like to try Google Colab out, head over to <a href="https://colab.research.google.com/" target="_blank" rel="noreferrer noopener" title="https://colab.research.google.com/">the site</a> and sign up using your Gmail account. It looks like a Jupyter Notebook but with its storage location in your Google Drive. Upload the script onto your Google Colab and execute the blocks as we guide you in this article.</p>



<p class="wp-block-paragraph">Execute the following command on a terminal or command prompt to clone any GitHub repo:</p>



<pre class="wp-block-preformatted">$ git clone https://github.com/username/project_name.git</pre>



<p class="wp-block-paragraph">The next step is to enable the GPU resource in our Colab environment. There are two ways to do this:</p>



<ul class="wp-block-list"><li><strong>Method 1</strong>: Click on the <strong>Edit</strong> tab. At the Notebook settings, choose GPU at the drop-down, and click Save.</li><li><strong>Method 2</strong>: Click on the <strong>Runtime</strong> tab. At the Change Runtime Type, choose GPU, and click Save.</li></ul>



<p class="wp-block-paragraph">Feel free to check out the video version of this tutorial for more in-depth explanations.</p>



<p class="wp-block-paragraph">Now, install the necessary packages using <em><a href="https://blog.finxter.com/how-to-install-pip-on-windows/" target="_blank" rel="noreferrer noopener" title="How To Install pip On Windows?">pip</a></em>:</p>



<pre class="wp-block-preformatted">$ <code>pip </code>install tensorflow--gpu pixellib --upgrade</pre>



<p class="wp-block-paragraph">As PixelLib relies on the <a href="https://blog.finxter.com/tensorflow-cheat-sheets/" target="_blank" rel="noreferrer noopener" title="[Collection] 10 Tensorflow Cheat Sheets Every ML Engineer Must Download, Print, and Study">TensorFlow </a>library, we need to install that as well. If your execution environment has no GPU resource, omit the “<em>&#8211;gpu</em>” for the installation.</p>



<p class="wp-block-paragraph">Now let&#8217;s import the required libraries:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import pixellib
from pixellib.instance import instance_segmentation
import matplotlib.pyplot as plt
</pre>



<p class="wp-block-paragraph">We imported the main <code>pixellib</code> library as well as its <code>instance_segmentation()</code> function. There you go, we just executed two out of five lines of code for object segmentation. And we also imported the <a href="https://blog.finxter.com/matplotlib-full-guide/" target="_blank" rel="noreferrer noopener" title="Matplotlib — A Simple Guide with Videos">Matplotlib </a>library to display images on our notebook. That’s all the libraries we need for this code project.</p>



<p class="wp-block-paragraph">Although we only focus on the instance segmentation, PixelLib has much more to offer. For example, background editing, semantic segmentation, and custom training. Check out its detailed <a href="https://github.com/ayoolaolafenwa/PixelLib" target="_blank" rel="noreferrer noopener" title="https://github.com/ayoolaolafenwa/PixelLib">GitHub repo</a> to learn more.</p>



<h2 class="wp-block-heading">Create Instance and Load Pretrained Model</h2>



<p class="wp-block-paragraph">To use the instance segmentation function, we must first initialize it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">segment_image = instance_segmentation()</pre>



<p class="wp-block-paragraph">The initialized function is passed to a variable named <code>segment_image</code>. PixelLib takes a pre-trained model and performs object segmentation on a given input. It then returns a bounding box and a boundary for every detected object in the input.</p>



<p class="wp-block-paragraph">We are going to use the Mask RCNN as the pre-trained model. Go ahead to <a href="https://github.com/ayoolaolafenwa/PixelLib/releases/tag/1.2" target="_blank" rel="noreferrer noopener" title="https://github.com/ayoolaolafenwa/PixelLib/releases/tag/1.2">this resource page</a> at the PixelLib GitHub repo and download the .h5 model file. We will also need a sample image and a sample video to test how well PixelLib performs. If you would like to use the same resources as this tutorial, they can be downloaded from <a href="https://www.flickr.com/photos/30604571@N00/5862679434/" target="_blank" rel="noreferrer noopener" title="https://www.flickr.com/photos/30604571@N00/5862679434/">this </a>site. Also, check out this relevant video:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe title="Short video of a busy city scene." width="937" height="527" src="https://www.youtube.com/embed/EivIBccZURA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph"> Also, feel free to use your images and videos to test the library out. Once you have the model and samples ready, upload them to Google Drive.</p>



<p class="wp-block-paragraph">Now, go back to your Google Colab interface and mount the Google Drive to it so that you can access the files. To do that, click on the Google Drive icon at the left of the interface for mounting – as shown in Figure 1.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img decoding="async" width="318" height="245" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-30.png" alt="" class="wp-image-36813" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-30.png 318w, https://blog.finxter.com/wp-content/uploads/2021/10/image-30-300x231.png 300w" sizes="(max-width: 318px) 100vw, 318px" /><figcaption><strong>Figure 1</strong>: Mount Google Drive on Google Colab.</figcaption></figure></div>



<p class="wp-block-paragraph">You will see a folder named “<em>drive</em>” appear on the data repository once it is mounted. You can also upload files directly onto Google Colab, but it will not be permanent storage. Once you are off your Google Colab tab, the files will not be there the next time you open them.</p>



<p class="wp-block-paragraph">Now you have access to the files. Time to reveal the fourth line of code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">segment_image.load_model("/content/drive/MyDrive/mask_rcnn_coco.h5")</pre>



<p class="wp-block-paragraph">The <code>load_model()</code> function takes a string path and uses the model for the segmentation work. Your folder location might be different than the sample code, so adjust accordingly.</p>



<h2 class="wp-block-heading">Object Segmentation on Images</h2>



<p class="wp-block-paragraph">Let’s display the sample image using matplotlib and see how it looks like:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">plt.figure(figsize=(26, 18))
im_bike_lane = plt.imread("/content/drive/MyDrive/bike-lane.jpg")
plt.imshow(im_bike_lane)
</pre>



<p class="wp-block-paragraph">We defined a plotting figure of size 26 inches in width and 18 inches in height. Then, we loaded and displayed the sample image using the <code><a href="https://blog.finxter.com/display-modify-and-save-images-with-matplotlib/" target="_blank" rel="noreferrer noopener" title="How to Display, Modify and Save Images in Matplotlib">imread()</a></code> and <code><a href="https://blog.finxter.com/matplotlib-imshow/" target="_blank" rel="noreferrer noopener" title="Matplotlib Imshow — A Helpful Illustrated Guide">imshow()</a></code> functions. Figure 2 shows the image, which is a normal street view consisting of people, cars, bicycles, etc.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="398" height="266" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-31.png" alt="" class="wp-image-36814" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-31.png 398w, https://blog.finxter.com/wp-content/uploads/2021/10/image-31-300x201.png 300w" sizes="auto, (max-width: 398px) 100vw, 398px" /><figcaption><strong>Figure 2</strong>: Our sample image, a street view photo (<a href="https://www.flickr.com/photos/30604571@N00/5862679434/" target="_blank" rel="noreferrer noopener" title="https://www.flickr.com/photos/30604571@N00/5862679434/">source</a>).</figcaption></figure></div>



<p class="wp-block-paragraph">OK, now comes the code for instance segmentation. Execute the following line:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">segment_image.segmentImage("/content/drive/MyDrive/bike-lane.jpg",
       show_bboxes=True, 
       extract_segmented_objects=False, 
       save_extracted_objects=False, 
       output_image_name="/content/drive/MyDrive/ bike-lane_segmented.jpg")
</pre>



<p class="wp-block-paragraph">We set the configurations for <code>segmentImage()</code> and let it do the heavy-lifting. The <code>show_bboxes</code> config is set to <code>True</code> because we want every detected object to having a bounding box. We set <code>extract_segmented_objects</code> to <code>False</code> to not extract objects as individual images. The <code>save_extracted_objects</code> is set to <code>False</code> to not save objects as separate files. The <code>output_image_name</code> config is the path where you want the new image to be saved. Figure 3 shows the processed image with all detected objects.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="412" height="275" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-32.png" alt="" class="wp-image-36815" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-32.png 412w, https://blog.finxter.com/wp-content/uploads/2021/10/image-32-300x200.png 300w" sizes="auto, (max-width: 412px) 100vw, 412px" /><figcaption><strong>Figure 3</strong>: The sample image overlaid with segmented object boundaries and bounding boxes.</figcaption></figure></div>



<p class="wp-block-paragraph">Not bad! Without model training and tuning, PixelLib managed to detect objects from the image. Categories like cars, bicycles, persons and traffic lights are detected. If you chose to save object segments, you will see object crop-outs saved as individual files.</p>



<p class="wp-block-paragraph">What if we want to segment only one, or a handful of categories instead of all categories? That can also be easily configured, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">target_classes = segment_image.select_target_classes(person=True)
#or
target_classes = segment_image.select_target_classes(car=True,bicycle=True)

segment_image.segmentImage("/content/drive/MyDrive/bike-lane.jpg", 
      segment_target_classes=target_classes,
      show_bboxes=True, 
      extract_segmented_objects=False, 
      save_extracted_objects=False, 
      output_image_name="/content/drive/MyDrive/bike-lane_segmented.jpg")</pre>



<p class="wp-block-paragraph">We gave the <code>select_target_classes()</code> function the category names to segment for. The configuration is passed to the <code>target_classes</code> variable. The first <code>target_classes</code> variable has config <em><code>“person=True”</code></em>. Only objects that are predicted as <code>Person</code> in the image will be segmented. Likewise, the second <code>target_classes</code> variable sets <em>Car</em> and <em>Bicycle</em> categories as <em>True</em>. That means we want to segment only these two objects. Figure 4 shows the segmented image with the target class <em>Person</em>. Figure 5 shows the one with target classes <em>Car</em> and <em>Bicycle</em>.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="399" height="266" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-33.png" alt="" class="wp-image-36816" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-33.png 399w, https://blog.finxter.com/wp-content/uploads/2021/10/image-33-300x200.png 300w" sizes="auto, (max-width: 399px) 100vw, 399px" /><figcaption><strong>Figure 4</strong>: Segmented objects predicted with the label “<em>Person</em>”.</figcaption></figure></div>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="402" height="268" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-34.png" alt="" class="wp-image-36817" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-34.png 402w, https://blog.finxter.com/wp-content/uploads/2021/10/image-34-300x200.png 300w" sizes="auto, (max-width: 402px) 100vw, 402px" /><figcaption><strong>Figure 5:</strong> Segmented objects predicted with labels “<em>Car</em>” and “<em>Bicycle</em>”.</figcaption></figure></div>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">So, what do you think about the object segmentation ability of PixelLib? Impressive, isn&#8217;t it? Objects are detected accurately according to the labels. Boundaries are defined precisely around the detected objects. Some objects are detected accurately even when half of their shapes are occluded.</p>



<h2 class="wp-block-heading">Object Segmentation on Videos</h2>



<p class="wp-block-paragraph">So far, so good on images. Now let’s see how PixelLib performs for video segmentation. Figure 6 shows a screenshot of the video, which is another street view with humans and vehicles.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="424" height="259" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-35.png" alt="" class="wp-image-36818" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-35.png 424w, https://blog.finxter.com/wp-content/uploads/2021/10/image-35-300x183.png 300w" sizes="auto, (max-width: 424px) 100vw, 424px" /><figcaption><strong>Figure 6</strong>: Our sample video, a recording of street view (<a href="https://www.youtube.com/watch?v=EivIBccZURA" target="_blank" rel="noreferrer noopener" title="https://www.youtube.com/watch?v=EivIBccZURA">source</a>).</figcaption></figure></div>



<p class="wp-block-paragraph">The code for video instance segmentation is like image segmentation. Surely you get the drill now with the straightforward function names. Execute the following code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">segment_video = instance_segmentation()
segment_video.load_model("/content/drive/MyDrive/mask_rcnn_coco.h5")
segment_video.process_video("/content/drive/MyDrive/city-scene.mp4", 
       show_bboxes=True,  
       extract_segmented_objects=True,
       save_extracted_objects=False, 
       frames_per_second= 5,  
       output_video_name="/content/drive/MyDrive/city-scene_segmented.mp4")
</pre>



<p class="wp-block-paragraph">We initialized the <code>instance_segmentation()</code> function again and passed it to the <code>segment_video</code> variable. The same pre-trained model is loaded using the <code>load_model()</code> function. We used the <code>process_video()</code> function and gave it the same configs. The additional <code>frames_per_second</code> config is the frame rate of the video to be saved.</p>



<p class="wp-block-paragraph">You might realize that it took a long time for PixelLib to process the video compared to the image. That is normal because videos are a collection of images. PixelLib processes the video frame by frame and performs object segmentation. Figure 7 shows a screenshot of how a segmented video frame looks like.</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="422" height="232" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-36.png" alt="" class="wp-image-36819" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-36.png 422w, https://blog.finxter.com/wp-content/uploads/2021/10/image-36-300x165.png 300w" sizes="auto, (max-width: 422px) 100vw, 422px" /><figcaption><strong>Figure 7:</strong> Our sample video with object segmentation.</figcaption></figure></div>



<p class="wp-block-paragraph">As expected, the library accurately detected objects in the video. That&#8217;s a ready-to-use object segmentation library without any hyperparameter tweaking! If you think PixelLib is useful, check out its repo to explore more functionalities.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">That’s it! You have learned that there exists a well-performing object segmentation library. And it can be implemented in five lines of code. Another useful tool to add to your machine learning skillset. I hope this was a fun learning process for you! If you encounter any issues and would like an in-depth walkthrough of the code, the video explanation is there to help you out. Happy learning!</p>



<p class="wp-block-paragraph">You can check out this tutorial with a video guide and advanced guidance on the <a href="https://academy.finxter.com/university/object-segmentation-pixellib/" target="_blank" rel="noreferrer noopener" title="https://academy.finxter.com/university/object-segmentation-pixellib/">Finxter Computer Science Academy</a>:</p>



<div class="wp-block-image"><figure class="aligncenter size-full is-resized"><a href="https://academy.finxter.com/university/object-segmentation-pixellib/" target="_blank" rel="noopener"><img fetchpriority="high" decoding="async" src="https://blog.finxter.com/wp-content/uploads/2021/10/image-37.png" alt="" class="wp-image-36820" width="272" height="488" srcset="https://blog.finxter.com/wp-content/uploads/2021/10/image-37.png 363w, https://blog.finxter.com/wp-content/uploads/2021/10/image-37-168x300.png 168w" sizes="(max-width: 272px) 100vw, 272px" /></a></figure></div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Classify Star Wars Lego Images using CNN and Transfer Learning</title>
		<link>https://blog.finxter.com/classification-of-star-wars-lego-images-using-cnn-and-transfer-learning/</link>
		
		<dc:creator><![CDATA[Hwei Geok Ng]]></dc:creator>
		<pubDate>Wed, 21 Jul 2021 07:05:49 +0000</pubDate>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[TensorFlow]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=33108</guid>

					<description><![CDATA[This tutorial is about training deep learning (DL) models to classify Star Wars Lego images. We use the TensorFlow library to create and compare the image classifiers. Are you looking for interesting deep learning projects that are suitable for beginners? Do not worry, this is not another MNIST image classification tutorial. Instead, we are going ... <a title="How to Classify Star Wars Lego Images using CNN and Transfer Learning" class="read-more" href="https://blog.finxter.com/classification-of-star-wars-lego-images-using-cnn-and-transfer-learning/" aria-label="Read more about How to Classify Star Wars Lego Images using CNN and Transfer Learning">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This tutorial is about training deep learning (DL) models to classify Star Wars Lego images. We use the TensorFlow library to create and compare the image classifiers.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="How to Classify Star Wars Lego Images using CNN and Transfer Learning" width="937" height="527" src="https://www.youtube.com/embed/2jNWij0eK0I?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph">Are you looking for interesting deep learning projects that are suitable for beginners? Do not worry, this is not another MNIST image classification tutorial. Instead, we are going to classify some Star Wars Lego images using the <a href="https://blog.finxter.com/tensorflow-overview/" target="_blank" rel="noreferrer noopener" title="TensorFlow — A Helpful Illustrated Guide">TensorFlow </a>library. This tutorial will sharpen your knowledge about convolutional neural networks and transfer learning. Intrigued? Let’s get started.</p>



<h2 class="wp-block-heading">Install and Import Modules</h2>



<p class="wp-block-paragraph">Feel free to download the script for this tutorial from <a href="https://github.com/finxter/image-classification-star-wars-lego" target="_blank" rel="noreferrer noopener" title="https://github.com/finxter/image-classification-star-wars-lego">this GitHub repo</a>. We will execute it in Google Colab and use some free GPU resources for model training. If you would like to try Google Colab out, <a href="https://colab.research.google.com/" target="_blank" rel="noreferrer noopener" title="https://colab.research.google.com/">head over to the site</a> and sign up using your Gmail account. It looks like Jupyter Notebook but with its storage location in your Google Drive. Upload the script onto your Google Colab and execute it along as you read through this article.</p>



<p class="wp-block-paragraph">Execute the following command on a terminal or command prompt to clone any GitHub repo:</p>



<pre class="wp-block-preformatted"><code>$ git clone https://github.com/username/project_name.git</code></pre>



<p class="wp-block-paragraph">The second step is to enable the GPU resource in our Colab environment. There are two ways to do this:</p>



<ul class="wp-block-list"><li><strong>Method 1:</strong> Click on the Edit tab. At the Notebook settings, choose GPU at the drop-down, and click Save.</li><li><strong>Method 2:</strong> Click on the Runtime tab. At the Change Runtime Type, choose GPU, and click Save.</li></ul>



<p class="wp-block-paragraph">Feel free to check out the video version of this tutorial for more in-depth explanations.</p>



<p class="wp-block-paragraph">Now, install the necessary packages using <em><a href="https://blog.finxter.com/how-to-install-pip-on-windows/" target="_blank" rel="noreferrer noopener" title="How To Install pip On Windows?">pip</a></em>:</p>



<pre class="wp-block-preformatted"><code>$ pip install numpy pandas matplotlib seaborn tensorflow</code></pre>



<p class="wp-block-paragraph">As well as importing all the required libraries:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import os
import math
import random
import shutil

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sn

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
</pre>



<p class="wp-block-paragraph">We need the first four libraries to restructure data folders. The <a rel="noreferrer noopener" title="NumPy Tutorial – Everything You Need to Know to Get Started" href="https://blog.finxter.com/numpy-tutorial/" target="_blank">Numpy </a>and <a rel="noreferrer noopener" title="10 Minutes to Pandas (in 5 Minutes)" href="https://blog.finxter.com/pandas-quickstart/" target="_blank">Pandas </a>libraries will be used for data manipulation. The <a rel="noreferrer noopener" title="Matplotlib — A Simple Guide with Videos" href="https://blog.finxter.com/matplotlib-full-guide/" target="_blank">Matplotlib </a>and <a rel="noreferrer noopener" title="Creating Beautiful Heatmaps with Seaborn" href="https://blog.finxter.com/heatmaps-with-seaborn/" target="_blank">Seaborn </a>libraries will be used to display plots and figures. The <a rel="noreferrer noopener" title="[Collection] 10 Tensorflow Cheat Sheets Every ML Engineer Must Download, Print, and Study" href="https://blog.finxter.com/tensorflow-cheat-sheets/" target="_blank">TensorFlow </a>library is what we will use for machine learning modeling.</p>



<p class="has-global-color-8-background-color has-background wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Note</strong>: We heavily use the <code>keras</code> library in this tutorial. Keras simplifies the implementation of deep learning models with TensorFlow. </p>



<p class="wp-embed-aspect-16-9 wp-has-aspect-ratio wp-block-paragraph">If you&#8217;re interested in Keras, feel free to watch this video and read the <a rel="noreferrer noopener" href="https://blog.finxter.com/keras-developer-income-and-opportunity/" target="_blank">associated blog article on the income levels of Keras developers</a>:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="From Keras to $65/h+ on Upwork... Juan" width="937" height="527" src="https://www.youtube.com/embed/rEQ3Jr3iX6U?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph">To check if GPU is enabled in our notebook environment, execute the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">tf.test.gpu_device_name()</pre>



<p class="wp-block-paragraph">If a GPU is allocated for your notebook, you will see a printout like <code>‘/device: GPU:0’</code> instead of an empty string.</p>



<p class="wp-block-paragraph">Execute the following line to check which GPU is assigned for you:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">!nvidia-smi</pre>



<p class="wp-block-paragraph">Locate the GPU name at the printout. For example, <code>‘Tesla P4’</code>.</p>



<h2 class="wp-block-heading">Load Dataset and Preprocess Data</h2>



<p class="wp-block-paragraph">So far, so good. Now, we need to download the dataset, which is the <strong><em>Lego Minifigures</em></strong> dataset from <a href="https://www.kaggle.com/ihelon/lego-minifigures-classification" target="_blank" rel="noreferrer noopener" title="https://www.kaggle.com/ihelon/lego-minifigures-classification">Kaggle</a>. We are going to use only the Star Wars folder for this tutorial. There are 15 subfolders of images in the Star Wars folder. To simplify things, we are going to use only the first five folders out of 15. So, go ahead and remove everything except for the first five folders of Star Wars. Then, upload this data folder to your Google Drive (the one that has the same Gmail account as your Colab).</p>



<p class="wp-block-paragraph">Dataset uploaded. Now, go back to our Google Colab interface and mount the Google Drive to it so that we can access the data. To do that, click on the Google Drive icon at the left of the interface to mount it – as shown in Figure 1.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="318" height="245" src="https://blog.finxter.com/wp-content/uploads/2021/07/image-12.png" alt="" class="wp-image-33112" srcset="https://blog.finxter.com/wp-content/uploads/2021/07/image-12.png 318w, https://blog.finxter.com/wp-content/uploads/2021/07/image-12-300x231.png 300w" sizes="auto, (max-width: 318px) 100vw, 318px" /><figcaption><strong>Figure 1</strong>: Mount Google Drive on Google Colab.</figcaption></figure></div>



<p class="wp-block-paragraph">You will see a folder named <em>“drive”</em> appear on the data repository once it is mounted. Next, we are going to restructure our data folder. We want to create a train set, a validation set, and a test set for modeling and evaluation.</p>



<p class="wp-block-paragraph">Execute the following code to restructure the data folder:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">BASE_DIR = '/content/drive/MyDrive/star-wars/'
names = ["YODA", "LUKE SKYWALKER", "R2-D2", "MACE WINDU", "GENERAL GRIEVOUS"]
train_proportion = 0.6
val_proportion = 0.25
total_train = 0
total_val = 0
total_test = 0</pre>



<p class="wp-block-paragraph">In the code, we defined a base directory for the Google Drive folder. We also reassigned names to the five data subfolders. We defined the proportion of image distribution in each folder to be copied onto the new sets. For example, in a subfolder of 10 images, 6 images will be copied to the train set, 3 to the validation set, and the remaining images to the test set. The variables <code>total_train</code>, <code>total_val</code>, and <code>total_test</code> are counters to calculate the total number of images in the said folders.</p>



<p class="wp-block-paragraph">Execute the following lines to create new folders:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">if not os.path.isdir(f'{BASE_DIR}train/'):
    for name in names:
        os.makedirs(f'{BASE_DIR}train/{name}')
        os.makedirs(f'{BASE_DIR}val/{name}')
        os.makedirs(f'{BASE_DIR}test/{name}')
</pre>



<p class="wp-block-paragraph">Executing the following code will copy the images over to the new folders:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">orig_folders = ["0001/", "0002/", "0003/", "0004/", "0005/"]

for folder_idx, folder in enumerate(orig_folders):
    files = os.listdir(BASE_DIR + folder)

    folder_name = names[folder_idx]
    number_of_images = len([name for name in files])

    n_train = int((number_of_images * train_proportion) + 0.5)
    n_valid = int((number_of_images * val_proportion) + 0.5)
    n_test = number_of_images - n_train - n_valid

    total_train += n_train
    total_val += n_valid
    total_test += n_test

    print(f'Folder {folder_name} has {number_of_images} images in total:\n train - {n_train}, val - {n_valid}, test - {n_test}\n')

    # copy images from original folders to the new ones
    for idx, file in enumerate(files):
        file_name = BASE_DIR + folder + file
        if idx &lt; n_train:
            shutil.copy(file_name, f'{BASE_DIR}train/{folder_name}')
        elif idx &lt; n_train + n_valid:
            shutil.copy(file_name, f'{BASE_DIR}val/{folder_name}')
        else:
            shutil.copy(file_name, f'{BASE_DIR}test/{folder_name}')
</pre>



<p class="wp-block-paragraph">The <a href="https://blog.finxter.com/python-loops/" target="_blank" rel="noreferrer noopener" title="Python Loops">for loop</a> iterates through each subfolder and copy the images to the new folders. You will see that three new data folders are created, each containing five image folders.</p>



<p class="wp-block-paragraph">We can now proceed to the data pre-processing step. One of the common pre-processing techniques for image data is normalization. It is to squeeze the value of input images from a range of 0-255 to a range of 0-1. That usually helps for better model training and convergence.</p>



<p class="wp-block-paragraph">Execute the following lines to create three data generators:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">train_gen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255, 
	     rotation_range=20, horizontal_flip=True, 
             width_shift_range=0.2, height_shift_range=0.2, 
             shear_range=0.2, zoom_range=0.2)
valid_gen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
test_gen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
</pre>



<p class="wp-block-paragraph">Data generators load data in batch, perform data pre-processing and pass them to a machine learning model. The <em>rescale=1./25</em>5 configuration is for image normalization. More pre-processing techniques are also configured for the train generator.</p>



<p class="wp-block-paragraph">Next, execute the following lines to load the data in batches using data generators:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">target_size = (256, 256)
batch_size = 4

train_batches = train_gen.flow_from_directory(
    f'{BASE_DIR}train',
    target_size=target_size,
    class_mode='sparse',
    batch_size=batch_size,
    shuffle=True,
    color_mode="rgb",
    classes=names   
)

val_batches = valid_gen.flow_from_directory(
    f'{BASE_DIR}val',
    target_size=target_size,
    class_mode='sparse',
    batch_size=batch_size,
    shuffle=False,
    color_mode="rgb",
    classes=names
)

test_batches = test_gen.flow_from_directory(
    f'{BASE_DIR}test',
    target_size=target_size,
    class_mode='sparse',
    batch_size=batch_size,
    shuffle=False,
    color_mode="rgb",
    classes=names
)
</pre>



<p class="wp-block-paragraph">We define the target input size as (256, 256) with a batch size of 4. The <code>flow_from_directory()</code> function of the data generators pulls the data from the given directory one batch at a time.</p>



<p class="wp-block-paragraph">The rest of the code in this section is to check how data batches look like. Execute the code in the script and see if the outcome makes sense.</p>



<h2 class="wp-block-heading">Train and Evaluate A CNN Model</h2>



<p class="wp-block-paragraph">It seems like we spent a lot of time fiddling with the data. That is where most data scientists spent their time. Good data processing is crucial for training machine learning models. You will thank yourself for the effort later.</p>



<p class="wp-block-paragraph">Now it’s time to get our hands dirty for machine learning! Execute the following lines to create a basic convolutional neural network model:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">model = keras.models.Sequential()
model.add(layers.Conv2D(32, (3,3), strides=(1,1), padding="valid", 
activation='relu', input_shape=(256, 256,3)))
model.add(layers.MaxPool2D((2,2)))
model.add(layers.Conv2D(64, 3, activation='relu'))
model.add(layers.MaxPool2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(5))
</pre>



<p class="wp-block-paragraph">If you want to see how the model architecture looks like, execute this line:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">model.summary()</pre>



<p class="wp-block-paragraph">From the printout, we see that the model consists of two convolution layers and max-pooling layers, as well as a flatten layer. It is followed by a dense layer and an output layer of 5 units. The unit of output layer determines the number of categories for model prediction.</p>



<p class="wp-block-paragraph">Let&#8217;s compile the model with the loss, accuracy, and optimization functions:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">loss = keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optim = keras.optimizers.Adam(learning_rate=0.001)
metrics = ["accuracy"]

model.compile(optimizer=optim, loss=loss, metrics=metrics)
</pre>



<p class="wp-block-paragraph">Note that there is no one-hot encoding for the labels (and no Softmax at the output layer). The loss function is chosen based on that.</p>



<p class="wp-block-paragraph">Execute the following lines for the actual model training:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">epochs = 30

early_stopping = keras.callbacks.EarlyStopping(monitor="val_loss", 
patience=5, verbose=2)

history = model.fit(train_batches, validation_data=val_batches,
callbacks=[early_stopping],epochs=epochs, verbose=2)

model.save(f"{BASE_DIR}lego_model.h5")
</pre>



<p class="wp-block-paragraph">We defined 30 epochs and an <code>EarlyStopping() </code>function to the <code><a href="https://blog.finxter.com/sklearn-fit-vs-transform-vs-fit_transform-whats-the-difference/" target="_blank" rel="noreferrer noopener" title="Sklearn fit() vs transform() vs fit_transform() – What’s the Difference?">fit()</a></code> function. The <code>EarlyStopping()</code> function will end the model training when the criteria are fulfilled. The <code>fit()</code> function trains and validates the model. We also saved the model as an H5 file when the training is completed.</p>



<p class="wp-block-paragraph">Let’s check out how the model performs by plotting the loss and accuracy outcomes:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='train loss')
plt.plot(history.history['val_loss'], label='valid loss')
plt.grid()
plt.legend(fontsize=15)

plt.subplot(1, 2, 2)
plt.plot(history.history['accuracy'], label='train acc')
plt.plot(history.history['val_accuracy'], label='valid acc')
plt.grid()
plt.legend(fontsize=15)
</pre>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="602" height="229" src="https://blog.finxter.com/wp-content/uploads/2021/07/image-13.png" alt="" class="wp-image-33115" srcset="https://blog.finxter.com/wp-content/uploads/2021/07/image-13.png 602w, https://blog.finxter.com/wp-content/uploads/2021/07/image-13-300x114.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /><figcaption><strong>Figure 2</strong>: Losses and accuracies of the basic CNN model.</figcaption></figure></div>



<p class="wp-block-paragraph">As shown in Figure 2, most of the train and validation losses are in the high range of 1.0 and 2.0. The train and validation accuracies fluctuated a lot. Generally, the model outcome is not optimal.</p>



<p class="wp-block-paragraph">We can see the model performance more obvious with images and predictions. Execute the following lines to make predictions with test data and plot the outcome:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">predictions = model.predict(test_batches)
predictions = tf.nn.softmax(predictions)
labels = np.argmax(predictions, axis=1)

print(test_batches[0][1])
print(labels[0:4])

show(test_batches[0], labels[0:4])</pre>



<p class="wp-block-paragraph">Note that Softmax is added here to compare the result with respective labels. This is how the prediction looks like:</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="323" height="326" src="https://blog.finxter.com/wp-content/uploads/2021/07/image-14.png" alt="" class="wp-image-33116" srcset="https://blog.finxter.com/wp-content/uploads/2021/07/image-14.png 323w, https://blog.finxter.com/wp-content/uploads/2021/07/image-14-297x300.png 297w, https://blog.finxter.com/wp-content/uploads/2021/07/image-14-150x150.png 150w, https://blog.finxter.com/wp-content/uploads/2021/07/image-14-120x120.png 120w" sizes="auto, (max-width: 323px) 100vw, 323px" /><figcaption><strong>Figure 3</strong>: Original labels vs predicted labels of the basic CNN model, with a sample batch of test data.</figcaption></figure></div>



<p class="wp-block-paragraph">As we can see, the model predicted all four sample test data incorrectly. It categorized most of the inputs as R2-D2, which shows a sign of overfitting. So, how do we go about improving the model performance?</p>



<h2 class="wp-block-heading">Comparison with a Transfer Learning Model</h2>



<p class="wp-block-paragraph">There are many things that we can do to improve the model. An example would be to adjust the hyperparameters. We will instead replace the model architecture with a transfer learning model. Let&#8217;s see if this can yield a better model performance.</p>



<p class="wp-block-paragraph">Create a transfer learning model based on the <a href="https://keras.io/api/applications/vgg/" target="_blank" rel="noreferrer noopener" title="https://keras.io/api/applications/vgg/">VGG16 architecture</a>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">vgg_model = tf.keras.applications.vgg16.VGG16()

model = keras.models.Sequential()
for layer in vgg_model.layers[0:-1]:
    model.add(layer)

for layer in model.layers:
    layer.trainable = False
</pre>



<p class="wp-block-paragraph">Here, we download the pre-trained weights of the VGG16 model. We add all layers except the output layer to a new model and make the weights unchangeable. Then, we add an output layer to the model, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">model.add(layers.Dense(5))</pre>



<p class="wp-block-paragraph">That’s it, now we have a transfer learning model with a custom output layer. We only need to train the last layer while all other layers stay the same. Compile the model with the same configurations as the basic CNN model:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">loss = keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optim = keras.optimizers.Adam(learning_rate=0.001)
metrics = ["accuracy"]

model.compile(optimizer=optim, loss=loss, metrics=metrics)
</pre>



<p class="wp-block-paragraph">The VGG16 function from TensorFlow comes with its pre-processing function. We use it for all the data generators:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">preprocess_input = tf.keras.applications.vgg16.preprocess_input

train_gen = keras.preprocessing.image.ImageDataGenerator(preprocessing_function=preprocess_input)
valid_gen = keras.preprocessing.image.ImageDataGenerator(preprocessing_function=preprocess_input)
test_gen = keras.preprocessing.image.ImageDataGenerator(preprocessing_function=preprocess_input)</pre>



<p class="wp-block-paragraph">Train the model using the same workflow we defined earlier, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">target_size = (224, 224)
batch_size = 4

train_batches = train_gen.flow_from_directory(
    f'{BASE_DIR}train',
    target_size=target_size,
    class_mode='sparse',
    batch_size=batch_size,
    shuffle=True,
    color_mode="rgb",
    classes=names   
)

val_batches = valid_gen.flow_from_directory(
    f'{BASE_DIR}val',
    target_size=target_size,
    class_mode='sparse',
    batch_size=batch_size,
    shuffle=False,
    color_mode="rgb",
    classes=names
)

test_batches = test_gen.flow_from_directory(
    f'{BASE_DIR}test',
    target_size=target_size,
    class_mode='sparse',
    batch_size=batch_size,
    shuffle=False,
    color_mode="rgb",
    classes=names
)

epochs = 30

# callbacks
early_stopping = keras.callbacks.EarlyStopping(
    monitor="val_loss",
    patience=5,
    verbose=2)

history = model.fit(train_batches, 
                    validation_data=val_batches,
                    callbacks=[early_stopping],
                    epochs=epochs, verbose=2)

model.save(f"{BASE_DIR}lego_model_transfer-learning.h5")
</pre>



<p class="wp-block-paragraph">Done with model training. Now we plot its losses and accuracies, as well as testing the model with the same batch of test data:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='train loss')
plt.plot(history.history['val_loss'], label='valid loss')
plt.grid()
plt.legend(fontsize=15)

plt.subplot(1, 2, 2)
plt.plot(history.history['accuracy'], label='train acc')
plt.plot(history.history['val_accuracy'], label='valid acc')
plt.grid()
plt.legend(fontsize=15);
</pre>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="602" height="225" src="https://blog.finxter.com/wp-content/uploads/2021/07/image-15.png" alt="" class="wp-image-33119" srcset="https://blog.finxter.com/wp-content/uploads/2021/07/image-15.png 602w, https://blog.finxter.com/wp-content/uploads/2021/07/image-15-300x112.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /><figcaption><strong>Figure 4</strong>: Losses and accuracies of the transfer learning CNN model.</figcaption></figure></div>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">model.evaluate(test_batches, verbose=2)

# make some predictions
predictions = model.predict(test_batches)
predictions = tf.nn.softmax(predictions)
labels = np.argmax(predictions, axis=1)

print(test_batches[0][1])
print(labels[0:4])

show(test_batches[0], labels[0:4])
</pre>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="327" height="331" src="https://blog.finxter.com/wp-content/uploads/2021/07/image-16.png" alt="" class="wp-image-33121" srcset="https://blog.finxter.com/wp-content/uploads/2021/07/image-16.png 327w, https://blog.finxter.com/wp-content/uploads/2021/07/image-16-296x300.png 296w, https://blog.finxter.com/wp-content/uploads/2021/07/image-16-120x120.png 120w" sizes="auto, (max-width: 327px) 100vw, 327px" /><figcaption><strong>Figure 5</strong>: Original labels vs predicted labels of the transfer learning CNN model.</figcaption></figure></div>



<p class="wp-block-paragraph">As shown in Figure 5, the model got three out of four sample data right. The training loss in Figure 4 looks better than the previous model. Both the train and validation accuracies achieved a higher and less fluctuating outcome. So we can say that the transfer learning model performs better than the basic CNN model. Note that both the models can be further optimized, so do not take this code example as an end-all result.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">Yay! We learned about image classification by implementing a basic CNN and a transfer learning CNN. I hope this was a fun learning process for you! If you encounter any issues and would like an in-depth walkthrough of the code, the video explanation is there to help you out. Happy learning!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenCV Course Part 1: Working with Images</title>
		<link>https://blog.finxter.com/opencv-working-with-images/</link>
		
		<dc:creator><![CDATA[Hwei Geok Ng]]></dc:creator>
		<pubDate>Sat, 03 Apr 2021 18:41:28 +0000</pubDate>
				<category><![CDATA[Image Processing]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=27387</guid>

					<description><![CDATA[]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="[OpenCV Course 1/5] Working with Images" width="937" height="527" src="https://www.youtube.com/embed/4eHurXUGGaM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Python OpenCV Image Processing &#8211; Resize, Blend, Blur, Threshold, Convert</title>
		<link>https://blog.finxter.com/five-useful-image-processing-techniques-in-python-using-opencv/</link>
		
		<dc:creator><![CDATA[Hwei Geok Ng]]></dc:creator>
		<pubDate>Mon, 22 Mar 2021 20:48:17 +0000</pubDate>
				<category><![CDATA[Data Science]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Matplotlib]]></category>
		<category><![CDATA[OpenCV]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[image processing projects using opencv]]></category>
		<category><![CDATA[opencv python example]]></category>
		<category><![CDATA[opencv python image processing]]></category>
		<category><![CDATA[opencv tutorial]]></category>
		<category><![CDATA[python image processing tutorial]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=26735</guid>

					<description><![CDATA[This tutorial is an introduction to the OpenCV library. Learn how to convert color channels, resize, blend, blur, and threshold images in Python. The OpenCV [1] library contains most of the functions we need for working with images. Handling images in programming requires a different intuition than handling text data. An image is made up ... <a title="Python OpenCV Image Processing &#8211; Resize, Blend, Blur, Threshold, Convert" class="read-more" href="https://blog.finxter.com/five-useful-image-processing-techniques-in-python-using-opencv/" aria-label="Read more about Python OpenCV Image Processing &#8211; Resize, Blend, Blur, Threshold, Convert">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This tutorial is an introduction to the OpenCV library. Learn how to convert color channels, resize, blend, blur, and threshold images in Python.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Python OpenCV Image Processing - Resize, Blend, Blur, Threshold, Convert" width="937" height="527" src="https://www.youtube.com/embed/M_TV4cca7oQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph">The OpenCV [1] library contains most of the functions we need for working with images. Handling images in programming requires a different intuition than handling text data. An image is made up of pixels. It looks like a spreadsheet full of cells with numerical values when zoomed in. Each pixel usually contains a value ranging between 0 to 255. The value indicates the degree of brightness for the color it is assigned to. So, how do we work with images in Python? We first need to load them as NumPy arrays, converting all image pixels into numerical values. Only then we can use different computer vision techniques to manipulate them.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="521" height="347" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-71.png" alt="Photo by Chris Lawton on Unsplash.com." class="wp-image-26736" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-71.png 521w, https://blog.finxter.com/wp-content/uploads/2021/03/image-71-300x200.png 300w" sizes="auto, (max-width: 521px) 100vw, 521px" /></figure></div>



<p class="wp-block-paragraph">In this article, we are going to get our hands dirty experimenting with images using OpenCV. We will look at techniques like color conversion, resizing, blending, blurring, and thresholding. Getting your image data right is a half-way success for a useful machine learning model. Intrigued? Let’s get started.</p>



<h2 class="wp-block-heading">Install and Import Required Modules</h2>



<p class="wp-block-paragraph">For this tutorial, we need to install the OpenCV, NumPy, and Matplotlib modules. NumPy is used to manipulate image arrays. Matplotlib is used to display images for comparing the &#8220;before and after&#8221;. Feel free to clone the GitHub repo of <a href="https://github.com/nghweigeok/image-processing-opencv" target="_blank" rel="noreferrer noopener" title="https://github.com/nghweigeok/image-processing-opencv">this tutorial</a>.</p>



<p class="wp-block-paragraph">First, create a virtual environment for this project. Then, install the mentioned modules in a Jupyter notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">!pip install opencv-python  
!pip install numpy
!pip install matplotlib</pre>



<p class="wp-block-paragraph">No surprise here &#8212; the installation should be straightforward and fast. Now execute the following lines of code in your notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
</pre>



<p class="wp-block-paragraph">Note that the <em>%matplotlib inline </em>magic command is exclusive for Jupyter notebooks. It is not required in a Python script. It sets the backend of the Matplotlib module to display figures inline and not on a separate window.</p>



<p class="wp-block-paragraph">Done! Get your favorite photos ready &#8212; it&#8217;s time for experiments!</p>



<h2 class="wp-block-heading">Load Image and Convert Color Channels</h2>



<p class="wp-block-paragraph">To load an image in the notebook, we use the <em>imread</em> method of the OpenCV module. By default, the method loads an image in color. To load a greyscale image, we need to supply a second parameter of “0’” to the method:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">img_greyscale = cv2.imread('./photo.jpg', 0)
img_greyscale.shape
img = cv2.imread('./photo.jpg')
img.shape
</pre>



<p class="wp-block-paragraph">Note that the images are loaded as NumPy arrays – one greyscale and another one in color. The <em>shape</em> method returns (5563, 3709) for the variable <em>img_greyscale</em> and (5563, 3709, 3) for <em>img</em>. The method returns information in the form of (height, width, channel). Both the variables have the same height and width values. But <em>img_greyscale</em> consists of only one channel (one color) while <em>img</em> has three.</p>



<p class="wp-block-paragraph">By default, the <em>imread</em> method loads an image with a color order of blue, green, red. It is not the usual red, green, blue. In case you ever wonder why your images look weird in OpenCV, it is that. To display an image, use the <em>imshow</em> method of the Matplotlib module as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">plt.imshow(img)</pre>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="549" height="353" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-72.png" alt="" class="wp-image-26737" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-72.png 549w, https://blog.finxter.com/wp-content/uploads/2021/03/image-72-300x193.png 300w" sizes="auto, (max-width: 549px) 100vw, 549px" /><figcaption>Figure 1: (Left) An image with (Blue, Green, Red) color channels. (Right) The same image with (Red, Green, Blue) color channels.</figcaption></figure></div>



<p class="wp-block-paragraph">Figure 1 shows how different an image can look when its color channels are mixed up. Matplotlib displays the red channel as blue for the image on the left. To fix this, we can use the OpenCV <em>cvtColor</em> method to convert the color channels from (B, G, R) to (R, G, B), as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)</pre>



<p class="wp-block-paragraph">The color-corrected image is shown on the right side of Figure 1. We will use the RGB image as an example in the latter sections. But using RGB images is not a requisite &#8212; feel free to use the BGR image if you prefer. Make sure to pick the correct channels in operation.</p>



<h2 class="wp-block-heading">Resize Image</h2>



<p class="wp-block-paragraph">Quiz time: which OpenCV method should you use to resize an image? You guessed it &#8212; the <em>resize</em> method. It takes an image and an image dimension as parameters. The following code resizes the image to be half its original size:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">width = int(img_RGB.shape[1] / 2)
height = int(img_RGB.shape[0] / 2)

img_RGB_smaller = cv2.resize(src=img_RGB, dsize=(width, height)) 
img_RGB_smaller.shape
</pre>



<p class="wp-block-paragraph">Note that you can supply any positive integer values to the <em>dsize</em> parameter of the <em>resize</em> method. Yet, it is a good practice to use a scale factor to keep the original aspect ratio of the image. The code shown takes the width and height values of the original image and divides them by two. The output of the <em>img_RGB_smaller.shape</em> is (2781, 1854, 3), which is 50% smaller than its original size, (5563, 3709, 3). You can also make the image larger by multiplying its width and height by two, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">width = int(img_RGB.shape[1] * 2)
height = int(img_RGB.shape[0] * 2)

img_RGB_bigger = cv2.resize(src=img_RGB, dsize=(width, height)) 
img_RGB_bigger.shape
</pre>



<p class="wp-block-paragraph">That creates an image of size (11126, 7418, 3). Feel free to be creative with the image dimension definitions. Figure 2 shows the resized images. Both look the same because their aspect ratios are retained. Note the differences in their width and height axes instead.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="499" height="322" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-74.png" alt="" class="wp-image-26741" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-74.png 499w, https://blog.finxter.com/wp-content/uploads/2021/03/image-74-300x194.png 300w" sizes="auto, (max-width: 499px) 100vw, 499px" /><figcaption>Figure 2: (Left) An image resized to half its original size. (Right) The same image resized to double its original size.</figcaption></figure></div>



<h2 class="wp-block-heading">Blend Images</h2>



<p class="wp-block-paragraph">Image blending means combining two images with shared transparency. We want two images to “blend into” each other as one image. For this, we need to load another image to our notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">img_overlay = cv2.imread('./photo-overlay.jpg')
img_overlay.shape
</pre>



<p class="wp-block-paragraph">All images used in this code project can be found at Unsplash.com. The second image is loaded as variable <em>img_overlay</em> with dimensions (2000, 1800, 3). Images must have the same size for image blending. As <em>img_overlay</em> is of a different size than the first image, we need to resize it to match the size of the first image:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">img_overlay = cv2.resize(img_overlay, (img_RGB.shape[1], img_RGB.shape[0]))
img_overlay.shape
</pre>



<p class="wp-block-paragraph">Note that the <em>dsize</em> parameter takes a value in the form of (width, height), not (height, width). Thus, we enter <em>(img_RGB.shape[1], img_RGB.shape[0]) </em>as the parameter instead of the other way round. Now, the output of <em>img_overlay.shape</em> should show the same size as <em>img_RGB</em>, which is (5563, 3709, 3). Enter the following code to blend both the images together:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">blended = cv2.addWeighted(src1=img_RGB, alpha=0.3, src2=img_overlay, beta=0.7, gamma=0)</pre>



<p class="wp-block-paragraph">The <em>addWeighted</em> method of OpenCV combines the images with a “transparency weightage”. The <em>src1</em> parameter takes the background image and the <em>src2</em> the foreground image. The <em>alpha</em> parameter sets the transparency of <em>src1</em> and the <em>beta</em> of <em>src2</em>. Both <em>alpha</em> and <em>beta</em> can take values ranging from 0 to 1 and should both add up to 1. A value closer to 0 indicates more transparency. A value closer to 1 indicates more opaqueness. The <em>gamma</em> parameter sets the brightness of the output image. Figure 3 shows the before and after of the image blending operation.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="331" height="436" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-75.png" alt="" class="wp-image-26743" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-75.png 331w, https://blog.finxter.com/wp-content/uploads/2021/03/image-75-228x300.png 228w" sizes="auto, (max-width: 331px) 100vw, 331px" /><figcaption>Figure 3: (Top left) The background image. (Top right) The foreground image. (Bottom) The blended image.</figcaption></figure></div>



<h2 class="wp-block-heading">Blur Image</h2>



<p class="wp-block-paragraph">Here, we crop out a smaller section of an image to better notice the image blurring operation. Taking <em>img_RGB</em>, we copy its bottom right part as <em>img_small</em> using NumPy array slicing:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">img_small = img_RGB[4000:, 2000:]</pre>



<p class="wp-block-paragraph">That will create a smaller image of size (1563, 1709, 3). There are various image blurring functions in the OpenCV module. For example, average blurring, median blurring, and Gaussian blurring. They differ in their mathematical operations and outcomes. For the sake of simplicity, we use the basic average blurring function in this tutorial. Enter the following line of code in your notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">blurred = cv2.blur(src=img_small, ksize=(100, 100))</pre>



<p class="wp-block-paragraph">You must have familiarized yourselves with the OpenCV parameters by now. Otherwise, press the SHIFT + TAB buttons to view any function description. The <em>ksize</em> parameter of the <em>blur</em> method defines the dimensions of the filter kernel. A kernel is like a paintbrush or sponge that you use to “smudge” the original image and make it blurry. The <em>ksize</em> parameter is the width and height of the sponge that you want to use – in this case, 100 x 100. Figure 4 shows the cropped image with its blurred after effect.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="526" height="238" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-76.png" alt="" class="wp-image-26744" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-76.png 526w, https://blog.finxter.com/wp-content/uploads/2021/03/image-76-300x136.png 300w" sizes="auto, (max-width: 526px) 100vw, 526px" /><figcaption><strong>Figure 4:</strong> (Left) A cropped image. (Right) The same image blurred with a kernel sized (100, 100).</figcaption></figure></div>



<h2 class="wp-block-heading">Threshold Image</h2>



<p class="wp-block-paragraph">Image thresholding turns a greyscale image into either black or white pixels. You might be asking: what’s the need for blurring and thresholding images? The answer is: so that computational models can perceive image data better. Take edge detection as an example: we want to blur or smooth object edges so that there will be less noise. And we want to threshold images so object boundaries can be defined better.</p>



<p class="wp-block-paragraph">For thresholding, we use <em>img_greyscale </em>instead of the coloured image. Enter the following one-liner in your notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ret, thresh1 = cv2.threshold(src=img_greyscale, thresh=127, maxval=255, type=cv2.THRESH_BINARY)</pre>



<p class="wp-block-paragraph">The threshold method takes a greyscale image as its <em>src</em> parameter. The <em>thresh</em> parameter is the cutting-point for the black/white pixel decision. Any pixel value lower than the <em>thresh</em> value will be assigned 0. Any pixel value above the <em>thresh</em> value will be assigned 1. That creates the black-or-white contrast. As the image has its values ranged from 0 to 255, we assign the <em>maxval</em> (largest value) parameter as 255. The <em>type</em> parameter defines the kind of threshold we want. THRESH_BINARY converts all shades of grey in the image into either black or white. Figure 5 shows a greyscale image with its outcome after the thresholding operation.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="433" height="281" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-77.png" alt="" class="wp-image-26745" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-77.png 433w, https://blog.finxter.com/wp-content/uploads/2021/03/image-77-300x195.png 300w" sizes="auto, (max-width: 433px) 100vw, 433px" /><figcaption>Figure 5: (Left) A greyscale image. (Right) The image after thresholding at value 127.</figcaption></figure></div>



<p class="wp-block-paragraph">You have just learned five useful techniques in computer vision. Well done!</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">This article elaborates on five basic image processing techniques of OpenCV. They include color conversion, resizing, blending, blurring, and thresholding. It is a step-by-step introductory tutorial to perform computer vision operations in Python.</p>



<h2 class="wp-block-heading">References</h2>



<p class="wp-block-paragraph">[1] <a href="https://opencv.org/about/">https://opencv.org/about/</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Extract Text from Images in Python using OpenCV and EasyOCR</title>
		<link>https://blog.finxter.com/how-to-extract-text-from-images-ocr-in-python-using-opencv-and-easyocr/</link>
		
		<dc:creator><![CDATA[Hwei Geok Ng]]></dc:creator>
		<pubDate>Sun, 07 Mar 2021 18:11:00 +0000</pubDate>
				<category><![CDATA[Data Science]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Matplotlib]]></category>
		<category><![CDATA[NumPy]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=25184</guid>

					<description><![CDATA[You can extract text from images with EasyOCR, a deep learning-based OCR tool in Python. EasyOCR performs very well on invoices, handwriting, car plates, and public signs. First released in 2007, PyTesseract [1] is the to-go library for extracting text from images. It uses classical computer vision methods to perform optical character recognition (OCR), then ... <a title="How to Extract Text from Images in Python using OpenCV and EasyOCR" class="read-more" href="https://blog.finxter.com/how-to-extract-text-from-images-ocr-in-python-using-opencv-and-easyocr/" aria-label="Read more about How to Extract Text from Images in Python using OpenCV and EasyOCR">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p class="has-pale-cyan-blue-background-color has-background wp-block-paragraph">You can extract text from images with EasyOCR, a deep learning-based OCR tool in Python. EasyOCR performs very well on invoices, handwriting, car plates, and public signs.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="How to Extract Text from Images in Python using OpenCV and EasyOCR" width="937" height="527" src="https://www.youtube.com/embed/9_SRXdO9EC4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph">First released in 2007, <strong>PyTesseract [1] is the to-go library for extracting text from images</strong>. It uses classical computer vision methods to perform optical character recognition (OCR), then features neural network components such as LSTM from its fourth version onwards. </p>



<p class="wp-block-paragraph">You may ask: Is there any alternative that is as good as PyTesseract for OCR? <strong>Yes, EasyOCR [2] it is.</strong> It is a new, <strong>deep learning-based module for reading text from all kinds of images</strong> in more than 80 languages.</p>



<p class="wp-block-paragraph">In this article, we will go through a three-step tutorial. </p>



<ul class="wp-block-list"><li>First, we will install the required libraries. </li><li>Second, we will perform image-to-text processing using EasyOCR on various images. </li><li>Third, we will use OpenCV to overlay detected texts on the original images. Let’s get started.</li></ul>



<h2 class="wp-block-heading">Step 1: Install and Import Required Modules</h2>



<p class="wp-block-paragraph">Optical character recognition is a process of reading text from images. An easy task for humans, but more work for computers to identify text from image pixels. For this tutorial, we will need OpenCV, Matplotlib, Numpy, PyTorch, and EasyOCR modules. <a href="https://github.com/nghweigeok/text-recognition-ocr-python" target="_blank" rel="noreferrer noopener" title="https://github.com/nghweigeok/text-recognition-ocr-python">Here&#8217;s the GitHub repo of this tutorial</a>.&nbsp;</p>



<p class="wp-block-paragraph">You can follow the tutorial in our interactive Jupyter notebook online:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><a href="https://colab.research.google.com/drive/1SG1bdoZSgj_OsQC1Ci1TkQ9Qb2FxYNfb?usp=sharing" target="_blank" rel="noopener"><img loading="lazy" decoding="async" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-44-1024x836.png" alt="" class="wp-image-25222" width="768" height="627" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-44.png 1024w, https://blog.finxter.com/wp-content/uploads/2021/03/image-44-300x245.png 300w, https://blog.finxter.com/wp-content/uploads/2021/03/image-44-768x627.png 768w" sizes="auto, (max-width: 768px) 100vw, 768px" /></a></figure>
</div>


<p class="wp-block-paragraph">First, <a href="https://blog.finxter.com/python-virtual-environments-with-venv-a-step-by-step-guide/" title="Python Virtual Environments with “venv” — A Step-By-Step Guide" target="_blank" rel="noreferrer noopener">create a virtual environment</a> for this project. Then, install the mentioned modules in a Jupyter notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">!pip install opencv-python
!pip install matplotlib
!pip install numpy
!pip install torch==1.7.1+cpu torchvision==0.8.2+cpu torchaudio===0.7.2 -f https://download.pytorch.org/whl/torch_stable.html
!pip install easyocr</pre>



<p class="wp-block-paragraph">The OpenCV module is for computer vision-related operations in Python. Specifically, we will use it to overlay images with respective recognized texts later. We need the Matplotlib module to display images. And we will use the Numpy module to convert images into arrays.</p>



<p class="wp-block-paragraph">PyTorch is a prerequisite for the EasyOCR module. Its installation varies according to OS and GPU driver requirements. You can get the installation commands at the PyTorch homepage [3]. Copy and execute the respective command as shown in Figure 1 if you operate on Windows.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="569" height="217" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-31.png" alt="" class="wp-image-25192" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-31.png 569w, https://blog.finxter.com/wp-content/uploads/2021/03/image-31-300x114.png 300w" sizes="auto, (max-width: 569px) 100vw, 569px" /><figcaption>Figure 1: Customized PyTorch installation command.</figcaption></figure>
</div>


<p class="wp-block-paragraph">Now, go ahead and install the EasyOCR module &#8212; the tool we need for extracting text from images. At this point, you should be able to execute the following lines of code in your notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import cv2
import numpy as np
import easyocr
import matplotlib.pyplot as plt
%matplotlib inline</pre>



<p class="wp-block-paragraph">Note that the <em><code>%matplotlib inline</code></em> magic command is exclusive for Jupyter notebooks. It is not required in a Python script. It sets the backend of the <a href="https://blog.finxter.com/matplotlib-full-guide/" target="_blank" rel="noreferrer noopener" title="Matplotlib — A Simple Guide with Videos">Matplotlib module</a> to display figures inline and not on a separate window.</p>



<p class="wp-block-paragraph">You’re off to a great start! Now, onto the next step.</p>



<h2 class="wp-block-heading">Step 2: Load Images and Extract Text using EasyOCR</h2>



<p class="wp-block-paragraph">For copyright reasons, all images used in the sample notebook are not provided in the GitHub repo. Feel free to download them from Unsplash.com or use your images. Define the path of an image using the following code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">im_1_path = './folder/image_name.jpg'</pre>



<p class="wp-block-paragraph">Next, initialize an EasyOCR reader with a list of languages you would like to use. Use the reader to read an image with the following function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def recognize_text(img_path):
    '''loads an image and recognizes text.'''
    
    reader = easyocr.Reader(['en'])
    return reader.readtext(img_path)</pre>



<p class="wp-block-paragraph">Did it surprise you that two lines of code are all you need to perform OCR? “Easy” for EasyOCR! The <em>recognize_text()</em> function initializes an OCR reader to a variable named reader. It takes a list of languages as a parameter. For this tutorial, we want to only recognize English text, thus the <em>‘en’</em> in the list. The <em>readtext</em> method reads an image given its stored directory. The returned OCR result is passed as the output of the <em>recognize_text()</em> function.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">result = recognize_text(im_1_path)
result</pre>



<p class="wp-block-paragraph">Note that it is going to take more time to execute EasyOCR on a CPU instead of a GPU. The <em>im_1_path</em> image took around ten seconds to be executed by <em>recognize_text()</em>. Figure 2 shows the operations in the EasyOCR framework. The framework includes image preprocessing, deep learning model recognition, and image postprocessing.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="602" height="185" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-32.png" alt="" class="wp-image-25197" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-32.png 602w, https://blog.finxter.com/wp-content/uploads/2021/03/image-32-300x92.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /><figcaption>Figure 2: The EasyOCR Framework [2].</figcaption></figure>
</div>


<p class="wp-block-paragraph">Here is the output of the EasyOCR module:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[([[1421, 1139], [1453, 1139], [1453, 1177], [1421, 1177]],
  'S',
  0.8625819477165351),
 ([[1524, 1038], [2201, 1038], [2201, 1211], [1524, 1211]],
  'CCC444',
  0.9068348515895301),
 ([[1641, 1201], [2012, 1201], [2012, 1245], [1641, 1245]],
  'T E S L A.C O M',
  0.33458756243407134),
 ([[2519, 1254], [2790, 1254], [2790, 1284], [2519, 1284]],
  'DUAL MSTOF',
  0.24584700695087508)]</pre>



<p class="wp-block-paragraph">It returns a list of detected text, with each text element containing three types of information. Which are: the text, its bounding box vertices, and the confidence level of the text detection. From the output, EasyOCR detected four text elements: ‘S’, ‘CCC444’, ‘T E S L A.C O M’, and ‘DUAL MSTOF’.</p>



<p class="wp-block-paragraph">To check the accuracy of the OCR, we need to display the original image on our notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">img_1 = cv2.imread(im_1_path)
img_1 = cv2.cvtColor(img_1, cv2.COLOR_BGR2RGB)
plt.imshow(img_1)</pre>



<p class="wp-block-paragraph">The <a href="https://blog.finxter.com/matplotlib-imshow/" title="Matplotlib Imshow — A Helpful Illustrated Guide" target="_blank" rel="noreferrer noopener"><em>imread</em> </a>method of the OpenCV module loads an image as a<a href="https://blog.finxter.com/numpy-tutorial/" title="NumPy Tutorial – Everything You Need to Know to Get Started" target="_blank" rel="noreferrer noopener"> Numpy array</a>, which is assigned to the <em>img_1</em> variable. The default color channels of OpenCV are (Blue, Green, Red) instead of (Red, Green, Blue). That is why we use the <em>cvtColor</em> method for channel conversion. Otherwise, we will see the image with its blue color assumed as red and vice versa. The image is shown in Figure 3, which is a car with a rear view of its vehicle registration plate.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="602" height="380" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-33.png" alt="" class="wp-image-25200" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-33.png 602w, https://blog.finxter.com/wp-content/uploads/2021/03/image-33-300x189.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>
</div>


<p class="wp-block-paragraph">Comparing the image with its OCR output, the car plate is captured accurately. EasyOCR detects the country code and the car provider name. Yet, the ‘DUAL MOTOR text on the right side of the car is detected as ‘DUAL MSTOF’. For that, image pre-processing techniques can be used to increase the OCR accuracy. But for now, we will only test the performance of EasyOCR out of the box.</p>



<h2 class="wp-block-heading">Step 3: Overlay Recognized Text on Images using OpenCV</h2>



<p class="wp-block-paragraph">Now, we want to draw a rectangle around each recognized text element on its original image. The <em>overlay_ocr_text()</em> function will be explained task-by-task.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def overlay_ocr_text(img_path, save_name):
    '''loads an image, recognizes text, and overlays the text on the image.'''
    
    # loads image
    img = cv2.imread(img_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    dpi = 80
    fig_width, fig_height = int(img.shape[0]/dpi), int(img.shape[1]/dpi)
    plt.figure()
    f, axarr = plt.subplots(1,2, figsize=(fig_width, fig_height)) 
    axarr[0].imshow(img)
</pre>



<p class="wp-block-paragraph">First, we use the OpenCV module to load an image as a Numpy array and correct its color channels. The array is assigned to the variable <em>img</em>. We want to display two images &#8212; the original image, and the original image with recognized texts. The <em>subplots</em> method of Matplotlib is used to display more than one figure at a time. The <em>imshow</em> method of the <em>axarr[0]</em> variable displays the original image.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># recognize text
    result = recognize_text(img_path)

    # if OCR prob is over 0.5, overlay bounding box and text
    for (bbox, text, prob) in result:
        if prob >= 0.5:
            # display 
            print(f'Detected text: {text} (Probability: {prob:.2f})')

            # get top-left and bottom-right bbox vertices
            (top_left, top_right, bottom_right, bottom_left) = bbox
            top_left = (int(top_left[0]), int(top_left[1]))
            bottom_right = (int(bottom_right[0]), int(bottom_right[1]))

            # create a rectangle for bbox display
            cv2.rectangle(img=img, pt1=top_left, pt2=bottom_right, color=(255, 0, 0), thickness=10)

            # put recognized text
            cv2.putText(img=img, text=text, org=(top_left[0], top_left[1] - 10), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 0, 0), thickness=8)
</pre>



<p class="wp-block-paragraph">The <em>recognize_text()</em> function returns the OCR output and assigns it to the <em>result</em> variable. A <em>for</em> loop is created to go through each text element contained in the variable. Recognized text elements are displayed only if their OCR confidence levels are higher than 0.5 (<em>prob &gt;= 0.5</em>). Then, the top left and bottom right vertices of each bounding box are obtained. They are converted into tuples of integer values (as required by OpenCV).</p>



<p class="wp-block-paragraph">The <em>rectangle</em> method creates a green bounding box for each detected text element. The <em>putText</em> method displays recognized text above its respective bounding box. As all these are done in a <em>for</em> loop, the operation repeats for every recognized text in the <em>result</em> variable.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># show and save image
    axarr[1].imshow(img)
    plt.savefig(f'./output/{save_name}_overlay.jpg', bbox_inches='tight')</pre>



<p class="wp-block-paragraph">Finally, the <em>overlay_ocr_text()</em> function displays every created text and bounding box. The <em>imshow</em> method of the <em>axarr[1]</em> variable displays the final image. As both the left and right images are in the same subplot, they are displayed as one final image. The <em>savefig</em> method stores the final image to a defined local directory.</p>



<h2 class="wp-block-heading">How Well Did EasyOCR Perform?</h2>



<p class="wp-block-paragraph">The figures below show how well EasyOCR performs for different kinds of images. We will test the library on handwriting, digits, an electronic invoice, and a public sign. For a complete overview, please refer to the demo notebook in the given GitHub repo.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="370" height="162" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-35.png" alt="" class="wp-image-25204" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-35.png 370w, https://blog.finxter.com/wp-content/uploads/2021/03/image-35-300x131.png 300w" sizes="auto, (max-width: 370px) 100vw, 370px" /><figcaption>Figure 4: EasyOCR performance on a vehicle registration plate.</figcaption></figure>
</div>


<p class="wp-block-paragraph">EasyOCR detects most of the text in Figure 7 correctly, except the text on the right-hand side.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="316" height="260" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-37.png" alt="" class="wp-image-25207" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-37.png 316w, https://blog.finxter.com/wp-content/uploads/2021/03/image-37-300x247.png 300w" sizes="auto, (max-width: 316px) 100vw, 316px" /></figure>
</div>


<p class="wp-block-paragraph">EasyOCR manages to detect every text in Figure 5. But the text sequence is not entirely correct.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="314" height="177" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-39.png" alt="" class="wp-image-25209" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-39.png 314w, https://blog.finxter.com/wp-content/uploads/2021/03/image-39-300x169.png 300w" sizes="auto, (max-width: 314px) 100vw, 314px" /><figcaption>Figure 6: EasyOCR performance on printed digits.</figcaption></figure>
</div>


<p class="wp-block-paragraph">EasyOCR detects everything in Figure 6 correctly. It is a relatively large image with clear printed digits and texts, which makes the OCR perform better.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="330" height="198" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-40.png" alt="" class="wp-image-25210" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-40.png 330w, https://blog.finxter.com/wp-content/uploads/2021/03/image-40-300x180.png 300w" sizes="auto, (max-width: 330px) 100vw, 330px" /></figure>
</div>


<p class="wp-block-paragraph">EasyOCR manages to detect every text on the invoice accurately without image preprocessing.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="399" height="274" src="https://blog.finxter.com/wp-content/uploads/2021/03/image-43.png" alt="" class="wp-image-25213" srcset="https://blog.finxter.com/wp-content/uploads/2021/03/image-43.png 399w, https://blog.finxter.com/wp-content/uploads/2021/03/image-43-300x206.png 300w" sizes="auto, (max-width: 399px) 100vw, 399px" /><figcaption>Figure 8: EasyOCR performance on a public sign.</figcaption></figure>
</div>


<p class="wp-block-paragraph">Again, EasyOCR nails it for Figure 8. Every text on the figure is correctly detected.</p>



<p class="wp-block-paragraph">We had the impression that EasyOCR performs greatly on images with clear text. It works fine without having to preprocess images, which saves time and cost.</p>



<h2 class="wp-block-heading">Bonus: Text-to-Speech Recognition</h2>



<p class="wp-block-paragraph">Outputs from OCR can be further utilized with a simple text-to-speech recognition application. It converts text to a voice utterance. First, we need to install the PyTTSX3 [4] module as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">!pip install pyttsx3</pre>



<p class="wp-block-paragraph">The implementation can be done in five lines of code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import pyttsx3

engine = pyttsx3.init()
engine.setProperty('rate', 100)
engine.say(sentence)
engine.runAndWait()
</pre>



<p class="wp-block-paragraph">The code initializes a TTS engine and assigns it to the variable engine. The <em>setProperty</em> method defines the speed of the utterance. The <em>say</em> method registers the text sentence to be pronounced. Finally, the <em>runAndWait</em> method executes the text-to-speech operation.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">This article explains how to extract text elements from images using EasyOCR. It also shows how to overlay recognized text on images using OpenCV. A simple text-to-speech is also introduced as an extended application for OCR output.</p>



<h2 class="wp-block-heading">References</h2>



<p class="wp-block-paragraph">[1] <a href="https://github.com/madmaze/pytesseract">https://github.com/madmaze/pytesseract</a></p>



<p class="wp-block-paragraph">[2] <a href="https://github.com/JaidedAI/EasyOCR">https://github.com/JaidedAI/EasyOCR</a></p>



<p class="wp-block-paragraph">[3] <a href="https://pytorch.org/get-started/locally/">https://pytorch.org/get-started/locally/</a></p>



<p class="wp-block-paragraph">[4] <a href="https://pypi.org/project/pyttsx3/">https://pypi.org/project/pyttsx3/</a></p>



<h2 class="wp-block-heading">Programming Humor</h2>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="648" height="525" src="https://blog.finxter.com/wp-content/uploads/2022/07/image-119.png" alt="" class="wp-image-528686" srcset="https://blog.finxter.com/wp-content/uploads/2022/07/image-119.png 648w, https://blog.finxter.com/wp-content/uploads/2022/07/image-119-300x243.png 300w" sizes="auto, (max-width: 648px) 100vw, 648px" /></figure>
</div>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Create an Interactive Web Application using a Jupyter Notebook</title>
		<link>https://blog.finxter.com/how-to-create-an-interactive-web-application-using-jupyter-notebook/</link>
		
		<dc:creator><![CDATA[Hwei Geok Ng]]></dc:creator>
		<pubDate>Sat, 20 Feb 2021 12:11:12 +0000</pubDate>
				<category><![CDATA[Computer Science]]></category>
		<category><![CDATA[Jupyter]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web Development]]></category>
		<guid isPermaLink="false">https://blog.finxter.com/?p=24200</guid>

					<description><![CDATA[Summary: To create an interactive web application in a Jupyter Notebook, use the three libraries ipywidgets, voila, and binder. This requires only basic Python programming skills without the need to learn a new framework. There are various Python tools available to create web applications and frontend GUIs. For example, Flask and Django. As useful as ... <a title="How to Create an Interactive Web Application using a Jupyter Notebook" class="read-more" href="https://blog.finxter.com/how-to-create-an-interactive-web-application-using-jupyter-notebook/" aria-label="Read more about How to Create an Interactive Web Application using a Jupyter Notebook">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p class="has-pale-cyan-blue-background-color has-background wp-block-paragraph"><strong>Summary</strong>: To create an interactive web application in a Jupyter Notebook, use the three libraries <em><code>ipywidgets</code></em>, <em><code>voila</code></em>, and <code>binder</code>. This requires only basic Python programming skills without the need to learn a new framework.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="How to Create an Interactive Web Application using a Jupyter Notebook" width="937" height="527" src="https://www.youtube.com/embed/t8P6estGusQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p class="wp-block-paragraph">There are various Python tools available to create web applications and frontend GUIs. For example, Flask and Django. As useful as they are, we still need to invest time in learning new frameworks. That might not be the highest on our priority list when we have spent a lot of time doing our Python project. </p>



<p class="wp-block-paragraph">Is there a way to create a simple, interactive web application with basic Python skills? The answer is yes, and this article will show you exactly that in five steps.</p>



<p class="has-base-background-color has-background wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Your PDF Certificate</strong>: If you want to get a PDF certificate for completing this small course &#8212; e.g., to show to your employer or freelancing clients &#8212; feel free to <a rel="noreferrer noopener" href="https://academy.finxter.com/university/how-i-created-an-interactive-web-application-using-a-jupyter-notebook/" data-type="URL" data-id="https://academy.finxter.com/university/how-i-created-an-interactive-web-application-using-a-jupyter-notebook/" target="_blank">check this out as a course on the Finxter Academy</a>! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f448.png" alt="👈" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">We will first create some interactive widgets using <em>ipywidgets</em> in a Jupyter notebook. Then, we will render the notebook as a web application using Voilà. We will push the project onto a GitHub repository, and finally, host the web app on Binder. </p>



<p class="wp-block-paragraph">Let’s get started! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4aa.png" alt="💪" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<h2 class="wp-block-heading">Step One: Create A New Git Repository and Install Python Packages</h2>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="954" height="637" src="https://blog.finxter.com/wp-content/uploads/2023/01/image-7.png" alt="" class="wp-image-1031731" srcset="https://blog.finxter.com/wp-content/uploads/2023/01/image-7.png 954w, https://blog.finxter.com/wp-content/uploads/2023/01/image-7-300x200.png 300w, https://blog.finxter.com/wp-content/uploads/2023/01/image-7-768x513.png 768w" sizes="auto, (max-width: 954px) 100vw, 954px" /></figure>
</div>


<p class="wp-block-paragraph">It is unnecessary to push every coding project onto our Git repository. But we need a Git repo for this project. That is because Binder will refer to it to create a Docker container that hosts our web application. For this tutorial, I have created a GitHub repo named <code>jupyter_webapp_demo</code>. Feel free to clone it at <a href="https://github.com/finxter/jupyter_webapp_demo" data-type="URL" data-id="https://github.com/finxter/jupyter_webapp_demo" target="_blank" rel="noreferrer noopener">https://github.com/finxter/jupyter_webapp_demo</a>.</p>



<p class="wp-block-paragraph">To clone a <a rel="noreferrer noopener" title="Git" href="https://blog.finxter.com/git/" target="_blank">Git repo</a> onto your local directory, type the following command on a terminal or command prompt:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ git clone https://github.com/username/project_name.git</pre>



<p class="wp-block-paragraph">Next, we are going to create a <a href="https://blog.finxter.com/python-virtual-environments-with-venv-a-step-by-step-guide/" target="_blank" rel="noreferrer noopener" title="Python Virtual Environments with “venv” — A Step-By-Step Guide">virtual environment</a> for the code project. It is always a good practice to isolate the required dependencies per project. We will export the names and versions of all installed packages to a text file later as a requisite for Binder.</p>



<p class="wp-block-paragraph">To create and activate a virtual environment, type the following commands:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ python3 -m venv venv_name
$ cd venv_name\Scripts
$ activate
$ cd ..\..</pre>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="602" height="254" src="https://blog.finxter.com/wp-content/uploads/2021/02/image-81.png" alt="" class="wp-image-24202" srcset="https://blog.finxter.com/wp-content/uploads/2021/02/image-81.png 602w, https://blog.finxter.com/wp-content/uploads/2021/02/image-81-300x127.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>
</div>


<p class="has-text-align-center wp-block-paragraph">Figure 1: Clone a Git repository and activate a virtual environment.</p>



<p class="wp-block-paragraph">Install the Jupyter Notebook app using either the <em>conda</em> or the <em>pip</em> package manager:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ conda install -c conda-forge notebook</pre>



<p class="wp-block-paragraph">or</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ pip install notebook</pre>



<p class="wp-block-paragraph">Type the following command to open the Jupyter Notebook app:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ jupyter notebook</pre>



<p class="wp-block-paragraph">You will see it opened on your default web browser.</p>



<p class="wp-block-paragraph">Now, create a new Python 3 notebook. Install three modules named <em>widgetsnbextension</em>, <em>ipywidgets</em>, and <em>voila</em>, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ pip install widgetsnbextension 
$ pip install ipywidgets 
$ pip install voila
</pre>



<p class="wp-block-paragraph">We need to enable the extensions to be properly displayed on the notebook:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">!jupyter nbextension enable --py widgetsnbextension --sys-prefix
!jupyter serverextension enable voila --sys-prefix</pre>



<p class="wp-block-paragraph">Comment out the commands and execute the cell again. That will hide the displayed text from the previous cell execution from our web app.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#!jupyter nbextension enable --py widgetsnbextension --sys-prefix
#!jupyter serverextension enable voila --sys-prefix</pre>



<p class="wp-block-paragraph">Now we are good to go. Time to load the web app with some sliders and buttons.</p>



<h2 class="wp-block-heading">Step Two: Create Interactive Web Elements with Ipywidgets</h2>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="956" height="637" src="https://blog.finxter.com/wp-content/uploads/2023/01/image-8.png" alt="" class="wp-image-1031735" srcset="https://blog.finxter.com/wp-content/uploads/2023/01/image-8.png 956w, https://blog.finxter.com/wp-content/uploads/2023/01/image-8-300x200.png 300w, https://blog.finxter.com/wp-content/uploads/2023/01/image-8-768x512.png 768w" sizes="auto, (max-width: 956px) 100vw, 956px" /></figure>
</div>


<p class="wp-block-paragraph"><em>Ipywidgets</em> is a module that lets us create interactive widgets in Jupyter notebooks. For example, buttons, text boxes, sliders, progress bars, and more. Feel free to explore the widget list [1] and use any that would help in showcasing your code project.</p>



<p class="wp-block-paragraph">Now, let&#8217;s create some widgets that curate a text message to Grandma for a dinner appointment.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="203" height="146" src="https://blog.finxter.com/wp-content/uploads/2021/02/image-82.png" alt="" class="wp-image-24204"/></figure>
</div>


<p class="has-text-align-center wp-block-paragraph"><em><strong>Figure 2: A web app that books a dinner appointment with Grandma.</strong></em></p>



<p class="wp-block-paragraph">For that, we will need widgets for displaying an image, a toggle button, a date picker, an integer slider, and a button. We will go through this tutorial for all the widgets but not for the layout. Feel free to refer to the given <a href="https://github.com/finxter/jupyter_webapp_demo" data-type="URL" data-id="https://github.com/finxter/jupyter_webapp_demo" target="_blank" rel="noreferrer noopener">Git repo</a> to see the complete notebook.</p>



<p class="wp-block-paragraph">First, import the <em>ipywidgets</em> module and the IPython display function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import ipywidgets as widgets
from IPython.display import display, clear_output
</pre>



<p class="wp-block-paragraph">The code for all widgets follows a similar and consistent format. Take the code for a toggle button as an example:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">grand = widgets.ToggleButtons(
            options=['grandson', 'granddaughter']
        )
</pre>



<p class="wp-block-paragraph">The <code>ToggleButtons</code> function from the <a href="https://blog.finxter.com/matplotlib-widgets-button/" target="_blank" rel="noreferrer noopener" title="Matplotlib Widgets — How to Make Your Plot Interactive With Buttons">widgets </a>module is called. Its options parameter is filled with some button names that we want. For this, two toggle buttons are created: grandson and granddaughter. This function is passed to a variable named grand.</p>



<p class="wp-block-paragraph">The following examples show how straight-forward it is to create any widget:</p>



<ul class="wp-block-list">
<li>A text box</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">name = widgets.Text(placeholder='Your name here')</pre>



<ul class="wp-block-list">
<li>A date picker</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">date = widgets.DatePicker(description='Pick a Date')</pre>



<ul class="wp-block-list">
<li>An integer slider</li>
</ul>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">friends = widgets.IntSlider(
            value=3, 
            min=0, 
            max=10,
            step=1,
            style={'description_width': 'initial' 
          )
</pre>



<p class="wp-block-paragraph">We need to create an extra function to configure the actions to be performed when the button is pressed:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">button_send = widgets.Button(
                description='Send to grandma',
                tooltip='Send',
                style={'description_width': 'initial'}
            )

output = widgets.Output()

def on_button_clicked(event):
    with output:
        clear_output()
        print("Sent message: ")
        print(f"Dear Grandma! This is your favourite {grand.value}, {name.value}.")
        print(f"I would love to come over on {date.value} for dinner, if that's okay for you!")
        print(f"Also, if you don't mind, I'll bring along {friends.value} hungry ghosts for your delicious food!")

button_send.on_click(on_button_clicked)

vbox_result = widgets.VBox([button_send, output])
</pre>



<p class="wp-block-paragraph">Notice how literal it is to get the information from all widgets:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">grand.value
name.value
date.value
friends.value
</pre>



<p class="wp-block-paragraph">Done? Then we are ready to turn the notebook into a web application.</p>



<h2 class="wp-block-heading">Step Three: Render the Notebook as A Web Application using Voilà</h2>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="973" height="637" src="https://blog.finxter.com/wp-content/uploads/2023/01/image-9.png" alt="" class="wp-image-1031738" srcset="https://blog.finxter.com/wp-content/uploads/2023/01/image-9.png 973w, https://blog.finxter.com/wp-content/uploads/2023/01/image-9-300x196.png 300w, https://blog.finxter.com/wp-content/uploads/2023/01/image-9-768x503.png 768w" sizes="auto, (max-width: 973px) 100vw, 973px" /></figure>
</div>


<p class="wp-block-paragraph">As we have installed the Voilà package, we can now see the button appeared on our notebook interface.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="602" height="64" src="https://blog.finxter.com/wp-content/uploads/2021/02/image-83.png" alt="" class="wp-image-24207" srcset="https://blog.finxter.com/wp-content/uploads/2021/02/image-83.png 602w, https://blog.finxter.com/wp-content/uploads/2021/02/image-83-300x32.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>
</div>


<p class="has-text-align-center wp-block-paragraph"><strong><em>Figure 3: A Voilà button that renders a notebook as a web application.</em></strong></p>



<p class="wp-block-paragraph">There are two ways to debug the notebook as a web application before we host it somewhere else. Either press the Voilà button or execute the notebook from the terminal as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ voila notebook_name.ipynb</pre>



<p class="wp-block-paragraph">When any of them is executed, a new tab will appear on our default web browser. It shows how our web application looks like in the local directory. Take your time to edit and complete your web app.</p>



<h2 class="wp-block-heading">Step Four: Push the Code Project onto Your Git Repository</h2>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="956" height="637" src="https://blog.finxter.com/wp-content/uploads/2023/01/image-10.png" alt="" class="wp-image-1031739" srcset="https://blog.finxter.com/wp-content/uploads/2023/01/image-10.png 956w, https://blog.finxter.com/wp-content/uploads/2023/01/image-10-300x200.png 300w, https://blog.finxter.com/wp-content/uploads/2023/01/image-10-768x512.png 768w" sizes="auto, (max-width: 956px) 100vw, 956px" /></figure>
</div>


<p class="wp-block-paragraph">Next, we need to create a requirements text file for the web app. To do that, type the following command in your <a href="https://blog.finxter.com/python-virtual-environments-conda/" target="_blank" rel="noreferrer noopener" title="Python Virtual Environments with Conda — Why the Buzz?">activated virtual environment:</a></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ pip freeze > requirements.txt</pre>



<p class="wp-block-paragraph">The file includes all packages that you have installed in the current <em><a href="https://blog.finxter.com/how-to-create-an-interactive-web-application-using-jupyter-notebook/" target="_blank" rel="noreferrer noopener" title="How to Create an Interactive Web Application using Jupyter Notebook">virtualenv</a></em>. Remove the following lines from the file as they are incompatible with Binder:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">pywin32==300
terminado==0.9.2
</pre>



<p class="wp-block-paragraph">We are now ready to commit and push our project folder onto our Git repo by typing the following commands:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">$ git add notebook.ipynb
$ git add requirements.txt
$ git commit -m “commit message”
$ git push origin branch_name
</pre>



<p class="wp-block-paragraph">Notice the changes taking place on our Git repo.</p>



<h2 class="wp-block-heading">Step Five: Host the Web Application on Binder</h2>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="954" height="637" src="https://blog.finxter.com/wp-content/uploads/2023/01/image-11.png" alt="" class="wp-image-1031741" srcset="https://blog.finxter.com/wp-content/uploads/2023/01/image-11.png 954w, https://blog.finxter.com/wp-content/uploads/2023/01/image-11-300x200.png 300w, https://blog.finxter.com/wp-content/uploads/2023/01/image-11-768x513.png 768w" sizes="auto, (max-width: 954px) 100vw, 954px" /></figure>
</div>


<p class="wp-block-paragraph">Now, we want the notebook to be displayed as a web app that is hosted on Binder, which anyone can access with a URL. Go to mybinder.org and choose the appropriate configurations for the code project.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="602" height="352" src="https://blog.finxter.com/wp-content/uploads/2021/02/image-85.png" alt="" class="wp-image-24209" srcset="https://blog.finxter.com/wp-content/uploads/2021/02/image-85.png 602w, https://blog.finxter.com/wp-content/uploads/2021/02/image-85-300x175.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>
</div>


<p class="has-text-align-center wp-block-paragraph"><strong><em>Figure 4: A sample configuration for hosting a Git project on Binder.</em></strong></p>



<p class="wp-block-paragraph">Elaboration for Figure 4:</p>



<ol class="wp-block-list" type="1">
<li>Choose your Git hosting platform, such as GitHub, GitLab, etc.</li>



<li>Insert the URL of your Git repo.</li>



<li>Insert the name of your notebook after <code>'/voila/render/'</code>.</li>



<li>Choose URL instead of File.</li>



<li>Click the Launch button.</li>



<li>Copy the text for displaying the Binder badge and paste it on the README file. Your web app will be shown when the badge is clicked on the Git repo.</li>
</ol>



<p class="wp-block-paragraph">And… voilà! Now you have a simple web application to showcase your work.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">This article explains how to create a web application in a Jupyter notebook. It only needs basic Python programming knowledge. The <em>ipywidgets</em> module is used to create interactive widgets. The voilà module is used to render the notebook as a web application. The web app is hosted on Binder, which is accessible by a URL.</p>



<h2 class="wp-block-heading">Programmer Humor</h2>



<p class="has-global-color-8-background-color has-background wp-block-paragraph"><em>There are only 10 kinds of people in this world: those who know binary and those who don’t.<br></em><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f469.png" alt="👩" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9d4-200d-2642-fe0f.png" alt="🧔‍♂️" class="wp-smiley" style="height: 1em; max-height: 1em;" /><em><br>~~~<br><br>There are 10 types of people in the world. Those who understand trinary, those who don’t, and those who mistake it for binary.</em> <em><br></em><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f469.png" alt="👩" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9d4-200d-2642-fe0f.png" alt="🧔‍♂️" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f471-200d-2640-fe0f.png" alt="👱‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph"><strong>Reference</strong>: [1] <a href="https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html">https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html</a></p>



<h2 class="wp-block-heading">More Projects and Learnings on the Finxter Academy! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f447.png" alt="👇" class="wp-smiley" style="height: 1em; max-height: 1em;" /></h2>



<p class="wp-block-paragraph">Do you want to keep doing real things with your coding skills rather than working on toy projects forever?</p>



<p class="wp-block-paragraph">If yes, feel free to check out our full course on this subject &#8212; and many more similar coding projects for real value creation on the <a rel="noreferrer noopener" href="https://academy.finxter.com/" data-type="URL" data-id="https://academy.finxter.com/" target="_blank">Finxter Academy</a>:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="https://academy.finxter.com/university/how-i-created-an-interactive-web-application-using-a-jupyter-notebook/" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="1024" height="560" src="https://blog.finxter.com/wp-content/uploads/2023/01/image-5-1024x560.png" alt="" class="wp-image-1031709" srcset="https://blog.finxter.com/wp-content/uploads/2023/01/image-5-1024x560.png 1024w, https://blog.finxter.com/wp-content/uploads/2023/01/image-5-300x164.png 300w, https://blog.finxter.com/wp-content/uploads/2023/01/image-5-768x420.png 768w, https://blog.finxter.com/wp-content/uploads/2023/01/image-5.png 1331w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>
</div>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Minified using Disk

Served from: blog.finxter.com @ 2026-10-26 13:15:35 by W3 Total Cache
-->