こんにちは、CCCMKホールディングス技術開発の三浦です。
1月の三連休が終わりました。だんだん周りから年末年始の雰囲気が消えて、元の生活に戻っていく感じがします。長期休みでもなるべく生活のペースを崩さないように意識しているつもりなのですが、それでも年末年始はちょっと遊び(ゲーム)の時間が増えてしまったり、食べる量が増えたりしました。少しずつ年末年始前の状態に戻していこうと思います。
さて、最近Azure Blob Storage(Azure Data Lake Storage)に格納していた画像ファイルをちょっと確認したいな、と思うことがありました。AzureのStorageを管理したり、格納されたファイルの確認用にMicrosoftからはAzure Storage Explorerというアプリケーションが提供されています。
普段Blob Storageのファイルの確認はこのアプリケーションを利用しているのですが、画像ファイルのサムネイル表示が出来ず、画像ファイルを一覧で確認したい場合何か他にいい方法がないかな、と考えていました。
他に方法がないなら自分で作ってみようかな、ということで、Azure Blob StorageのPython向けのClientライブラリとWebアプリケーション構築用のライブラリ"Streamlit"を使ってBlob Storage上の画像ファイルを一覧表示出来る、簡単なアプリケーションを作ってみました。
作ったアプリケーション
今回作ったアプリケーションは以下のようなものです。
左側のサイドメニューで画像一覧を表示させたいBlob Storage上の日付ごとのディレクトリを指定し、"表示"ボタンをクリックすると該当画像の件数と画像のサムネイルがグリッド表示されます。まさにこういった動作をするアプリケーションがあるといいなと思っていたので、自分で作ることが出来てうれしかったです!
作成前の初期設定
コードを書く前に、必要な初期設定を行っていきます。
Blob StorageクライアントとStreamlitのインストール
最初にセットアップを行います。PythonのBlob Storageクライアントライブラリのセットアップおよび使い方はMicrosoftのドキュメントを参考にしました。
Blob Storageクライアントライブラリのインストールです。
pip install azure-storage-blob azure-identity
Streamlitのインストールです。
pip install streamlit
Shared Access Signatures (SAS)トークンを発行する
アプリケーションからアクセスするBlob Storageコンテナに対するShared Access Signatures (SAS)トークンを発行しました。許可する操作は今回は"読み取り"と"リスト"の2つにしました。
作成手順
では必要なPythonの処理について、ここからご紹介していきたいと思います。今回の処理内容は全て1つの.py
ファイルに記述しています。
ContainerClient
クラスオブジェクトの作成
画像が格納されているコンテナに対する操作を行うため、azure.storage.blob.ContainerClient
クラスのオブジェクトを取得します。
blob_service_client = BlobServiceClient(account_url=storage_account_url,credential=sas_token) container_client = blob_service_client.get_container_client(container=container_name)
上のコードのパラメータについて説明します。account_url
にはコンテナではなくStorage AccountのURLを指定します。credential
には先の手順で発行した該当コンテナに対するSASトークン、container
には画像を格納しているコンテナ名を指定します。
Blob名一覧を取得する
Blobそのものではなく、まずBlob名の一覧を取得します。Blob名はそのBlob(ファイル)が属するディレクトリ名を含んだフルパスの文字列になっています。Blob名一覧はアプリケーション開始時に一度だけ取得し、以降はStreamlitのセッションの間データを保存することが出来るst.session_state
から取得するようにしました。
if 'blob_name_list' not in st.session_state: blob_name_list = container_client.list_blob_names(name_starts_with=base_dir) blob_name_list = [b_name for b_name in blob_name_list if b_name.split('.')[-1] in ['JPG','jpg','PNG','png']] st.session_state['blob_name_list'] = blob_name_list
container_client.list_blob_names
のname_starts_with
は取得するBlob名の条件として最初に含まれる文字列を指定することが出来ます。ここではコンテナ直下にあるディレクトリ名を指定しています。また画像ファイルのみ対象にするため、画像の拡張子を持つBlob名だけ取得するようにしました。
日付ディレクトリ名一覧を取得する
コンテナ直下のディレクトリの下に、日付ディレクトリに分けて画像を格納しています。日付を指定して画像一覧を表示出来るよう、この日付ディレクトリ名一覧を取得します。ディレクトリ名はBlob名に"/"で区切られて含まれているため、Pythonの文字列操作関数を利用して以下のように取得出来ます。
if 'cat_0' not in st.session_state: cat_0_list = [] for b_name in st.session_state['blob_name_list']: splited_name = b_name.split('/')[1:] cat_0_list.append(splited_name[0]) st.session_state['cat_0'] = sorted(list(set(cat_0_list)))
Pythonの文字列の関数split
はパラメータに指定した区切り文字で区切った文字列リストを取得出来ます。最初の要素はcontainer_client.list_blob_names
のname_starts_with
で指定した最上位のディレクトリ名に該当するため、1以上のインデックスの要素だけを取得します。
条件を設定するサイドバーの表示
表示する画像の条件設定用のサイドバーを表示するためのコードです。セレクトボックスとボタンで構成しています。
with st.sidebar: sel0 = st.selectbox('日にちを選んでください',st.session_state['cat_0']) disp_button = st.button('表示')
画像一覧表示部分の表示
次に画像一覧を表示する、このアプリケーションのメインになるパーツを作っていきます。条件(日付ディレクトリ)が指定され、"表示"ボタンがクリックされた時に表示されるようにします。まず条件に該当するBlob名と該当するBlobの件数を取得し、件数をテキストで表示します。
#画像表示部分 if sel0 and disp_button: #条件に該当する名称のblob名を取得 sel_blobs = [b for b in st.session_state['blob_name_list'] if b.startswith(base_dir + '/' + sel0)] image_num = len(sel_blobs) #件数表示 st.text(f'件数:{image_num}') ...
そしてここからが画像のグリッド表示に該当する部分です。1行にいくつ画像を表示するのかは変数image_column_num
で指定出来るようにしました。今回は4を設定しています。whileループを回しながら、1行ずつ画像を左から並べていきます。Streamlitではst.columns
を使うことで水平方向に画面を分割することが出来るため、1行をその行に並べる画像枚数で分割し、それぞれに画像のファイル名と画像を表示させていきます。
# 画像表示部分 if sel0 and disp_button: ... ... # 画像のグリッド表示 remaining = image_num row_num = 0 while remaining > 0: # 横に並べたい画像数分全て並べられる if remaining >= image_column_num: display_num = image_column_num # 残っている画像がimage_column_numよりも少ない場合は残り全てこの行に並べる else: display_num = remaining grid_columns = st.columns(display_num) temp_blobs = sel_blobs[row_num * image_column_num:row_num * image_column_num + display_num] temp_names = [b.split('/')[-1] for b in temp_blobs] for i in range(display_num): with grid_columns[i]: title = temp_names[i] st.text(title) bytes = container_client.get_blob_client(temp_blobs[i]).download_blob().readall() image = Image.open(io.BytesIO(bytes)) st.image(image, width=100) remaining -= display_num row_num += 1
あとはこのファイルをコマンドラインでstreamlit run home.py
のように実行するとアプリケーションを開始することが出来ます!
まとめ
今回はAzure Blob Storageに格納している画像を一覧表示して確認出来るようにするため、PythonのBlob StorageクライアントライブラリとWebアプリケーション構築ライブラリStreamlitを使って簡単なアプリケーションを自作してみた話をご紹介しました。StreamlitはPythonだけでサクサクっとアプリケーションを作ることが出来、とても便利です。今度は画像を表示させるだけでなく、ちょっとした編集機能なども実装してみたいな、と思いました。