Hi, I’m pretty new to Streamlit. My goal is to build a simple database. Guided by [this](# Get dataframe row-selections from users - Streamlit Docs) example, I was able to customize the pandas dataframe and streamlit dataframe to my needs.
Each item in the database is associated with an image, which I would like to display in the dataframe. The images are stored locally as PNGs. Reading some other topics, I managed to get this code:
import pandas as pd
import streamlit as st
from pathlib import Path
from faker import Faker
from PIL import Image
from io import BytesIO
import base64
st.set_page_config(layout="wide") # https://discuss.streamlit.io/t/how-to-increase-the-width-of-web-page/7697
map_paths = [f"/home/my.name/Pictures/db_trial/maps/map{i}.png" for i in range(1, 7)]
for x in map_paths:
assert Path(x).exists()
def get_image_from_disk(path_to_image):
return pure_pil_alpha_to_color_v2(Image.open(path_to_image))
def pure_pil_alpha_to_color_v2(image, color=(255, 255, 255)):
"""Alpha composite an RGBA Image with a specified color.
Simpler, faster version than the solutions above.
Source: http://stackoverflow.com/a/9459208/284318
Keyword Arguments:
image -- PIL RGBA Image object
color -- Tuple r, g, b (default 255, 255, 255)
"""
image.load() # needed for split()
background = Image.new('RGB', image.size, color)
background.paste(image, mask=image.split()[3]) # 3 is the alpha channel
return background
def image_to_base64(img):
if img:
with BytesIO() as buffer:
img.save(buffer, "png")
return base64.b64encode(buffer.getvalue()).decode()
@st.cache_data
def get_profile_dataset(number_of_items: int = 20, seed: int = 0) -> pd.DataFrame:
new_data = []
fake = Faker()
np.random.seed(seed)
Faker.seed(seed)
for i in range(number_of_items):
profile = fake.profile()
new_data.append(
{
"name": profile["name"],
"map": image_to_base64(get_image_from_disk(map_paths[np.random.randint(0, len(map_paths))])), # pick a random image from disk. image is not displayed in the dataframe
# "map": "https://i.imgur.com/fH2LHvo.png", # this image is displayed correctly in the dataframe
"revision": ("A", "B", "C")[np.random.randint(0, 2 + 1)],
"sensor": ("None", "GaAs", "CdTe")[np.random.randint(0, 2 + 1)],
"frame": ("None", "small", "big")[np.random.randint(0, 2 + 1)],
"bad_pixels": np.random.randint(0, 200 + 1, size=4), # one entry per threshold (should be percentage)
"dac_failures": np.random.randint(0, 10 + 1),
}
)
profile_df = pd.DataFrame(new_data)
return profile_df
column_configuration = {
"name": st.column_config.TextColumn(
"Name", help="The name of the module", max_chars=100, width="small"
),
"map": st.column_config.ImageColumn(
"Map", help="Module map", width="small"
),
"revision": st.column_config.TextColumn(
"Rev", help="ASIC Revision", max_chars=3, width="small"
),
"sensor": st.column_config.TextColumn(
"Sensor", help="The type of sensor", max_chars=15, width="small"
),
"frame": st.column_config.TextColumn(
"Frame", help="The type of frame", max_chars=15, width="small"
),
"bad_pixels": st.column_config.BarChartColumn(
"Bad Pixels",
help="The number of bad pixels per threshold",
width="small",
y_min=0,
y_max=3,
),
"dac_failures": st.column_config.NumberColumn(
"DAC Failures",
help="The number of failed DACs",
width="small",
)
}
select, compare = st.tabs(["Select Modules", "Compare selected"])
with select:
st.header("All Modules")
df = get_profile_dataset()
event = st.dataframe(
df,
column_config=column_configuration,
use_container_width=True,
hide_index=True,
on_select="rerun",
selection_mode="multi-row",
)
st.header("Selected Modules")
modules = event.selection.rows
filtered_df = df.iloc[modules]
st.dataframe(
filtered_df,
column_config=column_configuration,
use_container_width=True,
)
with compare:
# comparison can be improved a lot
bad_pixel_df = {}
for module in modules:
bad_pixel_df[df.iloc[module]["name"]] = df.iloc[module]["bad_pixels"]
bad_pixel_df = pd.DataFrame(bad_pixel_df)
dac_failures_df = {}
for module in modules:
dac_failures_df[df.iloc[module]["name"]] = df.iloc[module]["dac_failures"]
dac_failures_df = pd.DataFrame(dac_failures_df, index=[0])
if len(modules) > 0:
st.header("Bad Pixel comparison")
st.bar_chart(bad_pixel_df)
st.header("DAC Failures comparison")
st.bar_chart(dac_failures_df)
else:
st.markdown("No modules selected.")
For each item my code adds the output of image_to_base64() to the pandas dataframe entry. The dataframe is passed to st.dataframe. But the images don’t show up. There is no error message:
The “Maps” column should contain the images loaded from disk. Instead it is empty. What am I doing wrong? If I use the random imgur URL, the image of a chair appears in the “Maps” column, therefore I assume that the dataframe itself is configured correctly?