【Python】【CSS】Gradioでグリッドデザインを使う

 この記事で扱っているGradioのバージョンは4.4.1です。

 Gradioは機械学習モデルを手軽にデモするためのwebページを作るためのライブラリです。ざっくり作るのであれば次のような入力配列、出力配列、処理関数だけでも動きます。

def process_image(
        src_image_path: str,
        points_per_side: int,
        pred_iou_thresh: float,
        stability_score_thresh: float,
):
    // 処理本体
    
    // 処理結果の画像をタプルとして返す
    return src_image_path, combined_mask, some_mask, anomaly_image

if __name__ == "__main__":
    inputs = [
        gr.Image(label="入力画像 - 処理する画像をアップロードします。", type="filepath"),
        gr.Slider(minimum=4, maximum=64, step=32, label="画像の一辺に沿ってサンプリングされる点の数。点のサンプリング密度"),
        gr.Slider(minimum=1, maximum=4096, step=64, label="モデルが同時に実行するポイントの数。GPUメモリと相談"),
        gr.Slider(minimum=0, maximum=1, step=0.001, label="モデルの予測マスク品質"),
    ]
    outputs = [
        gr.Image(type="filepath", label="元画像", ),
        gr.Image(type="pil", label="セグメンテーション結果"),
        gr.Image(type="pil", label="ある物体を検出した画像"),
        gr.Image(type="numpy", label="異常検出結果画像")
    ]
    // process_image関数の引数と返り値に合う数の要素の inputs と outputs を用意
    gr.Interface(
        fn=process_image, inputs=inputs, outputs=outputs,
        title="Gradioテスト",
    ).launch()

 実際に上記コードを動かすと次の画面になります。

 あっさりした記述で画面を作れるのは便利です。開発はデモ画面よりもデモで動く中身の方に注力したいため、これぐらいあっさりしてくれるとありがたいです。

 しかしながらデフォルトのままですと入力も出力も増えるにつれて縦に伸び続けて見難くなってしまいます。先ほどの例は出力が4つの画像でしたが、もうそれだけで一画面からはみ出しています。これを見やすくします。このためにはBlocksを使うのが便利です。

 BlocksとCSSのGridを使って見やすくしたコードと結果が次です。

if __name__ == "__main__":
    # 入出力を変数にまとめるので、その箱。関数実行時にまとめて渡すのでこの形が扱いやすい
    inputs = []
    outputs = []
    # Blocks を使うと Interface よりも細かい設定ができる
    # @see https://www.gradio.app/docs/blocks
    # css ではどの CSS ファイルを読み込むか指定できる
    with gr.Blocks(css='style.css') as demo:
        gr.Markdown("# Gradioテスト") # 見出しのタイトル。マークダウンの # は見出しのレベルを表す
        # 入力ブロックと出力ブロックを含む 2 列のレイアウトを作成
        # elem_id は HTML 上での id 属性に相当する。この属性を指定することによって CSS を用いたスタイル指定がやりやすくなる
        with gr.Row(elem_id="root-box"):
            # root-box 同様に elem_id を指定
            with gr.Column(elem_id="input-box"):
                inputs.append(gr.Image(label="入力画像 - 処理する画像をアップロードします。", type="pil"))
                inputs.append(gr.Slider(minimum=4, maximum=64, step=32, label="画像の一辺に沿ってサンプリングされる点の数。点のサンプリング密度"))
                inputs.append(gr.Slider(minimum=1, maximum=4096, step=64, label="モデルが同時に実行するポイントの数。GPUメモリと相談"))
                inputs.append(gr.Slider(minimum=0, maximum=1, step=0.001, label="モデルの予測マスク品質"))
                submit_button = gr.Button("処理開始")
            # root-box 同様に elem_id を指定
            with gr.Column(elem_id="output-box"):
                outputs.append(gr.Image(label="元画像"))
                outputs.append(gr.Image(label="セグメンテーション結果"))
                outputs.append(gr.Image(label="ある物体を検出した画像"))
                outputs.append(gr.Image(label="異常検出結果画像"))
        # gr.Button で用意したボタンが押された時にどの処理をどう走らせるか決められる
        # fn には関数を指定する。inputs には関数に渡す引数を指定する。outputs には関数の戻り値を表示する場所を指定する
        submit_button.click(
            fn=process_image, inputs=inputs, outputs=outputs
        )
    # demo.launch() で Gradio の Web サーバーを起動する
    demo.launch()
/* style.css */
h1 {
    /* タイトルを中央ぞろえ */
    text-align: center;
}
#root-box {
    /* 入力欄:出力欄 = 1:2  の比率にする*/
    display: grid;
    grid-template-columns: 1fr 2fr;
    /* 入力欄と出力欄の隙間を大きくする */
    gap: 30px;
}
#output-box {
    /* 出力欄の結果が2列になるようにする */
    display: grid;
    grid-template-columns: 1fr 1fr;
}

 一目で全ての結果を確認でき、やたらと広くとられていた入力欄の領域を出力欄が一部使うようになりました。こんな感じで Gradio と CSS でグリッドを使うと比較的お手軽に見やすい画面を作れます。余談ですが、作りたいグリッドのイメージがあるならば、それをChatGPTやCopilotに伝えるといい感じに grid-xxx を使っていい感じのデザインの CSS を提案してくれます。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG