【Dataiku x Snowflake x Streamlit】画像の格納と表示!

【Dataiku x Snowflake x Streamlit】画像の格納と表示! | Tableau-id Press -タブロイド-
dataiku_snowflake_streamlit

アドベントカレンダー企画5日目!

こんにちは、ひめのです。
今回のテーマは…
「Dataikuで生成した画像データをStreamlit in Snowflakeで表示してみよう」、です!

Streamlitは社内でも盛り上がりを見せているのですが、
Snowflakeに組み込まれているStreamlit(以下S in Sとします)はいろいろ制約があります。(詳しくはこちら。)

なぜわざわざS in Sを使うか、というとSnowflakeの中で完結するのでデータのセキュリティがとても高いからです。

ダッシュボードにはもちろんグラフを載せたりしたいのですが、図を載せたいこともあると思います。
私はめちゃくちゃ図を載せたくていろいろ調べました。
そして実現できたので方法をご紹介します!

Dataikuで画像データを作成する

まずStreamlitで表示させるようの適当な画像を作成しましょう。これは本当に何でもいいです。
以下のコードではmanaged フォルダに格納しています。

import dataiku
import numpy as np
import matplotlib.pyplot as plt
import io
from dataiku.core.managed_folder import Folder

x1 = np.linspace(0.0, 5.0)
x2 = np.linspace(0.0, 2.0)

y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
y2 = np.cos(2 * np.pi * x2)

plt.subplot(2, 1, 1)
plt.plot(x1, y1, 'o-')
plt.title('A tale of 2 subplots')
plt.ylabel('Damped oscillation')

plt.subplot(2, 1, 2)
plt.plot(x2, y2, '.-')
plt.xlabel('time (s)')
plt.ylabel('Undamped')

bs = io.BytesIO()
plt.savefig(bs, format="png")
folder = Folder("xxxxxxxx")
folder.upload_stream("fig.png", bs.getvalue())


こんな感じのいい具合に適当な画像ができあがりました。

次はSnowflakeにアップロードします。
今回のミソは画像そのままでは格納できない(いろいろ試したんですのよ)のでエンコーディングする、という点です。
私はPythonレシピでSnowflakeテーブルをアウトプットとしました。

もちろんモデルで推論した結果などを用いることもできます。
以下のコードで一旦managed folderに書き出してある画像を読み込んで、
画像をエンコーディングし、その結果をSnowflakeにアップロードしています。

import dataiku
from dataiku import pandasutils as pdu

from snowflake.snowpark.session import Session
from dataiku.core.managed_folder import Folder

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import io
from PIL import Image
import base64
import binascii
from IPython.display import display

# Session
connection_parameters = {
   "account": "xxxxxxxxxxxxxxxxxxx",
    "user" : "xxxxxxxxxxxxxxxxxxx",
    "password" : "xxxxxxxxxxxxxxxxxxx",
    "role" : "ACCOUNTADMIN",
    "warehouse" : "xxxxxxxxxxxxxxxxxxx",
    "database" : "xxxxxxxxxxxxxxxxxxx",
    "schema" : "xxxxxxxxxxxxxxxxxxx",
}
session = Session.builder.configs(connection_parameters).create()
# test if we have a connection
session.sql("select current_warehouse() wh, current_database() db, current_schema() schema, current_version() v").show()

img_folder = dataiku.Folder("xxxxxxxx")
img_folder_info = img_folder.get_info()

path = img_folder_info['path'] + "/fig.png" # 画像の名前を指定します。この場合はfig.png
print(path)

# 画像の読み込み
img = Image.open(path)
img

# バイナリ形式に変換するために、io.BytesIOを使用
buffered = io.BytesIO()
img.save(buffered, format="PNG") # JPEGだと失敗しました
image_binary = buffered.getvalue()

# Base64エンコーディング
image_base64 = base64.b64encode(image_binary)
image_base64_string = image_base64.decode()

# Base64エンコードされた文字列
base64_string = image_base64_string

# Base64から画像へ
base64_bytes = base64.b64decode(base64_string)
image_base64 = Image.open(io.BytesIO(base64_bytes))
image_base64.show()  

display(image_base64) # 復元出来ているかの確認

# Base64エンコードされたデータをDataFrameに格納
df = pd.DataFrame({'image_base64': [image_base64_string]})

# DataFrameを表示
print(df)

# Recipe outputs
image_streamlit = dataiku.Dataset("image_streamlit")
image_streamlit.write_with_schema(df)

レシピを実行するとこんな感じでエンコーディングされてテーブルに収まります。

ちなみにbase64とhexの両方で試しましたが、同様にOKでした。

 

 

 

Snowflakeでデータを確認する

ログインして今のアップロードされたテーブルを確認しましょう。

無事に入っていますね!

いろいろ試行錯誤した過程のお話を少しさせていただくと…
DataikuからSnowflakeに画像データをアップロードすることはできたんです。
今↑ではTableに入っていますが、画像をそのまま置こうとするとStageになります。
だがしかし、S in Sの制約でServer-side encryptionのStageからは読み込めないらしいのです。

なのでTableに画像データ(のエンコーディング結果)が入っているのはS in S的にとてもうれしいことなのです。

 

 

Streamlitで画像を表示する

S in Sアプリを作成しましょう。

左のバーから最近追加されたStreamlitボタンをクリックします。

# Import python packages
import streamlit as st
from snowflake.snowpark.context import get_active_session
import base64
import binascii
import io
from PIL import Image

# Write directly to the app
st.title("Example Streamlit App :balloon:")

# Get the current credentials
session = get_active_session()

# SnowflakeでSQLを実行
database: str = 'YOUR_DATABASE'
schema: str = 'YOUR_SCHEMA'
table: str = 'YOUR_TABLE'
query_1 = f'select * from {database}.{schema}.{table} '

df = session.sql(query_1).to_pandas()
st.dataframe(df, use_container_width=True)

# Base64エンコードされた文字列
base64_string = df['image_base64'][0]

# Base64から画像へ
base64_bytes = base64.b64decode(base64_string)
image_base64 = Image.open(io.BytesIO(base64_bytes))
st.image(image_base64) # 画像を表示

RUNすると…

画像が復元されて表示できました!やったーーー!

Streamlitの中でエンコーディングした結果の文字列を画像に戻してst.imageで表示しています。
st.imageで指定するものとして、先ほども書いた通りServer-side encryptionのStageにある画像は不可です(2023年11月現在)。

今回はS in Sアプリでしたが、画像をエンコーディングしてSnowflakeに格納する方法を応用すると
S in Sアプリ以外にもたくさん活用できると思います。
ちなみに今回の記事はSnowflakeのquickstartを参考にしています。

少し前まではアプリパッケージを作成して、そこにビューをコピーしたり、という手間があったのですが
最近パワーアップしてそれも不要になりました!
S in Sアプリ作成時に指定してactive sessionを念のため確認する、という方法でOKです!

 

truestarではDataiku・Snowflakeの検討、導入支援や環境構築からアプリ開発まで幅広くサポート可能です。
Dataiku・Snowflakeに興味がある、導入済みだけどもっとうまく活用したい等々ありましたら、ぜひこちらからご相談ください!

アドベントカレンダーまだまだ続きますのでお楽しみに!