2025年6月25日水曜日

Oracle Database 23ai MLEにJIMPをロードし画像をグレースケールに変換する

Oracle CorporationのMartin Backさんが、setTimeout()のpolyfillを作成し、画像処理ライブラリのJIMPをMLEにロードして、画像をグレースケールに変換する手順をブログ記事で公開されています。

Multilingual Engine: polyfill timeouts and intervals

JIMPについては以前にMLEにロードを試みたことがありますが、Martin Backさんの記事にあるようにMLEにはsetTimeout()やfsモジュールが無いため諦めました。

今回はMartin Backさんの記事に沿って、MLEにJIMPとsetTimeout()のpolyfillをロードし、それを使用して画像をグレースケールに変換するAPEXアプリケーションを作成します。JIMPのロードやpolyfillの説明については、上記に掲載したMartin Backさんのブログ記事を参照してください。

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


以下より、実施した作業を記述します。

APEXワークスペースに紐づくスキーマで、MLEモジュールのロードやJavaScriptを実行します。そのため、スキーマにJavaScriptを実行する権限を与えます。これらのスクリプトはDBA権限を持つユーザー(Autonomous DatabaseであればADMIN、オンプレミスであればSYS AS SYSDBA)で実行します。
grant execute on javascript to [APEXワークスペース・スキーマ];
grant execute dynamic mle to [APEXワークスペース・スキーマ];
grant create mle to [APEXワークスペース・スキーマ];
続いて、作業に使用するディレクトリを作成し、プロジェクトを初期化します。ディレクトリ名はdemo-jimpとします。今回の作業で使用したnpmのバージョンは11.3.0です。

npm -v
mkdir demo-jimp
cd demo-jimp
npm init -y


% npm -v         

11.3.0

% mkdir demo-jimp

% cd demo-jimp

demo-jimp % npm init -y

Wrote to /Users/**********/Documents/demo-jimp/package.json:


{

  "name": "demo-jimp",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "keywords": [],

  "author": "",

  "license": "ISC",

  "type": "commonjs"

}




demo-jimp % 


jimpをインストールします。

npm install jimp

demo-jimp % npm install jimp


added 70 packages, and audited 71 packages in 6s


10 packages are looking for funding

  run `npm fund` for details


found 0 vulnerabilities

demo-jimp % 


esbuildをインストールします。demo-mle.jsに画像をグレースケールに変換するファンクションimage2Greyscaleを記述し、esbuildを実行してdemo-mle.jsにJIMPをバンドルします。

npm install --save-exact --save-dev esbuild

demo-jimp % npm install --save-exact --save-dev esbuild



added 26 packages, and audited 97 packages in 2s


10 packages are looking for funding

  run `npm fund` for details


found 0 vulnerabilities

demo-jimp % 


Martin BackさんのGitHubのリポジトリよりdemo-mle.jspolyfill.jsをダウンロードし、src/javascript以下に配置します。

curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/demo-mle.js
curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/polyfill.js
mkdir -p src/javascript
mv demo-mle.js polyfill.js src/javascript
ls src/javascript

demo-jimp % curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/demo-mle.js

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100  1023  100  1023    0     0   4903      0 --:--:-- --:--:-- --:--:--  4918

demo-jimp % curl -OL https://raw.githubusercontent.com/martincarstenbach/javascript-blogposts/refs/heads/main/timeout-polyfill/src/javascript/polyfill.js

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100  2996  100  2996    0     0   7682      0 --:--:-- --:--:-- --:--:--  7701

demo-jimp % mkdir -p src/javascript

demo-jimp % mv demo-mle.js polyfill.js src/javascript

demo-jimp % ls src/javascript

demo-mle.js polyfill.js

demo-jimp % 


esbuildを実行しダウンロードしたdemo-mle.jsにJIMPをバンドルします。

npx esbuild src/javascript/demo-mle.js --bundle --outfile=dist/bundle.js --format=esm

demo-jimp % npx esbuild src/javascript/demo-mle.js --bundle --outfile=dist/bundle.js --format=esm


  dist/bundle.js  1008.8kb


Done in 44ms

demo-jimp % 


SQLclでAPEXのワークスペース・スキーマに接続し、MLEモジュールを作成します。

以下は、ローカルで実行しているOracle Database 23ai Freeに、ユーザーWKSP_APEXDEVで接続して作業した例になります。

sql wksp_apexdev/[パスワード]@localhost/freepdb1

demo-jimp % sql wksp_apexdev/********@localhost/freepdb1



SQLcl: 水 6月 25 12:31:27 2025のリリース25.1 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


接続先:

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free

Version 23.8.0.25.04


SQL> 


demo-mle.jsにJIMPをバンドルしたdist/bundle.jsを、MLEモジュールJIMP_TEST_MODULEとして作成します。

mle create-module -replace -module-name jimp_test_module -filename dist/bundle.js

SQL> mle create-module -replace -module-name jimp_test_module -filename dist/bundle.js

MLEモジュールjimp_test_moduleが作成されました

SQL> 


setTimeout()のpolyfillを、MLEモジュールTIMERS_POLYFILLとして作成します。

mle create-module -replace -module-name timers_polyfill -filename src/javascript/polyfill.js

SQL> mle create-module -replace -module-name timers_polyfill -filename src/javascript/polyfill.js

MLEモジュールtimers_polyfillが作成されました

SQL> 


以上で、demo-mle.jsに含まれているファンクションimage2Greyscaleを、データベース内で呼び出せるようになりました。

画像をグレースケールに変換するAPEXアプリケーションを作成します。

変換する画像および変換後の画像を保存する表EBAJ_DEMO_IMAGESを作成します。

SQLワークショップSQLコマンドで、以下のDDLを実行します。
create table ebaj_demo_images (
    id              number generated by default on null as identity
                    constraint ebaj_demo_images_id_pk primary key,
    filename        varchar2(80 char),
    mime_type       varchar2(80 char),
    operation       varchar2(80 char),
    source_image    blob,
    target_image    blob
);

作成したMLEモジュールJIMP_TEST_MODULEおよびTIMERS_POLYFILLを含むMLE環境としてJIMP_ENVを作成します。以下のCREATE MLE ENV文を実行します。
create mle env jimp_env imports (
    'jimp' module jimp_test_module,
    'polyfill' module timers_polyfill
);

使用するデータベース・オブジェクトの準備ができました。

APEXアプリケーションを作成します。

アプリケーション・ビルダーよりアプリケーション作成ウィザードを開きます。アプリケーションの名前demo jimpとします。デフォルトで追加されているホーム・ページ削除します。


ホーム・ページを削除したのちページの追加をクリックし、代わりとなるフォームのページを追加します。

ページの追加をクリックしたときに開く画面でフォームを選択します。


ページ名グレースケール変換とします。に先ほど作成したEBAJ_DEMO_IMAGESを選択します。

ページの追加をクリックします。


表EBAJ_DEMO_IMAGESをソースとしたフォームのページが追加されました。

アプリケーションの作成をクリックします。


アプリケーションが作成されます。

画像のアップロードやグレースケールへの変換処理は、ページ番号1のグレースケール変換に実装します。


APEXアプリケーションからMLEモジュールを参照できるように、APEXアプリケーションにMLE環境を紐づけます。

アプリケーション定義セキュリティデータベース・セッションのセクションに含まれるMLE環境に、先ほど作成したMLE環境JIMP_ENVを設定します。

変更の適用をクリックします。


ページ・デザイナでページ、グレースケール変換を開きます。


主キー値を保持するページ・アイテムP1_IDセッション・ステートストレージセッションごと(永続)に変更します。

新規に画像がアップロードされる場合、ページ・アイテムP1_IDには空白(null)が渡されます。ページ送信時に実行されるプロセスでは、画像を含む行をデータベースに挿入する際に、INSERT INTO ... RETURNING id INTO :P1_ID という構文を用いて主キーの値を取得し、それをP1_IDに設定します。

しかし、セッション・ステートストレージリクエストごと(メモリのみ)になっていると、プロセスの処理が完了してフォームのページが再描画される際に、P1_IDの値がセッション・ステートに保存されておらず、結果としてP1_IDは空白のままになります。このため、直前にアップロードされた画像をP1_IDの値を用いて参照できません。

通常、フォームはレポート画面から開かれ、フォームを閉じるとレポート画面に戻る構成であるため、このような問題は発生しません。

しかし、今回のアプリケーションでは、フォームを閉じた後に再び同じフォームに戻る動作となっているため、P1_IDの値がページ遷移を超えて維持される必要があります。この対応として、セッション・ステートストレージセッションごと(永続)に変更します。


ページ・アイテムP1_FILENAMEP1_OPERATIONおよびP1_TARGET_IMAGEは非表示に変更します。


今回のAPEXアプリケーションでは使用しない属性ですが、表EBAJ_DEMO_IMAGESの列OPERATIONがつねにgreyscacleになるようにします。

ページ・アイテムP1_OPERATION計算を作成します。

実行ポイントとして送信後を選択し、計算タイプ静的値静的値としてgreyscaleを設定します。これでページ送信時のP1_OPERATIONの値がgreyscaleになります。


アップロードするファイルを選択するページ・アイテムP1_SOURCE_IMAGEタイプファイルのアップロードからイメージ・アップロードに変更します。

ストレージMIMEタイプ列MIME_TYPEファイル名列FILENAMEを設定します。


リージョンEbaj Demo Image外観テンプレートを装飾のないBlank with Attributesに変更します。


テンプレートが変更されたので、ボタンが配置されていたスロットが無くなっています。ボタンについては、Bodyの直下に横並びになるように移動します。

ボタンCANCELは削除し、CREATESAVEDELETEの順に並べます。

すべてのボタンの外観ホットオフにし、テンプレート・オプションWidthStretchに設定します。


ボタンDELETEレイアウト新規行の開始オフにし、ボタンSAVEの右隣に表示させます。


グレースケール変換前の画像を表示するページ・アイテムとしてP1_SOURCEを作成します。

タイプイメージの表示ラベルSource設定基準SQL文で戻されたBLOB列を選択し、SQL文として以下を記述します。
select source_image, null, filename, mime_type
from ebaj_demo_images
where id = :P1_ID

同様にグレースケール変換後の画像を表示するページ・アイテムとしてP1_TARGETを作成します。

ラベルTargetSQL文は以下になります。変換前の画像の右隣に表示させるため、レイアウト新規行の開始オフにします。
select target_image, null, filename, mime_type
from ebaj_demo_images
where id = :P1_ID

以上でユーザー・インターフェースについては完成です。

本記事の本題であるJIMPによるグレースケール処理を実装します。

プロセスを作成し、プロセス・フォームグレースケール変換の下に配置します。

識別名前greyscaleタイプコードを実行とします。ソース言語JavaScript(MLE)を選択し、JavaScriptコードとして以下を記述します。


サーバー側の条件タイプリクエストは値に含まれるを選択し、CREATE SAVEを入力します。ボタンCREATEおよびSAVEをクリックしたときに、プロセスgreyscaleが実行されます。


ボタンDELETEをクリックするとフォームに表示されている行(P1_IDが主キー値である行)が削除されます。フォームに設定されている削除済みの行のデータをリセットするプロセスを作成します。

識別名前resetタイプセッション・ステートのクリアを選択し、設定タイプとして現在のページのすべてのアイテムのクリアを選択します。

サーバー側の条件ボタン押下時DELETEです。


以上でアプリケーションは完成です。

アプリケーションを実行すると記事の先頭のGIF動画のように動作します。

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

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