Displaying Images in table along with the DataFrame

i am currently running the app locally,

Versions:
Python – 3.12
Streamlit – 1.33

When ever the user inputs a style and click on enter, it fetch’s the style image url from an api (fastapi) and insert to DataFrame and display the df with images side by side.

Now my Problem is every time inputs a style, streamlit reruns and display the table with images, when i have less records it doesn’t take much time. But when i have many style it takes time for display the df with images. is there any way to optimize my code to display the data little faster.
like can i cache existing data along with images instead of rerunning for all records it can run for only the new style

import streamlit as st
import requests
from PIL import Image
from io import BytesIO
import pandas as pd
import base64
from streamlit import session_state as ss

if "style_data" not in ss:
    ss.style_data = []

if "input_style" not in ss:
    ss.input_style = ''

# Streamlit configuration
st.set_page_config(layout="wide")

# Define API URL
API_URL = "http://localhost:8000"

# Function to fetch style info from backend
def fetch_style(style):
    try:
        response = requests.get(f"{API_URL}/style/{style}")
        if response.status_code == 200:
            return response.json()
        else:
            st.error("Style not found")
            return None
    except requests.exceptions.RequestException as e:
        st.error(f"Error: {e}")
        return None

def get_image_from_url(url):
    try:
        response = requests.get(url)
        response.raise_for_status()
        img = Image.open(BytesIO(response.content))
        return img
    except Exception as e:
        st.error(f"Error fetching image from URL: {e}")
        return None

def get_thumbnail(url):
    img = get_image_from_url(url)
    if img:
        img.thumbnail((200, 200))
    return img

def image_to_base64(img):
    if img:
        with BytesIO() as buffer:
            img.save(buffer, 'jpeg') 
            return base64.b64encode(buffer.getvalue()).decode()

def image_formatter(url):
    img = get_thumbnail(url)
    if img:
        return f'<img src="data:image/jpeg;base64,{image_to_base64(img)}">'

@st.cache_data
def convert_df(input_df):
     return input_df.to_html(escape=False, formatters=dict(thumbnail=image_formatter))

def new_styles_cb():
    """Append data to style_data from selection."""
    ss.style_data.insert(0,
        {
            "styleno": ss.styleno,
            "imagelink": ss.imagelink
        }
    )

def fetch_data():
    style_info = fetch_style(ss.input_style)
    # Display style information in a table
    if style_info:
        list_data = list((style_info.values()))
        ss.styleno = list_data[0]
        ss.imagelink = list_data[1]
        new_styles_cb()
        ss.input_style = ''

# Streamlit UI
st.title("Style Information")

# Input form for style number
st.text_input("Enter Style Number", key='input_style', on_change=fetch_data)
 

if (len(ss.style_data)) > 0:

    df_data = pd.DataFrame(ss.style_data)
    df_data['thumbnail'] = df_data['imagelink']

    html_view = convert_df(df_data)
    st.markdown(
        html_view,
        unsafe_allow_html=True
    )
1 Like

Hey @Dinesh_N,

Thanks for your questions!

To optimize your app, you could focus on caching the output of image processing functions. Use Streamlit’s @st.cache to cache the results of get_image_from_url and get_thumbnail.

This way, you’d prevent re-fetching / re-processing images that have already been processed, thus speeding up the app’s performance for repeated views of the same images.

Let me know if that works for you?

Best,
Charly

1 Like

@Charly_Wargnier,

Thanks

1 Like

You’re very welcome!

To speed up image fetching in your app, especially when dealing with multiple images, you can also use Python’s concurrent.futures.ThreadPoolExecutor for parallel downloads. This is useful for I/O-bound tasks like downloading images from URLs.

Let me know if you need any help implementing it! :slight_smile:

Best,
Charly

2 Likes

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.