その他

【ローカルVLM】手書きスケッチからWebUIを作る!画像認識への挑戦

その他
この記事は約12分で読めます。

はじめに

みなさん、こんにちは。2026年新卒として株式会社インプルに入社しました、yotaです。大学では、数学/数理学を専攻していました。ゲームが趣味で、プログラミングはゲーム用のツール開発のために勉強し始めました。

最近は生成AIの活用が主流になってきましたが、みなさんはどのAIをよく使っていますか?
私はGeminiをよく使っています。大学時代、定理の証明などの論理的な問題を解かせる際、当初使っていたChatGPTよりもGeminiの方が優秀だと感じたのがきっかけです(今は逆転してるかも?)。それ以来、趣味の開発でもGeminiを活用しています。とはいえ、最近はClaude Codeのコーディング能力も気になっており、少し浮気心が芽生えつつある今日この頃です。

今後、rambleでは、LLM(大規模言語モデル)やAI技術を軸にした記事を更新していく予定です!

今回の目標と流れ

手書きスケッチからWebUIを作る

プログラミングの学習を始めた頃、HTMLやCSSを使ったUIデザインの配置に大いに苦戦しました。「UIデザインが難しすぎる……手書きで直感的に配置できたらいいのに!」とも思いました。

そこで今回は、「手書きスケッチからWebアプリのプロトタイプ(HTML/CSS)を自動生成するアプリ」の構築を目指して検証してみます。

Geminiに相談!ローカルVLM「Llama 3.2-VIsion」との出会い

とはいえ、LLMを用いた開発については何も知らないため、まずは相棒のGeminiに相談してみました。

すると、「画像内の配置や要素を解釈し、コードを生成するにはVLM(視覚言語モデル)が適している」と教えてくれました。その中でも、ローカル環境で動作し、かつコード生成能力が高いモデルとして、Meta社のオープンソースモデル「Llama 3.2-Vision」を推奨してくれました。

今回は、完全無料でローカルPC上でAIを動かせる「Ollama」を使って環境を構築していきます。

環境構築

アプリ開発にはPythonStreamlitを使用します。さっそく環境を作っていきましょう。

0. 事前準備:Homebrewの導入

今回の環境構築では、パッケージマネージャーであるHomebrewを使用します。もしターミナルで brew -v と入力して command not found と表示される(まだインストールしていない)場合には、先に以下のコマンドをターミナルに貼り付けてHomebrewをインストールしておいてください。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

1. Ollamaとモデルのインストール

まずはOllamaをインストールし、Llama 3.2-Visionモデルをダウンロードします。

# Ollamaのインストール
brew install ollama

# Ollamaサービスの起動
brew services start ollama

# Llama 3.2-Visionモデルのダウンロード
ollama pull llama3.2-vision

※終了時は brew services stop ollama を実行します。

2. Pythonプロジェクトの準備

番外編:MacのPython環境構築でのプチトラブル

さて、次はPythonの準備!と思ったのですが、Macの設定で少し罠にハマったので共有します。

「Macなら最初からPythonが入っているだろう」と思い、VSCodeでPythonの拡張機能をインストールすると「Python入ってないかも…」との警告が…。ターミナルでpython3 --versionを叩くと、「Command Line Toolsをインストールしてください」とポップアップが出ました。

しかし、そのままインストールしようとすると「ソフトウェアアップデートサーバから入手できない」というエラーが発生。調べて「XCodeをインストールすると治った」という報告があり、Xcodeを入れようとするも、今度は「macOSのバージョンが古い」と弾かれてしまいました…。

結論として、「MacのOSを最新にアップデートする」ことで全て解決しました。OSアップデート後に再度コマンドを実行すると、無事にCommand Line Toolsが入り、Pythonが使えるようになりました(やはり元々入っていたようです)。もし同じエラーで詰まった方は、OSのバージョンを疑ってみてください!

本編:Pythonプロジェクトの準備

気を取り直して、アプリ用のディレクトリを作成し、仮想環境を立ち上げます。仮想環境を立ち上げることで、PC内にある他のPythonプロジェクトとライブラリのバージョンが混ざってエラーになるのを防ぐことができます。今回は、OllamaStreamlitに加え、画像の読み込みやリサイズも行うためにPillowも併せてインストールしておきます。

# ディレクトリを作成 + そのディレクトリに移動("sketch-to-web"のところは好きな名前にしてください)
mkdir sketch-to-web && cd sketch-to-web

# "venv"という名前の仮想環境を作成(ここで先ほど作成したディレクトリに新たに"venv"というディレクトリが作成されます)
python3 -m venv venv

# 仮想環境をアクティベート(ここでターミナルの入力行の先頭に(venv)という文字がつけば、無事に仮想環境に入れた合図です)
source venv/bin/activate

# Ollama, Streamlit, Pillowをインストール
pip install ollama streamlit pillow

仮想環境を終了(ディアクティベート)する時は、deactivate と入力します。

プロトタイプの作成

自分で一からコードを書く方が勉強にはなるのですが、今回はスピード重視!ということで、Streamlitを使ったWebアプリのプロトタイプコードはGeminiに書いてもらいました(app.pyなどのPythonファイルは作成しておいてください)。

import streamlit as st
import ollama
from PIL import Image
import io
import re

# ページの設定
st.set_page_config(page_title="sketch-to-web", layout="wide")
st.title("🎨 sketch-to-web")
st.caption("手書きのスケッチをローカルLLMでHTML/CSSコードに変換します")

# サイドバー:モデル選択
st.sidebar.header("Settings")
model_name = st.sidebar.selectbox("Model", ["llama3.2-vision"], index=0)

# メインエリア:ファイルアップローダー
uploaded_file = st.file_uploader("手書きの構成図やスケッチをアップロードしてください", type=["png", "jpg", "jpeg"])

if uploaded_file is not None:
    # 画像の表示
    image = Image.open(uploaded_file)
    
    col1, col2 = st.columns(2)
    
    with col1:
        st.subheader("Uploaded Sketch")
        st.image(image, width='stretch')

    with col2:
        if st.button("Generate HTML"):
            with st.spinner("思考中... (ローカルLLMで解析しています)"):
                try:
                    # 画像をバイトデータに変換
                    img_byte_arr = io.BytesIO()
                    image.save(img_byte_arr, format='PNG')
                    img_bytes = img_byte_arr.getvalue()

                    # システムプロンプト
                    prompt = """
                    You are an expert web developer. 
                    Analyze the provided sketch and convert it into a professional single-file HTML with Tailwind CSS.
                    Include all styles within the HTML file.
                    Output ONLY the code block starting with ```html.
                    """

                    # Ollamaにリクエスト
                    response = ollama.chat(
                        model=model_name,
                        messages=[{
                            'role': 'user',
                            'content': prompt,
                            'images': [img_bytes]
                        }]
                    )
                    
                    raw_content = response['message']['content']

                    # 正規表現によるコード抽出
                    code_match = re.search(r'```html\n(.*?)```', raw_content, re.DOTALL)
                    if code_match:
                        clean_code = code_match.group(1)
                    else:
                        clean_code = raw_content.strip().replace('```html', '').replace('```', '')

                    st.subheader("Generated Code")
                    st.code(clean_code, language='html')
                    
                    # プレビュー表示
                    st.subheader("Live Preview")
                    st.components.v1.html(clean_code, height=600, scrolling=True)
                    
                    # ダウンロードボタン
                    st.download_button(
                        label="Download HTML",
                        data=clean_code,
                        file_name="prototype.html",
                        mime="text/html"
                    )
                except Exception as e:
                    st.error(f"エラーが発生しました: {e}")
                    st.info("Ollamaアプリが起動しているか確認してください。")

一瞬で作ってくれますね。モデルの選択やダウンロードボタン、プレビュー表示など頼んでいないところまで作ってくれました。プロンプトが英語で書いてありますが、プロンプト設計は後回しにしてとりあえず試してみましょう!

コードを保存したら、以下のコマンドでWebアプリを立ち上げます。

streamline run app.py

ブラウザが自動的に開き、画像アップロード機能のついたWebアプリが起動します!

いざ検証!

ここからが本番です。AIによる「意味解釈」の精度を測るため、自作したテスト画像を読み込ませてHTMLを出力させてみました。

テスト1:シンプルな基本レイアウト

最初は「Header」「Main」「Footer」のみを描いたシンプルなスケッチを入力してみました。

結果は次のようになりました。

画像の構成を認識し、概ね正しいレイアウトのHTMLが出力されました!プレビューを見てもHeader、Main、Footerが縦並びに配置されています。

テスト2:画像プレースホルダーを含むUI

次は少し難易度を上げて、「3つの並んだカード」のスケッチを入力してみます。カードの上部には画像のプレースホルダーのつもりで「×」印を描いています。

結果は次のようになりました。

失敗です。画像のプレースホルダーという意味を理解できず、スケッチに描かれた斜め線を「幾何学模様」として解釈してしまい、HTMLで無理やり斜め線を再現しようとする複雑なコード(ループ)を生成してしまいました。

考察と次なる一歩

今回の検証で、OllamaLlama 3.2-Visionを使えば、完全無料のローカル環境だけで「画像からコードを生成するアプリ」の基盤が作れることがわかりました。

一方で、テスト2のように「画像の図形を無理やりコーディングしようとする」という失敗も起きました。しかし、これを直ちに「VLMの限界や弱点」と結論づけるのは早計です。今回はプロンプト設計をほとんど行わずに画像を丸投げしたため、AIが「意味を解釈する」のではなく「見た目を物理的に再現する」タスクと勘違いした可能性が非常に高いためです。

本格的なアプリに昇華させるには、画像をそのままVLMに丸投げするのではなく、プロンプト設計の調節や画像の前処理などにより、精度の向上を図るべきでしょう。加えて、そもそも「どこで精度が落ちているのか」を確認するためにも、処理の「機能分解」が必要だと考えています。

次回の記事では、この方針をもとに、機能を分解した処理を実装し、より精度の高いプロトタイプの構築に挑みたいと思います。お楽しみに!