2025年5月15日 星期四

Streamlit 學習筆記 : 展示多媒體資料 (一) 圖片

多媒體資料主要包含圖片, 音訊, 與影片等素材, 均為非結構化的二進位資料, 本篇旨在測試如何用 Streamlit 顯示圖片, 播放音訊與影片. 
Streamlit 用來顯示多媒體內容的函式摘要如下表 :


 多媒體  函式  可用媒體格式  可用資料來源
圖片 st.image() PNG, JPG 本機路徑, URL, PIL.Imagenumpy 陣列
音訊 st.audio() MP3, WAV, OGG 本機檔案, URL, BytesIO
影片 st.video() MP4, WebM 本機檔案, URL, BytesIO


1. 顯示圖片 : 

顯示圖片的 st.image() 函式參數結構如下 : 

st.image(image, caption=None, width=None, use_container_width=False, clamp=False, channels="RGB", output_format="auto")

第一參數 image 為必要參數, 可傳入如下圖片來源 : 
  • 本機圖檔 (例如 'cat.jpg')
  • 網路圖檔 URL (例如 'https://example.com/image.png')
  • PIL.Image 物件
  • NumPy 陣列 (RGB 或灰階)
image 也可以是上面四種來源組成之陣列, 這會一次顯示多個圖片. 

參數 caption 用來在圖片正下方顯示標題文字 (支援 Markdown 語法), 在顯示單一圖檔時要傳入字串, 在一次顯示多圖時則是傳入字串陣列. 關於 Markdown 語法參考 :


參數 width 與 use_container_width 用來調整圖片的顯示寬度, 但它們是互斥的, 不要同時設定. width 的值為整數 (px), 預設值雖然是 None, 但實際渲染時的預設容器寬度是 740 px. 如果未指定 width, 當圖片實際寬度大於 740 px 時會被比例縮小到 740 px; 如果指定的寬度 width 小於 740 px, 就會比例縮小到指定的寬度; use_container_width 參數的值為布林值 (預設 False), 當設為 True 時圖片會完全填滿容器寬度. 

參數 clamp 用來設定是否將圖片的像數值限制在有效範圍 (0~255) 內, 預設為 False. 此參數僅對 ByteIO 陣列的圖片有用, 對網路圖片無意義. 

參數 channels 用來設定色彩通道順序, 預設是 "RGB", 如果圖片來源為 Numpy 陣列要用 "RGB", 如果來自 OpenCV 函式庫則要設定為 "BGR". 

參數 outpu_format 用來指定傳輸圖片資料時的格式, 預設為 'auto' (自動指定), 如果是照片應設為 'jpg', 若為圖表則設為 'png'. 

例如 : 

測試 1-1 : 顯示本機圖片 [看原始碼]

# st-image-test-1.py
import json
import streamlit as st

st.subheader("顯示圖片(預設參數)")
st.image('cat1.jpg')
st.subheader("顯示圖片(設定 width=740)")
st.image('cat1.jpg', width=740)
st.subheader("顯示圖片(設定 width=370)")
st.image('cat1.jpg', width=370)

此例使用本機工作目錄下的圖片 cat1.jpg, 解析度為 2250x1500, 此圖可在 GitHub 下載 : 


第一次呼叫 st.image() 時使用預設參數, 因此會被比例縮小至 740 px 的寬度 :




第二次呼叫 st.image() 時傳入 width=740, 這與父容器預設大小一樣, 所以顯示的結果與上面使用預設值的相同 : 




第三次呼叫 st.image() 時傳入 width=370, 這是父容器預設寬度 740 px 的一半, 於是圖片就被等比例縮成 370 px 了 : 




接下來測試網路圖片, 使用的圖片為 Wiki 上的 Lenna 圖 : 

  
此圖的解析度為 316x316.


測試 1-2 : 顯示網路圖片 [看原始碼]

# st-image-test-2.py
import streamlit as st

st.subheader("顯示圖片(預設參數)")
url='https://upload.wikimedia.org/wikipedia/zh/thumb/3/34/Lenna.jpg/300px-Lenna.jpg'
st.image(url)
st.subheader("顯示圖片(設定 use_container_width=True)")
st.image(url, use_container_width=True)
st.subheader("顯示圖片(設定 width=200)")
st.image(url, width=200)

第一次呼叫 st.image() 時使用預設參數, 由於原圖解析度 316x316, 因此會在容器中以原始尺寸顯示; 第二次呼叫 st.image() 時傳入 use_container_width=True 參數, 渲染時會將圖片比例放大到容器預設寬度 740 px : 




第三次呼叫 st.image() 時傳入 width=200, 這又比預設參數時的原圖寬度更小 : 




下面範例測試將 Bytes 類型資料傳入 st.image(), 利用 open() 函式開啟本機目前工作目錄下的 cat1.jpg 圖檔, 將所讀取的二進位圖片資料 (Bytes 型別) 傳給 st.image() 顯示 : 


測試 1-3 : 顯示 Bytes 類型圖片 (設定 caption 參數) [看原始碼]

# st-image-test-3.py
import streamlit as st

with open('cat1.jpg', 'rb') as f:
    image=f.read()
    print(type(image))   # 顯示 <class 'bytes'>
    
st.subheader("顯示圖片 (Bytes)")
st.image(image, caption='**可愛的貓咪1**')

此例呼叫 open() 開啟圖檔並讀取 Bytes 類型的圖片資料後傳給 st.image(), 同時也傳送了 Markdown 語法的 caption 參數, 結果如下 : 




可見圖片標題 (caption) 會顯示在正下方, 且有 Markdown 粗體效果.

接下來測試用 st.image() 來顯示 PIL.Image.Image 物件所表示的圖片, PIL 是一個歷史悠久的 Python 影像處理套件, 但它只支援到 Python 2.7 就停止開發了, 改由 Pillow 接手改版為支援 Python 3 版本, 但基於向下相容, 安裝時使用 pip install pillow, 但使用時仍然使用 PIL 套件名稱, 用法參考 :  


 
測試 1-4 : 顯示 PIL.Image.Image 物件之圖片 [看原始碼]

# st-image-test-4.py
import streamlit as st
from PIL import Image

image=Image.open('cat2.jpg') # 傳回 PIL.Image.Image 物件
st.subheader("顯示圖片 (PIL.Image.Image 物件)")
st.image(image, caption='**可愛的貓咪1**')

此例使用 PIL.Image.Image() 開啟本機目前工作目錄下的 cat2.jpg :


PIL.Image.Image() 會傳回一個 PIL.Image.Image 物件, 將其傳給 st.Image() 也能顯示圖, 結果如下 : 




Numpy 產生的 ndarray 物件圖片 (byte 值 0~255) 也可以傳給 st.image() 顯示, 如下範例所示 :


測試 1-5 : 顯示 Numpy 的 ndarray 物件陣列圖片 [看原始碼]   

# st-image-test-5.py
import streamlit as st
import numpy as np

st.subheader("Numpy 隨機生成的圖片")
image1=np.random.randint(0, 256, (300, 400, 3), dtype=np.uint8)
image2=np.random.randint(0, 256, (300, 400), dtype=np.uint8)
st.image([image1, image2], caption=['**彩色隨機圖**', '**灰階隨機圖**'], clamp=True)

此例用 Numpy 的 randint() 函式分別產生 RGB 與灰階圖片的 ndarray 物件, 然後將其組成串列傳入 st.image() 顯示. randint() 的前兩個參數分別是設定隨機數的下限 low 與上限 high (不含), 故它會傳回 0~255 的隨機整數的像素值. 第三參數是一個表示圖片尺寸與色彩通道數的元組 (height, width [, channels]), 如果要產生 RGB 彩圖 channels 設為 3, 如果要灰階圖就不要傳入 channels. 第四參數 dtype 設為 np.unit8 表示每個像素使用 8 位元無號整數 (0–255).

注意, 此例還傳入 clamp=True 參數用來強制將圖片像素質限制在 0~255 範圍, 避免 Stramlit 自動將其轉成 0~1 的浮點數. 另外, 因為圖片來源為一陣列, caption 參數也必須是具有相同長度的字串陣列, 否則會拋出例外. 其實以上所有圖片來源都可以一個陣列來同時顯示多張圖片. 

結果如下 : 



可見上圖為 RGB 彩色隨機圖, 下圖則僅有灰階色彩. 

使用 OpenCV 套件 cv2.imread() 讀取的圖檔也可以直接傳給 st.image() 顯示, 因為 OpenCV 底層也是使用 Numpy 處理圖片, cv2.imread() 讀取圖片後也是傳回一個 ndarray 物件, 例如 : 


測試 1-6 : 顯示 OpenCV 讀取的圖片 (Numpy 的 ndarray 物件) [看原始碼]   

# st-image-test-6.py
import cv2
import streamlit as st

st.subheader('顯示 OpenCV 讀取的圖片')
image=cv2.imread('cat2.jpg')  # 使用 OpenCV 讀取 BGR 格式圖片
print(type(image))  # <class 'numpy.ndarray'>
image_rgb=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)  # 轉為 RGB 格式
st.image(image_rgb, caption='**可愛的貓咪**')

此例因為 cv2.imread() 傳回的 ndarray 圖片物件是 BGR 格式, 因此須呼叫 cv2.cvtColor() 函式將其轉換成 RGB 格式才能後才能傳給 st.image() 顯示, 結果如下 : 




關於 OpenCV 用法參考 :


沒有留言 :