CCCMKホールディングス TECH LABの Tech Blog

TECH LABのエンジニアが技術情報を発信しています

ブログタイトル

Azure Blob Storageに保存した画像一覧を表示するアプリをStreamlitで作ってみました。

こんにちは、CCCMKホールディングス技術開発の三浦です。

1月の三連休が終わりました。だんだん周りから年末年始の雰囲気が消えて、元の生活に戻っていく感じがします。長期休みでもなるべく生活のペースを崩さないように意識しているつもりなのですが、それでも年末年始はちょっと遊び(ゲーム)の時間が増えてしまったり、食べる量が増えたりしました。少しずつ年末年始前の状態に戻していこうと思います。

さて、最近Azure Blob Storage(Azure Data Lake Storage)に格納していた画像ファイルをちょっと確認したいな、と思うことがありました。AzureのStorageを管理したり、格納されたファイルの確認用にMicrosoftからはAzure Storage Explorerというアプリケーションが提供されています。

azure.microsoft.com

普段Blob Storageのファイルの確認はこのアプリケーションを利用しているのですが、画像ファイルのサムネイル表示が出来ず、画像ファイルを一覧で確認したい場合何か他にいい方法がないかな、と考えていました。

他に方法がないなら自分で作ってみようかな、ということで、Azure Blob StorageのPython向けのClientライブラリとWebアプリケーション構築用のライブラリ"Streamlit"を使ってBlob Storage上の画像ファイルを一覧表示出来る、簡単なアプリケーションを作ってみました。

作ったアプリケーション

今回作ったアプリケーションは以下のようなものです。

アプリケーションの画面

左側のサイドメニューで画像一覧を表示させたいBlob Storage上の日付ごとのディレクトリを指定し、"表示"ボタンをクリックすると該当画像の件数と画像のサムネイルがグリッド表示されます。まさにこういった動作をするアプリケーションがあるといいなと思っていたので、自分で作ることが出来てうれしかったです!

作成前の初期設定

コードを書く前に、必要な初期設定を行っていきます。

Blob StorageクライアントとStreamlitのインストール

最初にセットアップを行います。PythonのBlob Storageクライアントライブラリのセットアップおよび使い方はMicrosoftのドキュメントを参考にしました。

learn.microsoft.com

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_namesname_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_namesname_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だけでサクサクっとアプリケーションを作ることが出来、とても便利です。今度は画像を表示させるだけでなく、ちょっとした編集機能なども実装してみたいな、と思いました。