0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Markdown AIのサーバーAI機能を使ってWebサイトを作ってみよう by MarkdownAIAdvent Calendar 2024

Day 22

Markdown AIと動体検知を組み合わせたかった!呼び込みAI制作のログ

Last updated at Posted at 2024-12-31

はじめに

ご覧いただきありがとうございます!

Qiita Advent Calendar 2024「Markdown AIのサーバーAI機能を使ってWebサイトを作ってみよう」22日目です!

この記事を書いている時点で既にクリスマスは通り過ぎてしまっていますが、枠が空いていたので1本記事を書いてみたいと思います

今回は、Markdown AIのサーバーAI機能を使い、動体検知で通行人を認識、そしてナレッジに記載しているお店の紹介をしてくれるWebアプリを作ってみたので紹介します

記事投稿前に追記
Webアプリを作っている途中まで、きちんと動作をしていたのですが、Markdown AIの仕様が変わった?のか、数日前からJavaScriptが上手く動かなくなってしまいました…

CDNで読み込んでいるOpenCV.jsはダウンロード出来ているのでどこに原因があるのかサッパリです…

以下Webアプリ作成途中に書いた記事のため、若干内容の辻褄が合わない箇所があるかもしれません

こんなことやろうとしてたんだな、ということが読者の皆さんに伝わりましたら嬉しいです

どんなWebアプリを作ろうとした?

JavaScriptでOpenCVを動かし、動体検知をさせ、動くものを検出したらMarkdown AIのサーバーAIが喋ってくれるWebアプリです

Markdown AIはMarkdownだけでなく、HTML要素やJavaScriptも組み合わせることができるので、自由な発想を形にすることができます

開発までの構想

Shikuno (@shikuno_dev) さんが先日投稿されていた記事を見て、
Markdown AIにカメラを組み合わせるというアイデアを頂きました

この記事を読んだ後に、Markdown AIがどんなところでより活躍できそうかお風呂で考えていたら、今回のWebアプリの着想を得た次第です

せっかくAIが使えるなら、通行人の性別や年齢に合わせて話題を変えられたら良いなぁ、と思いましたが、JavaScriptで性別年齢検出をする際によく用いられているface-api.jsとMarkdown AIの相性が悪そう(検出用のモデルファイルを配置しないといけない)だったので今回は見送りました

つくりかた

動かなくなってしまったので割愛します

作成したコード

一応ギリギリまで動作していたコードを貼り付けておきます

index.html
<video autoplay playsinline muted></video>
<div id="kanyu">MOVE!</div>
<script src="https://docs.opencv.org/3.4.0/opencv.js"></script>
<script>
    class MotionDetection {
        constructor({ video, onMove, onStop, fps = 8 }) {
            this.video = video;
            this.onMove = onMove;
            this.onStop = onStop;
            this.fps = fps;
            this.running = false;
            this.lastFrame = null;
            this.timer = null; this.cap = null;
            this.currentFrame = null;
            this.previousFrame = null;
            this.diffFrame = null;
            this.grayDiff = null;
        } start() {
            if (this.running) return;
            this.running = true; const width = this.video.videoWidth;
            const height = this.video.videoHeight; this.currentFrame = new cv.Mat(height, width, cv.CV_8UC4);
            this.previousFrame = new cv.Mat(height, width, cv.CV_8UC4);
            this.diffFrame = new cv.Mat(height, width, cv.CV_8UC4);
            this.grayDiff = new cv.Mat(height, width, cv.CV_8UC1); this.cap = new cv.VideoCapture(this.video);
            this.loop();
        } stop() {
            this.running = false;
            clearTimeout(this.timer);
            this.cleanUp();
        } loop() {
            if (!this.running) return; const delay = 1000 / this.fps;
            this.cap.read(this.currentFrame); if (!this.previousFrame.empty()) {
                cv.absdiff(this.previousFrame, this.currentFrame, this.diffFrame);
                cv.cvtColor(this.diffFrame, this.grayDiff, cv.COLOR_RGBA2GRAY);
                const thresh = new cv.Mat();
                cv.threshold(this.grayDiff, thresh, 25, 255, cv.THRESH_BINARY); const motionDetected = cv.countNonZero(thresh) > 1000; if (motionDetected) {
                    this.onMove();
                } else {
                    this.onStop();
                }
                thresh.delete();
            } this.currentFrame.copyTo(this.previousFrame);
            this.timer = setTimeout(() => this.loop(), delay);
        } cleanUp() {
            this.previousFrame?.delete();
            this.currentFrame?.delete();
            this.diffFrame?.delete();
            this.grayDiff?.delete();
        }
    } function loadOpenCv() {
        cv['onRuntimeInitialized'] = () => {
            const medias = {
                audio: false,
                video: {
                    facingMode: 'user'
                }
            }; navigator.mediaDevices.getUserMedia(medias)
                .then(successCallback)
                .catch(errorCallback);
        };
    } async function successCallback(stream) {
        const video = document.querySelector('video');
        const kanyu = document.getElementById('kanyu');
        const fps = 8; video.onloadedmetadata = () => {
            video.width = video.videoWidth;
            video.height = video.videoHeight; const motionDetection = new MotionDetection({
                video,
                onMove: async () => {
                    kanyu.style.display = 'block';
                    const serverAi = new ServerAI();
                    const message = "人が通ります";
                    try {
                        const answer = await serverAi.getAnswerText('qV3jL7aqpBLucJDvmLchKU', '', message);
                        if (answer) {
                            const speech = new SpeechSynthesisUtterance(answer);
                            window.speechSynthesis.speak(speech);
                        }
                    } catch (error) {
                        console.error('Error fetching answer:', error);
                    }
                },
                onStop: () => {
                    kanyu.style.display = 'none';
                },
                fps
            }); motionDetection.start();
        }; video.srcObject = stream;
    } function errorCallback(err) {
        alert('Error accessing media devices: ${ err.message }');
    } loadOpenCv();
</script>

作成したプロンプト

あなたはKnowledgeに記載されている店の従業員です。
今、店の前を通りかかっている人に対して宣伝を行います。

1文で魅力が伝わるよう、声に出す言葉を出力してください。
全ての情報を入れ込まなくてもかまいません。

Knowledge
# 美味しいポップコーン店
このお店では、美味しいポップコーンを販売しています
芳醇な香りを楽しめる高級な材料を使用しているので絶品です

# メニュー
## 塩キャラメル
1カップ 200円
1袋 400円

## 北海道バター
1カップ 200円
1袋 300円

## 4種のチーズ
1カップ 300円
1袋 500円

# 営業時間
年中無休
午前8時から午後8時

本当はMarkdown AIのKnowledge機能を使用したかったのですが、どうしてもエラーが出てしまい利用できなかったので、プロンプトに記載しました

注意点と対策

Markdown AIとCDN

このWebアプリではCDNでOpenCV.jsを読み込ませていますが、Markdown AIでは、ページ公開前のプレビュー画面と、実際に公開されたページで、CDNの扱いが変化します

実際に公開されたページではCDNを利用した外部ライブラリの読み込みができますが、プレビュー画面では読み込むことができません

プレビュー画面で動きを確認しながらページを作成したい場合は、CDNの中身を直接貼り付けてあげる必要があります

記事投稿前追記
仕様が変わった可能性があるため、上記記述については正確性が担保できません

CDNが使えないときに試してみると良いかもしれません

Markdown AIでよく見るエラー

image.png

Markdown AIでJavaScriptを動かそうとするとよく見るエラーです
ブラウザのデベロッパーツールを使うと確認できます

JavaScriptの記述をコードブロックとして処理してしまうことに起因するようです

image.png
↑動かない例

非常にコードが読みにくくなりますが、インデントと1行以上の空行を全て削除してあげると動くようになります

今回作ろうとしたWebアプリを改良するなら…

Markdown AIのサーバーAI機能は、実行してから返答が返ってくるまで数秒程度の時間がかかります

つまり通行人が通り過ぎた後に喋り出してしまうんですね、はい…
実際に使用するためには、第一声は定型文にする等の対策が必要だと思います

音声認識を導入し、簡易的な応対機能をつけても面白いでしょう

おわりに

Qiitaのアドベントカレンダーって大晦日まで投稿できるって知ってました?

私、恥ずかしながら知らなかったので、25日の23時58分に慌てて中途半端な記事を投稿してしまいました…
期待してリンクを踏んでもらった方々、大変申し訳ないです
心よりお詫び申し上げます

本当は、今回制作しようとしたWebアプリを紹介して、記事を締めたいと思っていたのですが、成果物が無くなってしまったので、記事中でも少し触れたMarkdown AIの仕様変更について考察したいと思います

曖昧な記憶とかすかな履歴が頼りなのですが、公開したページのレンダリングが、記事作成画面で表示されるプレビュー画面のレンダリングと同様になるように変更されている気がしています

以前はMarkdown AI内で記述した内容はbodyタグ直下あたりで読み込まれていたはずなのですが、今はプレビュー画面での表示と同じ、class名が"text-preview"のdiv要素内で読み込まれています

このあたりの変更がJavaScriptの動作に影響を与えているんじゃないかなぁ…?と思っていますが、お手上げ状態です

いつかまた、今回のWebアプリをMarkdown AIで動かしたいです

最後までお読みいただきありがとうございました

参考文献

OpenCV.jsを使った動体検知の実装

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?