Embed images in Jupyter

Aug. 21, 2021

Taking a variety of tools to embed images to the Jupyter Notebook. A few are: Using img tag in HTML; converting image to a base64 string representation; using selenium to render map created by folium

Convert images into base64 string is handy to avoid missing attached files and pack the document into one single file


TOP

Embed screenshot

  • This is the simplest case. We have still images somewhere and we want to include them in the Jupyter notebook for demonstration
  • we can upload this image to a public hosting such as unsplash.com , imgur.com , imgbb.com . Then we can insert the image using tag.
In [1]:
from IPython.core.display import HTML, display

img tag as HTML

In [2]:
img_url = 'https://upload.wikimedia.org/wikipedia/commons/8/84/Hubble_Visible_View_of_Jupiter.jpg'
<img src='https://en.wikipedia.org/wiki/Jupiter#/media/File:Jupiter_and_its_shrunken_Great_Red_Spot.jpg' class='img-fluid'>
In [3]:
HTML(f"")
Out[3]:

Markdown

Here is the code to embedded image using markdown style

!['jupyter'](https://upload.wikimedia.org/wikipedia/commons/8/84/Hubble_Visible_View_of_Jupiter.jpg?s=200)

'jupyter'|1%

The major drawback using markdown is very hard to control the resposiveness of the image. If you reading this on the mobile, it does not scale well to your screen.

TOP

Convert to a base64 string


  • This comes handy when you wanted to include a screenshot or a offline image directly to the Jupyter Notebook.

  • Simply we will convert to the image to bytes in format of string and give a keyword so that the browser understands this is the data of image

  • To start, let import base64 python package

In [4]:
import base64
In [5]:
def convert_img_base64(fpath=None):
    '''convert image to bytes and display on jupyter'''
    
    with open(fpath, "rb") as image_file:
        encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
    img = f'data:image/png;base64,{encoded_string}'
    print(img[:200])
    return HTML(f"{img}>")
In [6]:
# Here is another image of Jupiter
image_path = 'Jupiter.jpg'
In [7]:
convert_img_base64(image_path)
data:image/png;base64,/9j/2wBDAAQDAwQDAwQEAwQFBAQFBgoHBgYGBg0JCggKDw0QEA8NDw4RExgUERIXEg4PFRwVFxkZGxsbEBQdHx0aHxgaGxr/2wBDAQQFBQYFBgwHBwwaEQ8RGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGh
Out[7]:
  • We can now share the Jupyter Notebook and less worry that our friend emails back asking for the images

Matplotlib.pyplot to retain quality

  • underlying matplotplot is a base64 string. After save Notebook as html format, and read the html using a text editor such as Notepad ++, Sublime, Vcode, you will see this
    In [8]:
    import matplotlib.pyplot as plt
    import numpy as np
    %matplotlib inline
    
In [9]:
X = np.linspace(-np.pi, np.pi, 100)
Y = [np.sin(i) for i in X]
Z = [np.cos(i) for i in X]
# X, Y, Z
In [10]:
from matplotlib.ticker import FormatStrFormatter, MultipleLocator
In [11]:
plt.style.use('default')
plt.rcParams['font.family'] = 'monospace'
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(X, Y, color='red', lw=2, label='sine(x)')
ax.plot(X, Z, color='blue', lw=2, label='cosine(x)')
ax.axhline(0, color='grey')
ax.axvline(0, color='grey')
# ax.xaxis.set_major_formatter(FormatStrFormatter('%g $\pi$'))
ax.xaxis.set_major_locator(MultipleLocator(np.pi/2))
ax.xaxis.set_minor_locator(MultipleLocator(np.pi/4))
for s in ["top","right","left"]:
        ax.spines[s].set_visible(False)
ax.grid(which='both')
ax.legend()
ax.set_title('An Example of Matplotlib with sine and cosine', fontsize=16, y=1.05)
fig.tight_layout()
  • now we want to get the string representation of the graph and plot back in

  • first we read content of fig object into bytes and then string

  • we then add property to let the browser know this is a string of image data:image/png;...

In [12]:
import io
img = io.BytesIO()
fig.savefig(img, format='png', bbox_inches="tight")
In [13]:
# similar to function above
def plt_2_img(fig=None):
    '''convert image to bytes and display on jupyter'''
    img = io.BytesIO()
    fig.savefig(img, format='png', bbox_inches="tight")
    encoded_string = base64.b64encode(img.getvalue()).decode("utf-8").replace("\n", "")
    img = f'data:image/png;base64,{encoded_string}'
    return HTML(f"{img}>")
In [14]:
plt_2_img(fig=fig)
Out[14]:

TOP

Folium map

In [15]:
import folium
In [16]:
from folium.features import DivIcon
In [17]:
import pandas as pd
  • go to kaggle and get the csv file csv
In [18]:
capitals = pd.read_csv('concap.csv')
In [19]:
capitals.head()
Out[19]:
CountryName CapitalName CapitalLatitude CapitalLongitude CountryCode ContinentName
0 Somaliland Hargeisa 9.550000 44.050000 NaN Africa
1 South Georgia and South Sandwich Islands King Edward Point -54.283333 -36.500000 GS Antarctica
2 French Southern and Antarctic Lands Port-aux-Français -49.350000 70.216667 TF Antarctica
3 Palestine Jerusalem 31.766667 35.233333 PS Asia
4 Aland Islands Mariehamn 60.116667 19.900000 AX Europe
In [20]:
def add_marker(m, row):
    '''add marker by each row'''
    try:
        if row is not None:
            location=[row['CapitalLatitude'],row['CapitalLongitude']]
            tooltip = f"{row['CapitalName']}"
            folium.Circle(location = location, 
                          tooltip = tooltip,
                         fill=True,
                        radius = 1_000,
                         color='maroon'
                        ).add_to(m)
            
            return m
    except Exception as e:
        print(e)
    return None
In [21]:
m = folium.Map(min_zoom=2, max_bounds=True, tiles='cartodbpositron')
for i, row in capitals.iterrows():
    add_marker(m, row)
m
Out[21]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [22]:
# we could save to html format as a seperate file
m.save('captitals.html')
  • to capture the map in picture, we need to render the screen (similar to screenshot)
  • one solution to using selenium as web scraper and geckodriver to control the actual webbrowser
In [23]:
# first we need to copy the right version of geckodriver to the executable path.
# the simpliest approach is to copy directly to bin of virtual environment
!echo $PATH
/home/uno/Desktop/yake/venv/bin:/home/uno/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
In [24]:
# now we can render the map to img object with png format
img = m._to_png(3)
In [25]:
encoded_string = base64.b64encode(img).decode("utf-8").replace("\n", "")
img = HTML(f'{encoded_string}>')
In [26]:
img
Out[26]:

TOP

References

In [ ]:
 
get Jupyter Notebook:
Copyright © b-io.info. All Rights Reserved.