Stable Diffusion webuiのAPIをJavaScriptから叩く

息子氏のゲーミングPCを拝借して、「Stable Diffusion Web UI Forge」を入れてみた。
APIもPythonからもサクッと使えてすごく楽しい!…だが、クライアントサイドJavaScriptからだと、お馴染みのCORS問題で跳ねらる!

ググった結果、ちゃんと起動オプションが用意されており、下記の2つを設定すればJavaScriptからもAPIが正常に使える様になった。

–cors-allow-origins
許可するIPを(複数ある場合はカンマ区切りで)指定すればOK。
ワイルドカードも使える。
–no-gradio-queue
WebページがWebSocketの代わりにhttpリクエストを使用するようにする。

結論

APIのみ利用の場合

--no-gradio-queue --nowebui --cors-allow-origins *

WebUIとAPIの両方を使う場合

--xformers --no-gradio-queue --listen --api --cors-allow-origins *

参考サイト

KerasモデルがTenserFlow.js用に保存できない問題

Python + TenserFlowで画像分類をやろうと、チュートリアルをシコシコ進めてた。

できあがった、KerasモデルをTenserFlow.js用に保存しようと思ったが、関数「save_keras_model」で下記のエラーが出てしまう….

Exception: Error dumping weights, duplicate weight name kernel

この原因は、まさかのTensertFlow現行版(2.16.1)のバグだった!!
2.15.0にダウングレードしたらすんなり動いた…

Duplicate weight problem #8246

勉強中だから自分のコードが悪いと思ってたぞ、コンチクショー!

Node.jsサーバでCROS対応

Node.jsのオレオレサーバ上のAPIをCROS対応させたくて調べたところ、Expressを使う前提の解説ばかりが出てくる。
めげずに調べたら、全く同じ悩みを持つ方が海外にいらっしゃった。

How to allow CORS with Node.js (without using Express)

これによると、下記の3行をヘッダ情報に追加すりゃ良いって事で、早速試したら問題なく動いた。

'Access-Control-Allow-Origin': '*', /* @dev First, read about security */
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
'Access-Control-Max-Age': 2592000, // 30 days

GPT4AllをNode.jsから使ってみる

料金を気にしながら、遊び目的でChatGPTを使うのが微妙にストレスだったので、無料&ローカルでも動くGPT4Allを使ってみた。

グラボ非搭載の低スペックPCでも使える軽量チャットAI「GPT4ALL」の使い方まとめ

基本的にこの記事の通りやればサクッと動いた。2017年のインテルiMacでやり取りに数十秒かかるけど、とりあえず遊ぶ分には十分使えそう。

…ならば、Node.jsでも使いたい。

本サイトにTypeScriptから叩くやり方は書いてあったので、それを参考にやってみたら、簡単にできた!

作業環境

macOS Monterey(12.6.5)
Node.js v18.2

Node.jsの設定

  1. npmでGPT4Allをインストール
    npm install gpt4all
  2. とりあえず、簡単なサンプルスクリプトを実行。
    import { GPT4All } from 'gpt4all';
    
    let gpt4all = null;
    const _model = 'gpt4all-lora-quantized';
    
    /*GPT4ALL初期化*/
    const initGPT = async () => {
        gpt4all = new GPT4All(_model, true);
        await gpt4all.init();
        await gpt4all.open();
        console.log(">> GPT Opened");
        askGPT("Hello GPT!")
    }
    
    /*問い合せ実行*/
    const askGPT = async(xQstr) =>{
      console.log(xQstr);
      const xResponse = await gpt4all.prompt(xQstr);
      console.log(">>" + xResponse);
    }
    
    initGPT();

    初回はgpt4allの本体と学習済みモデル(gpt4all-lora-quantized)データがダウンロードされる。4G位あるので恐ろしく時間がかかけど、のんびり待つ。

  3. スクリプトがエラーになる場合、モジュールのimportが機能してない場合が多い。その場合は「package.json」に以下の一行を追加。
    "type":"module"
  4. こんな感じになれば、成功!
     % node sample.js
    >> GPT Opened
    Hello GPT!
    >>Thank you for your input, GPT!
  5. 別の学習済みモデルを利用したければ、ダウンロードして下記ディレクトリに入れ、newする時にモデル名を指定すれば使える。
    /Users/<ユーザー名>/.nomic/

これで、心置きなく、遊べる!!

ESP32-CAMのStream動画をA-FrameのVR空間に表示する

M5Camera Xの動画をA-FrameのVR空間内に表示させたいと思ったが、Motion JPEG stream形式だと、デフォルトの機能(image、video)では対応できなかった。

で、調べたところドンピシャのページを発見。仕組みはよく分からんけど、サンプル通りのコンポーネント作ったらすんなり表示できた!!

>> Use Motion JPEG stream as a source of 360 deg image

AFRAME.registerComponent('box', {
    schema: {
        width: { type: 'number', default: 1 },
        height: { type: 'number', default: 1 },
        depth: { type: 'number', default: 1 },
        color: { type: 'color', default: '#AAA' }
    },

    init: function () {
        var data = this.data;
        var el = this.el;

        this.loader = new THREE.TextureLoader();

        this.geometry = new THREE.BoxBufferGeometry(data.width, data.height, data.depth);
        this.material = new THREE.MeshPhongMaterial({
            map: this.getImage()
        });
        this.material.needsUpdate = true;
        this.mesh = new THREE.Mesh(this.geometry, this.material);
        el.setObject3D('mesh', this.mesh);
    },

    tick: function (time, timeDelta) {
        this.mesh.material.map.img = this.getImage();
        this.mesh.material.map.needsUpdate = true;
    },

    getImage: function() {
        return this.loader.load("ストリーム動画のパス");
    }

サンプルでは立方体だが、下記の部分をいじれば、他のプリミティブでもいける。

this.geometry = new THREE.BoxBufferGeometry(data.width, data.height, data.depth);

ラズパイ & Node.js でMQTTサーバを立てる

HEROKUを使えば、サクッとMQTTサーバができるらしいが、色々自前でやりたいので調べてみたら、「mosca」ってのを使えば、Node.jsでサーバ作れるし、「mqtt.js」を使えば、クライアント側もできるらしいので、早速やってみた。
Node.jsはすごいねぇ。

Brokerサーバの構築

1.moscaのインストール
npm install mosca
2.基本的なスクリプト
var mosca = require('mosca');
var server = new mosca.Server({
	port: <サーバのポート番号>,
});

/*準備完了*/
server.on('ready', function(){
	console.log('Server is ready.');
});

/*クライアントが接続された*/
server.on('clientConnected', function(client){
	console.log('broker.on.connected.', 'client:', client.id);
});

/*クライアントが切断されrた*/
server.on('clientDisconnected', function(client){
	console.log('broker.on.disconnected.', 'client:', client.id);
});

/*Subscriberからデータの配信要望が来た*/
	server.on('subscribed', function(topic, client){
	console.log('broker.on.subscribed.', 'client:', client.id, 'topic:', topic);
});

/*Subscriberからデータの配信停止要望が来た*/
	server.on('unsubscribed', function(topic, client){
	console.log('broker.on.unsubscribed.', 'client:', client.id); 
});
	
/*Publisherからデータが送信された*/
server.on('published', function(packet, client){
	if (/\/new\//.test(packet.topic)){
		return;
	}
	if (/\/disconnect\//.test(packet.topic)){
		return;
	}
	console.log('broker.on.published.', 'client:', client.id);
});

/*MQTTサーバをHTTP サーバに結びつける*/
var http = require('http');
var httpServer = http.createServer();
server.attachHttpServer(httpServer);
httpServer.listen(<サーバのポート番号>);

Node.jsでのクライアント機能の実装

1. mqtt.jsのインストール
npm install mqtt

※ブラウザから実行する場合は埋め込み利用も可

<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
2. 初期化スクリプト
const mqtt = require('mqtt');
var client = mqtt.connect({
	host: ' ',
	port: ,
	clientId: '<自分のID>'	//任意(無しの場合は自動設定)
});

/*Brokerサーバに接続*/
client.on('connect', function(){
	console.log('publisher.connected.');
});

データの送信(Publisher)

/*データ配信*/
client.publish('<任意のTOPIC>',<送信データ>);

データの受信(Subscriber)

/*購読希望リクエスト*/
client.subscribe('<購読希望TOPIC>');

/*データ受信*/
client.on("message", (topic, payload) => {
	console.log(topic);
	var message = payload.toString('utf-8');
	//var message = String.fromCharCode.apply(null, payload);
	console.log(message);
});

参考サイト


無事MQTTサーバができたので、micro:bitとHalocodeを双方向で通信させてみた。

BlueJelly と bleno でBLE通信する際の文字列の取り回しについて

ブラウザでお手軽BLEをするのに、BlueJellyがすごく便利なんだが、Peripheral化させたラズパイ(Node.js + bleno)とデータのやり取りする時に、Byte Arrayとかなんとかメンドクサイのが出て来て訳が分からんので、とりあえず簡単に使う方法を整理。

●データを受ける(Read)

Peripheral側から飛んできたデータはArray Bufferになってるので、文字列に戻す

//Bluejelly イベント
ble.onRead = function (data, uuid){
 let xVal = buffToString(data.buffer);
 console.log(xVal);
}

/*ArrayBufferから値を読んで文字列に変換*/
const buffToString = function(xBuffer) {
  let xArrayBuffer = new Uint8Array(xBuffer);
  let xRes = String.fromCharCode.apply(null,xArrayBuffer);
  return xRes;
}
●データを送る(Write)

Peripheralにデータを送るには文字列をArrayBufferにして、送る。

let xSendVal = "送信データ";

//文字列をArrayBufferに変換
let arrayBuffe = new TextEncoder().encode(xSendVal);
ble.write('UUID名',arrayBuffe);
●Node.js側でWriteされたデータを文字列に戻す
//xVal = writeされたデータ
let xVal = Buffer.from(Uint8Array.from(Buffer.from(data))).toString();

Node.jsでラズパイのカメラモジュールを使う

Raspberry Pi Zeroとカメラモジュールを入手したので、Node.jsで制御させてみた。
コマンドラン叩く系とかは面倒くさいのので敬遠してたが、「pi-camera」ってモジュール入れたら、簡単にできた。

npm install pi-camera

モジュールを入れたらサイトにあるサンプルスクリプトを参考に、JavaScriptファイルを作成して書いて実行。

const PiCamera = require('pi-camera');

const myCamera = new PiCamera({
  mode: 'photo',
  output: `${ __dirname }/test.jpg`,
  width: 640,
  height: 480,
  nopreview: true,
});
 
myCamera.snap()
  .then((result) => {
    // Your picture was captured
  })
  .catch((error) => {
     // Handle your error
  });

撮影完了までに5秒くらいの待つけど、これは仕様らしいので、諦めるしか無いっぽい。
動画にも対応してる様なので、次は動画にチャレンジ。

参考サイト
Pi-Camera(npm)
pi-camera(GitHub)

ejsでテンプレートのパスが通らない件

Node.js + express + ejsの環境で、下記みたいに書いてたが、ejsのテンプレートファイルが見つからないって、エラー(Error: Cannot find module ‘views/’)になる。

app.get("/", function(req, res) {
    res.render('./views/temp.ejs', { title: 'SAMPLE'});
});

で、どうしたもんかと2日ばかり悩んだが、express.staticで、ルートのパスを指定すれば、動いた。

app.use(express.static('./views'));

app.get("/", function(req, res) {
    res.render('temp.ejs', { title: 'SAMPLE'});
});

…ただ、これが正解かどうかは怪しいので、どなたか正解プリーズ!