16
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenCVAdvent Calendar 2024

Day 17

PyScriptでOpenCVを使ってWebで画像処理AI

Last updated at Posted at 2024-12-16

PyScriptとは

PyScriptとは、Webブラウザ上でPythonコードを実行するためのフレームワークです。
Anaconda社が中心となって開発を進めており、Pythonをフロントエンドの開発に統合することを目的としているようです。

Hello Worldのサンプルは以下です。
PyScript出始めの時に触った方からしたら、ちょっと違和感あるかもしれません(ちょいちょい開発は行われていて、たまに破壊的な変更もあったりなかったり。。。)
※破壊的変更が怖いので、個人的にはjsとcss読み込む際は、latest指定ではなくreleases/2024.11.1のようにバージョン固定で読み込むことをおすすめします

<!DOCTYPE html>
<html>
    <head>
        <title>Hello World</title>
        <link rel="stylesheet" href="https://pyscript.net/releases/2024.11.1/core.css">
        <script type="module" src="https://pyscript.net/releases/2024.11.1/core.js"></script>
    </head>
    <body>
        <script type="py">
            from pyscript import display
            from datetime import datetime
            now = datetime.now()
            display("Hello World: " + now.strftime("%Y/%m/%d %H:%M:%S"))
        </script>
    </body>

GitHub Pagesにも公開しているので、動作している様子を確認いただけます↓

JavaScriptの代わりに、Pythonで直接DOM操作やWeb APIの操作が可能だったりするので、気になる方は公式ドキュメントを眺めてみると良いかもしれません。
公式ドキュメント:https://docs.pyscript.net

また、一部ではありますが、opencv-python、numpy、pandasなどのPythonのパッケージも使用することができます。
PyScriptは内部的にはPyodide(CPythonをWebAssembly/Emscriptenに移植したもの)を使用していて、Pyodideがサポートしているパッケージを使用することができます。
Pyodideサポートパッケージ:https://pyodide.org/en/stable/usage/packages-in-pyodide.html

ちなみにPyodideで上記のサンプルと同じようなことをしようとすると、以下のようなコードになります。
当たり前と言えば当たり前ですが、PyScriptのほうが簡易で使いやすいです。

<!doctype html>
<html>
    <head>
        <title>Hello World</title>
        <script src="https://cdn.jsdelivr.net/pyodide/v0.26.4/full/pyodide.js"></script>
    </head>
    <body>
        <div id="text"></div>

        <script type="text/javascript">
            async function main() {
                let pyodide = await loadPyodide();
                const code = `
                    from js import document 
                    from datetime import datetime
                    now = datetime.now()
                    document.getElementById('text').innerHTML = "Hello World: " + now.strftime("%Y/%m/%d %H:%M:%S")
                `;
                pyodide.runPython(code);
            };

            main();
        </script>
    </body>
</html>

こちらも、GitHub Pagesに公開しています↓

さあ、画像処理AIやろうぜと思ったけど、、、

さあ、画像処理AIやろうぜと思って、Pyodideサポートパッケージ を眺める👀

opencv-python、numpy、pandas、scikit-learnにscikit-imageはあるけど、、、
さすがに、TensorFlow、PyTorch、onnxruntimeは無いんですよねー。。。
いや、こいつらのビルドの面倒くささ考えると、opencv-pythonあるだけでも、ありがたいのですが、、、🙄

そもそも、パッケージ名とバージョンだけが羅列してあって、何のパッケージか分かりにくいんだよなー、、、
元リンクくらい貼ってくれよ。と思いますが。。。😇
image.png

TensorFlow、PyTorch、onnxruntime が無いので、軽く諦めかけてかけていましたが、、、
我々には、OpenCV の dnnモジュールと言う強い味方がいます🦔

と言うわけで、YOLOX(Tiny)動かそう

01.gif

上記のデモは、GitHub Pagesに公開しています↓

ソースコードに興味ある方は、以下の中身を眺めてみてください👀
推論はOpenCVのdnnモジュールを利用しています。

せっかくなので、PINTOさんのYOLOv9-Wholebody25も動かしてみよう

02.gif

上記のデモは、GitHub Pagesに公開しています↓

ソースコードに興味ある方は、以下の中身を眺めてみてください👀
こちらも、推論はOpenCVのdnnモジュールを利用しています。
もともとPINTO_model_zooではdnnモジュールは利用していないので、NMSのポストプロセスが統合されていないONNXを使う処理をザーッと書きました(バグってたらコソっと教えてください。。。)

課題

上のデモを見て「YOLOX-Tinyを使ってるわりに何か遅くねえ?」と感じた方もいると思いますが、、、
その感覚は正しいですが、推論自体はそんなに遅くないです。
ただ、キャンバスへの反映が500ms前後かかっており、そこで遅くなっています。

解決できなくて放置していたのですが、、、
いったんアドベントカレンダーに公開します(誰か知見持っているかもですし)

main.pyを眺めていただくと、以下のような処理があります。
コメントアウトされていない処理が、上記のデモで動作していた処理です。
コメントアウトされている方の処理だとキャンバスへの反映は速いのですが、じゃんじゃんメモリリークしていきます。
ちょっと調べていたのですが、メモリリークが解決できず妥協した感じです。
何かご存じの方がいればコメントいただけると助かります。

main.py
        #### ToDo:メモリリーク箇所
        # # メモリリークするけど早い処理
        # # NumPy 配列のバッファを使用して Uint8ClampedArray を作成
        # buffer = image.flatten().tobytes()
        # bytes_proxy = create_proxy(buffer)
        # try:
        #     # 新しい ImageData オブジェクトを作成
        #     bytes_buffer = bytes_proxy.getBuffer("u8clamped").data
        #     new_image_data = js.ImageData.new(bytes_buffer, canvas.width, canvas.height)
        #     context.putImageData(new_image_data, 0, 0)
        # finally:
        #     # キャンバスに画像データを描画
        #     bytes_proxy.destroy()
        #     del bytes_proxy, bytes_buffer, new_image_data

        # メモリリークしないけど遅い処理
        height, width, _ = image.shape
        buffer = image.flatten().tobytes()
        js_image = ImageData.new(Uint8ClampedArray.new(buffer), width, height)
        context.putImageData(js_image, 0, 0)
        ####

以上。

16
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?