ラベル Google の投稿を表示しています。 すべての投稿を表示
ラベル Google の投稿を表示しています。 すべての投稿を表示

2025年8月26日火曜日

GoogleのVirtual Try-On APIを呼び出すAPEXアプリケーションを作成する

GoogleよりVirtual Try-Onというモデルがリリースされています。日本語ではバーチャルでお試しAPIと、なんとなく楽しそうな名前になっています。最近MCPサーバーばかりでAPEXアプリケーションを作っていなかったので、Virtual Try-On APIを呼び出すアプリを作ってみました。

作成したアプリは以下のように動作します。


作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/google-virtual-try-on.zip

Virtual Try-On APIは、ブラウザにasapさんによる「Virtual Try-On APIをPythonで試す方法」の記事がお勧めされたので知りました。Oracle APEX(正確にはOracle Database)でPythonは実行できないので、認証も含めてすべてPL/SQLで記述する必要があります。

Googleのサービスを呼び出すためのAPI認証については、以前の記事「GoogleのGemini APIをOracle APEXから呼び出す」でパッケージUTL_CRED_GOOGLEを紹介しています。

Virtual Try-OnはVertex AIのサービスのようなので、プロジェクトにサービス・アカウントを作ってAPI認証を行ないます。サービス・アカウントに作成した秘密キーをダウンロードし、その秘密キーよりAPI認証に使用するトークンを生成します。

以下は、秘密キーからトークンを生成してAPEXのWeb資格証明として登録するスクリプトです。APEXアプリケーションや公開するスクリプトに秘密キーを埋め込むと手違いで公開してしまうこともありえるので、手元のPCからSQLclやsqlplusでAPEXのワークスペース・スキーマに接続して実行することを想定しています。


作成したAPEXアプリケーションですが、ホーム・ページにすべての機能を実装しています。

Google Virtual Try-On APIに送信する画像を選択するページ・アイテムとして、P1_PERSON(人物の画像)とP1_PRODUCT(服の画像)を作成しています。タイプイメージ・アップロードストレージタイプ表APEX_APPLICATION_TEMP_FILESとします。選択した画像を送信せずにプレビューするため、記事「画像ビューワーのアプリを改良する」で紹介している動的アクションを実装しています。

ボタンTRY_ONをクリックすると、ページ・アイテムP1_PERSONP1_PRODUCTに設定した画像がサーバーに送信されます。

サーバーでは以下のコードを実行し、GoogleのVirtual Try-On APIを呼び出します。呼び出しに使用した人物の画像、服の画像およびお試しの結果である画像は、APEXコレクションに保存します。


処理結果はページ・アイテムP1_PERSON_IMAGEP1_PRODUCT_IMAGEP1_TRY_ONに表示します。タイプとしてイメージの表示を選択し、設定基準SQLで戻されたBLOB列SQL文として以下を記述します。c001の条件はPERSONPRODUCTTRYONのどれかになります。

select blob001 from apex_collections where collection_name = 'VIRTUAL_TRY_ON' and c001 = 'PERSON'


作成したアプリケーションの説明は以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年5月29日木曜日

Google MediaPipe Solutionsの手指のランドマーク検出とオブジェクト検出をAPEXアプリで実行する

Google MediaPipeソリューションガイドを参照して、ソリューションに含まれる手指のランドマーク検出オブジェクト検出を、APEXアプリケーションに実装してみます。

参照しているGoogle MediaPipeソリューションガイドのリンクは以下です。

どちらもウェブ版を元にAPEXに実装しています。参照しているのはCodepenのコード例です。

手指のランドマーク検出ガイドのコード例はこちらです。
オブジェクト検出タスクガイドのコード例はこちらです。

CodepenではサンプルとしてHTML、CSSおよびJS(TypeScript)の3種類のコードが提供されています。このうち、CSSとJS(TypeScript)はほぼそのままAPEXアプリの静的アプリケーション・ファイルとして含めています。

CSSの記述に含まれているクラスで、APEXの表示に影響するクラス定義をコメントアウトしています。また、JS(TypeScript)のコードはJavaScriptにしないと静的アプリケーション・ファイルとして保存できないため、型定義を削除しています。追加したコードは、ボタンを押したときにevent.preventDefault()の呼び出して、APEXのボタン・クリックの標準動作であるページの送信を行わないようにしています。

作成した手指のランドマーク検出のAPEXアプリは、以下のように動作します。


このAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/google-mediapipe-hand-landmaker-on-apex.zip

オブジェクト検出のAPEXアプリは以下のように動作します。


このAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/google-mediapipe-object-detector-on-apex.zip

カメラにアクセスするため、HTTPSおよびホスト名が正しく付けられているサイトでAPEXアプリケーションが動作している必要があります。上記のAPEXアプリはAlways FreeのAutonomous Databaseで実行しています。

両方とも、画面の構成は同じです。そのため、手指のランドマーク検出のAPEXアプリを例に取って実装について紹介します。

JavaScriptとCSSのコードは、静的アプリケーション・ファイルapp.jsおよびapp.cssとして保存しています。


アプリケーションの画面はホーム・ページに実装しています。

ページ・プロパティJavaScriptファイルURLに、HTML内で参照しているJavaScriptのファイルへのリンクと静的アプリケーション・ファイルapp.jsへのリンクを含めています。

CSSファイルURL静的アプリケーション・ファイルapp.cssへのリンクを含めています。


静的な画像を対象とした手指のランドマーク検出と、Webカムの動画より手指のランドマーク検出を行う2種類のデモが含まれています。それぞれdemo_imagesおよびdemo_videoというリージョンに実装しています。

そのリージョンを切り替えて表示するリージョンdemosを作成しています。タイプリージョン表示セレクタです。リージョンの静的IDとしてdemos外観CSSクラスinvisibleを設定しています。これはMediaPipeの初期化が完了するまで、idがdemosのリージョンを非表示にする処理がJavaScriptで実装されているためです。

リージョンdemo_imagesは、画像から手指のランドマーク検出を行うためのリージョンです。画像はリージョンPreviewに表示されます。タイプ静的コンテンツソースHTMLコードに以下を記述しています。
<div class="detectOnClick">
    <img id="preview" src="&P1_URL." title="Click to get detection!"></img>
</div>
CSSクラスとしてdetectOnClickが設定されている要素をクリックしたときに、その子要素のimgとして表示されている画像を対象に、手指のランドマーク検出が行われるようapp.js内にJavaScriptで記述されています。


画像のプレビューの実装については、こちらの記事「画像ビューワーのアプリを改良する」の「ファイルを送信する前にプレビューを表示」のセクションで紹介している方法をそのまま採用しています。

リージョンdemo_videoは、動画から手指のランドマークの検出を行うためのリージョンです。こちらは静的コンテンツとしてCodepenのHTMLの記述をそのままHTMLコードに書き込んでいます。
<p>Hold your hand in front of your webcam to get real-time hand landmarker detection.</br>Click <b>enable webcam</b> below and grant access to the webcam if prompted.</p>

<div id="liveView" class="videoView">
    <button id="webcamButton" class="mdc-button mdc-button--raised">
        <span class="mdc-button__ripple"></span>
        <span class="mdc-button__label">ENABLE WEBCAM</span>
    </button>
    <div style="position: relative;">
        <video id="webcam" style="position: abso" autoplay playsinline></video>
        <canvas class="output_canvas" id="output_canvas" style="position: absolute; left: 0px; top: 0px;"></canvas>
    </div>
</div>
ボタンwebcamBottonをクリックしたときに実行されるコードは、app.jsに記述されています。APEXではボタンのクリックでページの送信が行われます。それを抑止するために、webcamButtonをクリックしたときに呼び出されるファンクションenableCamの先頭でevent.preventDefault()を実行するようにしています。


概ね以上の変更を行うことで、MediaPipe SolutionsのサンプルをAPEXアプリとして実装できました。

余談ですが、Object DetectorのCodepenのサンプルに含まれるJavaScriptのコードは、以下から始まっています。
import {
  ObjectDetector,
  FilesetResolver,
  Detection,
  ObjectDetectionResult
} from "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.2";
APEXに貼り付けて実行すると、以下のエラーが発生しました。
app.js:18 Uncaught SyntaxError: The requested module 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.2' does not provide an export named 'Detection' (at app.js:18:3)
Codepenで動作しているコードなのでAPEX側の問題とばかり考えていたため、原因はまったく見当がつきませんでした。

仕方ないのでGoogleのことはGoogleに聞こうと思い、Gemini 2.5 Flashに先のエラー・メッセージを貼り付けて原因を聞いたところ、元のコードがおかしいと回答されました。


Task-based API: MediaPipe Tasks Vision is designed around "tasks" like FaceDetector, ObjectDetector, HandLandmarker, etc. You don't directly interact with a generic "Detection" class. Instead, you use the specific task classes to perform detections.

GeminiからDetectionとObjectDetectionResultをインポート対象から外すように提示され、実際、外したらエラーは解消しました。

改めてAIはすごいな、と感じました。

今回の記事は以上です。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年5月22日木曜日

APEXアプリのページ上でGoogle AI EdgeのMediaPipe LLM Inference APIを呼び出す

GoogleがGoogle AI Edgeのソリューションとして提供しているMediaPipe LLM Inference APIを、Oracle APEXのアプリケーションのページ上で呼び出してみます。

以下のサンプル・アプリケーションに含まれるindex.jsを、Oracle APEXで動くように改変ししました。

MediaPipe LLM Inference task for web
https://github.com/google-ai-edge/mediapipe-samples/tree/main/examples/llm_inference/js

実行環境はmacOSのChromeです。作業としては、ウェブ用LLM推論ガイドのクイックスタートを参照しています。

元々は最近発表されたGemma 3nを動かすつもりだったのですが、Gemma 3nは現時点では.taskファイルのみ公開されているようです。ウェブ用LLM推論ガイドモデル変換に.taskの対応プラットフォームとしてAndroid、iOSはありますが、ウェブがありません。そのため、クイックスタートのとおりにgemma2-2b-it-gpu-int8を使用しました。

サンプル・アプリケーションのindex.htmlは、APEXのアプリケーションのページとして作り直しています。

作成したアプリケーションは以下のように動作します。


空のAPEXアプリケーションを作成し、機能はすべてホーム・ページに実装します。

APEX向けに書き直したindex.jsは以下になります。

このファイルを静的アプリケーション・ファイルとしてアップロードします。作成した静的アプリケーション・ファイル参照をメモします。


静的アプリケーション・ファイルindex.jsをページに読み込むために、ホーム・ページのページ・プロパティJavaScriptファイルURLに以下を記述します。moduleを指定します。

[module]#APP_FILES#index#MIN#.js


LLMに送信する文字列は、ページ・アイテムP1_INPUTに入力します。タイプテキスト領域を選択します。セッション・ステートデータ型CLOBストレージリクエストごと(メモリーのみ)を設定します。


ボタンSUBMITを作成します。ボタンをクリックしたときに実行されるイベントハンドラは、index.js内に記述されています。APEXアプリとしては何も処理をしないので、動作アクションとして動的アクションで定義を選択します。また、index.js内でこのボタンをsubmitというIDで見つけているので、詳細静的IDsubmitを設定します。


LLMの出力はページ・アイテムP1_OUTPUTに表示します。LLMの出力フォーマットはMarkdownなので、ページ・アイテムのタイプとしてMarkdownエディタを選択します。セッション・ステートデータ型CLOBストレージリクエストごと(メモリーのみ)を設定します。


Oracle APEXでMediaPipe LLM Inference APIを呼び出すサンプルは以上で完成です。

モデル・ファイルのgemma2-2b-it-gpu-int8.binは、kaggle.comからダウンロードしました。


LLMとしては小さなモデルとはいえ、数GBはあります。Oracle APEXの静的アプリケーション・ファイルとして保存するのも避けたかったので、ORDSから直接ファイルとしてダウンロードできる場所に配置しました。

(CDNではなく)ORDSがOracle APEXのイメージ・ファイルを提供するように構成している場合、以下のようにstandalone.static.pathが設定されています。(global/settings.xml内)

<entry key="standalone.static.path">/opt/oracle/apex/images</entry>

このパスの下はURLから見ると/i/以下にあるファイルとして参照されます。そのため、(上記の例では/opt/oracle/apex/images以下に)ディレクトリmodelsを作成し、その下にgemma2-2b-it-gpu-int8.binを置きました。

index.js内ではモデル・ファイルを以下のように指定しています。
const modelFileName = '/i/models/gemma2-2b-it-gpu-int8.bin'; /* Update the file name */
CORSのエラーが発生しないよう、APEXが動作しているホストと同じホストからモデル・ファイルをダウンロードする必要があるようです。また、ダウンロード元がlocalhost以外の場合、https経由でダウンロードする必要があるとの記載もありました。今回の作業ではAPEXはlocalhostでアクセスしているため、両方の条件が必須かどうかは確認できていません。

今回使用したモデルはWebGPUを使用します。Chromeのchrome://flagsを開いて、WebGPUに関する以下の2つの設定を有効にしています。

#enable-unsafe-webgpu、#enable-webgpu-developer-features


今回の記事は以上になります。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。https://github.com/ujnak/apexapps/blob/master/exports/sample-google-ai-edge-mediapipe.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2025年4月10日木曜日

GoogleのA2Aのサンプルに含まれるCoderエージェントをOracle APEXから呼び出してみる

先日、GoogleよりアナウンスされたAgent2Agentプロトコル(A2A)のサンプルに含まれているCoderエージェントをOracle APEXのアプリケーションから呼び出してみます。

Googleのアナウンスは以下より参照できます。

A2Aプロトコルのサンプルが含まれているGitHubのリポジトリは以下です。

作業はmacOS Sequoia 15.3.2で実施します。

作成したAPEXアプリケーションは以下のように動作します。最初にAgent Cardを参照し、Agent Cardに含まれるexamplesの1文を取り出し、タスクとしてCoderエージェントを呼び出しています。


最初にサンプルを含むA2Aのリポジトリをクローンしました。

git clone https://github.com/google/A2A

% git clone https://github.com/google/A2A

Cloning into 'A2A'...

remote: Enumerating objects: 320, done.

remote: Counting objects: 100% (90/90), done.

remote: Compressing objects: 100% (71/71), done.

remote: Total 320 (delta 31), reused 29 (delta 18), pack-reused 230 (from 2)

Receiving objects: 100% (320/320), 1.86 MiB | 20.21 MiB/s, done.

Resolving deltas: 100% (73/73), done.

% 


今回はTypeScriptで記述されたサンプルを実行します。サンプルのあるディレクトリへ移動します。

cd A2A/samples/js

% cd A2A/samples/js

js % 


README.mdに記載されてい内容を確認します。マークダウンなのでブラウザからA2A/samples/jsを参照します。


Coderエージェントの実行にはGoogleのAPIキーが必要です。Google AI Studioにアクセスし、API Keyを取得します。



サンプルが依存しているgenkitをインストールします。

npm install genkit

% npm install genkit

npm warn deprecated @types/handlebars@4.1.0: This is a stub types definition. handlebars provides its own type definitions, so you do not need this installed.


added 401 packages, and audited 402 packages in 9s


44 packages are looking for funding

  run `npm fund` for details


found 0 vulnerabilities

js % 


環境変数GEMINI_API_KEYに、先ほどGoogle AI Studioにアクセスして取得したAPIキーを設定します。

export GEMINI_API_KEY=[APIキー]

サンプルのCoderエージェントを実行します。

npm run agents:coder

js % npm run agents:coder


> a2a-samples-js@0.1.0 agents:coder

> npx tsx src/agents/coder/index.ts


[CoderAgent] Server started on http://localhost:41241

[CoderAgent] Press Ctrl+C to stop the server

A2A Server listening on port 41241 at path /



別ウィンドウからCoderエージェントを呼び出すクライアントを起動します。

npm run a2a:cli

js % npm run a2a:cli


> a2a-samples-js@0.1.0 a2a:cli

> npx tsx src/cli.ts


A2A Terminal Client

Agent URL: http://localhost:41241

Attempting to fetch agent card from: http://localhost:41241/.well-known/agent.json

✓ Agent Card Found:

  Name:        Coder Agent

  Description: An agent that generates code based on natural language instructions and streams file outputs.

  Version:     0.0.1

Starting Task ID: fe3dd9e3-0bf2-4710-b2cb-b0032fafe427

Enter messages, or use '/new' to start a new task.

Coder Agent > You: 


Coder Agent > You: に"Write a python function to calculate fibonacci numbers."を入力します。

Artifact Received: fibonacci.pyに続けて、コードが出力されていることが確認できます。

Enter messages, or use '/new' to start a new task.

Coder Agent > You: Write a python function to calculate fibonacci numbers.

Sending...


Coder Agent [11:00:50]: ⏳ Status: working

  Part 1: 📝 Text: Generating code...

Coder Agent > You: 

Coder Agent [11:00:55]: 📄 Artifact Received: fibonacci.py (Index: 0)

  Part 1: 📝 Text: # This file contains a function to calculate Fibonacci numbers iteratively.


def fibonacci(n: int) -> int:

  """

  Calculates the nth Fibonacci number iteratively.


  Args:

    n: The position in the Fibonacci sequence (non-negative integer).


  Returns:

    The nth Fibonacci number. Returns 0 for n <= 0.

  """

  if n <= 0:

    return 0

  elif n == 1:

    return 1

  else:

    a, b = 0, 1

    # Iterate n-1 times because we already have the first number (b=1 for n=1)

    for _ in range(n - 1):

      a, b = b, a + b

    return b


# Example usage (optional):

# print(fibonacci(0))  # Output: 0

# print(fibonacci(1))  # Output: 1

# print(fibonacci(10)) # Output: 55

# print(fibonacci(20)) # Output: 6765



Coder Agent [11:00:55]: ✅ Status: completed

  Part 1: 📝 Text: Generated files: fibonacci.py

SSE stream finished for method tasks/sendSubscribe.

--- End of response for this input ---

Coder Agent > You: 


以上でサンプルのCoderエージェントが実行できました。

このCoderエージェントをOracle APEXのアプリケーションから呼び出してみます。

作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/a2a-talk-to-agent.zip

空のAPEXアプリケーションを作成し、ホーム・ページにすべてを実装しています。

ページ・アイテムP1_TASKに、タスクとして送信するテキストを入力します。


ページ・アイテムP1_RESPONSESend a Taskとして呼び出したレスポンスに含まれる生成結果を表示します。


Agent Cardの内容はページ・アイテムP1_AGENT_CARDに保持します。


サンプルのCoderエージェントは、起動時にJSONRPCによるリクエストを受け付けるURLを表示します。

% npm run agents:coder


> a2a-samples-js@0.1.0 agents:coder

> npx tsx src/agents/coder/index.ts


[CoderAgent] Server started on http://localhost:41241


今回はAPEXをpodmanのコンテナで実行しているため、localhostへのアクセスにはhost.containers.internalを代わりに指定する必要があります。

このURLは作業環境で異なるため、アプリケーション定義置換に、置換文字列G_ENDPOINTとして設定します。


ボタンAGENT_CARDをクリックしてAgent Cardを取得します。Agent Cardを取得するプロセスAgent Cardに、以下のコードを記述します。



ボタンSEND_A_TASKをクリックしたときに、タスクの実行をCoderエージェントに依頼します。タスクの実行を依頼するプロセスSend a Taskに、以下のコードを記述します。



レスポンスに含まれる配列artifactsの先頭の要素を参照し、さらに、artifactに含まれる配列partsの先頭の要素だけを参照しているため、配列artifactsやpartsの要素が複数あると、生成結果を正しく受け取れないことがあります。今回はとりあえずA2Aのサンプルを呼び出すことが目的なので、そのままにしています。

以下のようにCoderエージェントのログにEmitting final file (index 1)と表示されている生成結果は、(indexが0でないため)APEXアプリケーションには表示されません。

[CoderAgent] Server started on http://localhost:41241

[CoderAgent] Press Ctrl+C to stop the server

A2A Server listening on port 41241 at path /

[[Task 3262b5fd-c805-faa8-e063-0200590aca0e] Created new task and history.

[CoderAgent] Emitting completed file (index 0): fib

[CoderAgent] Emitting final file (index 1): fibonacci.py

[Task 3262b5fd-c807-faa8-e063-0200590aca0e] Created new task and history.

[CoderAgent] Emitting final file (index 0): fibonacci.py

[Task 3262b5fd-c80a-faa8-e063-0200590aca0e] Created new task and history.

[CoderAgent] Emitting final file (index 0): fibonacci.py



今回の記事は以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。