米倉卒論_データ

GPT4にCSV形式の観測データを与え、
「2023年6/9の、AIカメラが捉えたデータです。1列目は時間、2列目はAIカメラに写った顔の数です。3列目以降は無視して良いです。0から23時まで、1時間ごとに、写っていた顔の平均個数を算出してCSV形式で出力して。縦方向に時間が来るように、1列目に時間、2列めに平均個数がくるように、例えば
0,0
1,0
2,0

などのように出力してください。」
などとプロンプトすると、結果を出力してくれた。有効データは6/17から7/11までの22日間。全体で平均すると、13-14時、18-21時に顔検出数が大きい結果となった。有効データは8~21時で良いだろうか。さらに、プログラムから当日に提示していたシーンを割り出し、紅葉、海辺、冬山の3種類に分けて描画したところ、学生が自由に活動できる18-21時の間では、冬山>紅葉>海辺の順で顔検出数が多くなった。

各日の18-21時の顔検出数の平均値を求め、提示したシーンにより差があるかを参加者間計画の1要因分散分析で検討したところ、F(2,17)=1.49, n.s.となり、有意な差は認められなかった。

using UnityEngine;
using UnityEngine.SceneManagement;
using System;

public class SceneSwitcher : MonoBehaviour
{
    public string[] sceneNames; // 表示するシーンの名前を格納する配列
    private int currentSceneIndex; // 現在のシーンのインデックス
    private int lastDay; // 最後に確認した「日」
    private int lastSecond; // 最後にログを出力した秒

    //ここから長野が書き換え231101
    private void Update() {

        if (Input.GetKeyDown(KeyCode.F12)) { SceneManager.LoadScene("S0"); }
        if (Input.GetKeyDown(KeyCode.F1)) { SceneManager.LoadScene("S1"); }
        if (Input.GetKeyDown(KeyCode.F2)) { SceneManager.LoadScene("S2"); }
        if (Input.GetKeyDown(KeyCode.F3)) { SceneManager.LoadScene("S3"); }
        if (Input.GetKeyDown(KeyCode.F4)) { SceneManager.LoadScene("S4"); }
        if (Input.GetKeyDown(KeyCode.F5)) { SceneManager.LoadScene("S5"); }
        if (Input.GetKeyDown(KeyCode.F6)) { SceneManager.LoadScene("S6"); }

        if (Input.GetKeyDown(KeyCode.Escape)) { Application.Quit(); }

    }

    /*

        private void Start()
        {
            DateTime currentTime = DateTime.Now;
            lastDay = currentTime.Day;
            lastSecond = currentTime.Second;
            currentSceneIndex = lastDay % sceneNames.Length;
            //SceneManager.LoadScene(sceneNames[currentSceneIndex]);
        }

        private void Update()
        {
            // 現在の時刻を取得
            DateTime currentTime = DateTime.Now;
            int currentHour = currentTime.Hour;
            int currentMinute = currentTime.Minute;
            int currentSecond = currentTime.Second;
            int currentDay = currentTime.Day;

            // 現在のシーン名を取得
            string currentSceneName = SceneManager.GetActiveScene().name;

            // 秒が変わったときだけ時刻をログに出力
            if (currentSecond != lastSecond)
            {
                lastSecond = currentSecond;
                string timeString = currentTime.ToString("HHmmss");
                Debug.Log(timeString);
            }

            // 20:30から7:30までの間はS0を強制表示(ただし現在のシーンがS0でない場合のみ)
            //if ((currentHour >= 20 && currentMinute >= 30) || (currentHour <= 7 && currentMinute <= 30))
            if ((currentHour > 20 || (currentHour == 20 && currentMinute >= 30)) || (currentHour < 7 || (currentHour == 7 && currentMinute < 30)))
            {
                Debug.Log("change to S0");
                if (currentSceneName != "S0")
                {
                    SceneManager.LoadScene("S0");
                }
            }
            else if (currentDay != lastDay)  // それ以外の時間は日が変わったらシーンを切り替える
            {
                lastDay = currentDay;
                currentSceneIndex = currentDay % sceneNames.Length;
                if (sceneNames[currentSceneIndex] != currentSceneName)
                {
                    SceneManager.LoadScene(sceneNames[currentSceneIndex]);
                }
            }

            if (Input.GetKeyDown(KeyCode.F12)) { SceneManager.LoadScene("S0"); }
            if (Input.GetKeyDown(KeyCode.F1)) { SceneManager.LoadScene("S1"); }
            if (Input.GetKeyDown(KeyCode.F2)) { SceneManager.LoadScene("S2"); }
            if (Input.GetKeyDown(KeyCode.F3)) { SceneManager.LoadScene("S3"); }
            if (Input.GetKeyDown(KeyCode.F4)) { SceneManager.LoadScene("S4"); }
            if (Input.GetKeyDown(KeyCode.F5)) { SceneManager.LoadScene("S5"); }
            if (Input.GetKeyDown(KeyCode.F6)) { SceneManager.LoadScene("S6"); }

            if (Input.GetKeyDown(KeyCode.Escape)) { Application.Quit(); }
        }
    */


}

23mh203 11/3の報告

 予備実験が延期になり、データ数が揃うのは11/10の見込みに
空き時間でポリヴェーガル理論で聴覚に関連する部分について調べた。

 ポリヴェーガル理論入門(著:ステファン・W・ポージェス 訳:花丘ちぐさ)では、人間は低いピッチの音が捕食動物が近づいて来たと解釈する傾向があり、反対に高いピッチの柔らかい音はお母さんが赤ちゃんをあやす時の声によく見られることから「安全である」という「合図」になりうると触れられている。
 特に女性の歌声は低周波を伴わず、抑揚がある上に歌声をコンピューターで変換するとさらに抑揚は強調され音韻も強調されるのでより「安全である」という「合図」を受け取りやすくなるとのことであった。

 この際に絡んでくるのが本内で「中耳筋」と呼ばれている部分なのだが、中耳筋という筋肉は少し探した限り見当たらなかった。
 中耳というのは鼓膜のさらに先の部分で、鼓膜の他に耳小骨と呼ばれる3つの骨が関節でつながっている部分がある。

坂下耳鼻咽喉科HPより https://sakashita-jibika.com/ear/ear_construction/

 この耳小骨の関節を動かす筋肉が本の中で出てきた「中耳筋」と思われる(医学系だと耳小骨筋と呼ばれていた)。これを動かすことで鼓膜の張り具合を変えて聞きたい音を聞き取りやすくするらしい。
 さらに、この筋肉をつかさどっている脳の部位が心臓にも関連しているらしい。このため心拍の減少やRMSSDの増加などが見られたのかもしれない。

23mh203 10/31までの報告

予備実験を1名実施。(のこり3名は水、金、翌月曜に協力してもらう予定)(また、他にも協力して良いとの声があったのでさらに4名増やすか検討したい)

 新に取得した結果を加えるとRMSSDとHRのグラフが大きく変わった。1人増えただけなのでカウンターバランスが取れずデータが乱れたのか、はたまた女性の声であればそこまで好感度に差は無いのだろうか・・・?
 もう少しポリヴェーガル理論と絡めて調べる必要がある。

 一方、皮膚温についてはグラフの上下関係が変わりつつある。枯色がやや下がり、つくよみが少し上がった。

 全体的にデータ数がまだ不足気味で何とも言えないが、音声の好感度や性別といった情報で何かしら違いはありそうだ。操作チェックの意味合いも込め、音声の性別や好感度といった情報を被検者から収集した方が良いかもしれない。

23mh203 10/24までの報告

合成音声の好感度がリラックス度合にかかわる生理指標にどこまで影響するか、とりあえず予備実験を4名協力していただき、そのデータを概観する

 10/24までに3名のデータを取得(内1名の実験中、別のところに気を取られる場面があったのでデータに不安あり、もう一名取るかも・・・)

・結果
 皮膚温のデータはあまり変化の無いように思える・・・
 RMSSDについては、kybiosの時間設定を間違えていることに気づいたので明日朝修整予定。少し見たところ好感度大と小では被験者内で一貫性があるかもしれない。
まだ順序効果の可能性も捨てきれないため、データの修正と4人目の結果から検討したい。

本日は作業がままならなくなってきたので取り急ぎここまで

R-4.3.1 for Windows

はじめに
 卒論の実験で取得したデータをもとに, js-starから3要因の分散分析(AsBCモデル)を行っていたところ, 二次の交互作用で有意な差が認められた。しかし, js-starでは二次の交互作用の下位検定が行えなかった。一時は断念したがネットで調べると, js-starのもととなった「R」は下位検定を行うことが可能であった。今回は, Rによる下位検定および使用上の注意点をまとめる。

1. R-4.3.1
・Download:リンク
・js-starとの連携方法:リンク

2. パッケージ
 デフォルトでも分析は行えるが入れておくと便利なパッケージを紹介する。パッケージのインストール方法は, 「パッケージ→インストール→Japan→(任意のパックを選択)→ok」を行い, スクリプト画面に「library(パッケージ名)」を入力して漸く使用できる。全体的な操作を通じてPython感があった。

library(Rcmdr):マウス操作で分析を行う
library(sem):Rcmdrのみだとエラー表示のため
library(psych):記述統計量の出力

3. Rの分析方法
 ここでは, js-starの算出結果を元に, Rの3要因分散分析の分析方法および結果の見方について紹介する。なお, プログラムはjs-starで発行されるため, 基礎知識は不要。

① js-starで分析
 通常通りjs-starから分析を実行する。ページを下部にスクロールさせ「Rプログラム:第1~3枠」のプログラムを用いる。それぞれ「第1枠=データセット」「第2枠=主効果, 交互作用」「第3枠=多重比較, 単純効果」を表している。

  

② Rで実行
 Rに移動し, 「library(Rcmdr)」を実行する。その後, Rスクリプト部分に, 先ほどの「Rプログラム:第1~3枠」のプログラムをコピペする。実行する箇所を聞かれるため, ctrl+Aで全選択した後, 実行を行う。すると赤文字でプログラムが書き込まれ, 統計結果は青文字で記載される。また, それに応じた図も表示された。

③ 結果の見方
 正直な話, 出力画面に何百行も表示されているが, 第3枠の出力は第2枠も補っているため, 省いていい。また, Rを使用して感動したのが, 統計結果をレポート形式でまとめてくれることだ。つまり, 第3枠の「#結果の書き方」を確認すれば, この長い出力結果と睨めっこしなくていいようだ。下記が出力結果の例である。

  • 要因Aを参加者間,要因B・Cを参加者内に配置した3要因分散分析(TypeⅢ_SS使用)を行った結果 (Table(tx1)参照),主効果Aが有意でなく (F(2,11)=1.196, p=0.338, ηp2=0.179, 1-β=0.866),主効果Bが有意傾向であった (F(1,11)=3.99, p=0.071, ηp2=0.266, 1-β=0.842)。
  • 二次の交互作用を分析するため単純交互作用検定 (α=0.20) を行った (Table(tx5)参照)。その結果,A3におけるBxCが有意であった (F(4,44)=9.366, adjusted p=0, ηp2=0.46)。以下,有意な単純交互作用についてさらに単純・単純主効果検定 (α=0.35) を行った (Table(tx7)参照)。A3におけるBxCについては,単純・単純主効果BがC5で有意であり (F(1,11)=9.317, MSE=0.301, adjusted p=0.17),B1の平均0.681がB2の平均-0.377よりも大きかった。

 一部省略したが, 主効果・一次の交互作用・二次の交互作用の下位検定が確認できる。この記述方法を推奨されているなら, 分析後コピペで済む。めちゃくちゃ楽だ。

*単純交互作用・単純単純主効果の検定の有意確率については他と同様に5%水準のため, α=0.35を基準に判断するのは疑問に残る。デフォで動かしているため, パッケージなどを入れないとダメなのかもしれない。

==========================================================================

まとめ
 Rによる分析方法を紹介したが, js-starを介せば, プログラムも一切不要。また, 統計結果もレポート形式でまとめてくれて, この面のみ見れば非常に便利であった。また, パッケージなどを使用することにより, 上記で紹介した分析ツール以外にも「PDF化, レポート作成, Googleの各種API取得」などできるようだ(サイト)。Rには驚かされてばかりだ…sasよりこっち習いたかったな。

コットの足

Futurefoxのコットの足が折れること4本目。「保証期間が過ぎたので、オプションパーツを買い求めください」とのことで、2000円かかる。今後も折れる予感がするが、その度買うのはちょっと・・・捨てるしかないのか!?こんな時こそ3Dプリントじゃない!?

PLAで作っても、いかにも折れそうなので、スライサーを設定変更して強化する。外殻(ウォール)は通常2層のところを7層に。TopとBottomの厚みも4ミリに、Infillは80%だ。印刷経過を見てたけど、この設定だと、ほぼほぼ100%詰まってるぞ笑

出来上がったパーツ(印刷時間約2時間)は、ずっしりと重みがある。穴の遊びもまぁ悪くない(純正品と変わらない)レベル。さて、機能するのか!? 写真を取り忘れたけど、ちゃんとコットに組み付けても大丈夫そうだった。「これで足折り放題ですね!(重田)」との事だが、SDGsにはこういう安心感が大事じゃないですか?

M2TP大容量Ver.

M2TP(無線皮膚温測定器)の電池を大容量化した。ケースの下側を再デザインし、850mAhのリチウムイオンバッテリーを内蔵可能とした。ちょっと厚みがあるけれど、6時間程度の動作時間が期待できる。4台作成を試みたけど、4台目は調子が悪いみたいでダメだった。設計ファイルとプログラムも登録し、とりあえず完了。

0628生理心理学特殊研究

18:00~ 
作業内容としては
1.EMG測定器2つの動作チェック
2.配線整理
3.プログラムの修正
(プログラムの修正に関しては長野先生の投稿にて
これで毎度プログラムを書き込まなくて済む・・・)
4.AI測定と同時に実施して、問題点のブラッシュアップ

1.EMG測定器2つの動作チェック
1つめの測定機(大きい方)に関しては問題なし
しかし、0626では使えていたが2つ目の測定器(小さい方)のうち、
  片方の測定ができない(画像青部分)


 左右を変えて計測しても、プラグを変えても測定不可
  配線は手を加えていないので、ボード上の電子機器が接触不良の可能性

2.配線整理
  長い配線が計測中にどこかに引っ掛かり、外れる可能性があるので短いものと交換 
 した

4.AI測定と同時に実施して、問題点のブラッシュアップ
 大きい方の計測器とiPadのAI測定を用いて同時に測定をしてみる
配置は図の通り

 表情読み取りのiPadとEMG測定器はBluetoothでPCと接続
 PCとモニターは有線接続のうえ、2画面のモードにすることで、PC側から動画再生などの操作を可能にした(PC画面はこんな感じ↓)

結果 測定自体は問題なくできることが確認できた

内省報告(兼改善点)
 iPadのカメラが下側にあるため高さを稼ぐ必要がある
 電極によって頬が引っ張られるような感じがした
 PCのポートがほとんど埋まる
 Unity の右上に経過時間が表示されるが、他のタブと被って見えなくなってしまった
 

HTTP_Fcap

WebCamに現在写っている顔の数を数えてサーバーに保存する
開発場所:CVUnity内、HTTPFcapシーン
OpenCVforUnityのLibFaceDetectionV3Exampleを改造し、検出した顔の情報をWebサーバーに投げて保存するプログラム。今回は測定結果の保存先がWebサーバーであるため、新たにスクリプト「HTTTPrecord」を作成した。データの保存先はここ。アプリケーションのダウンロードはこちらから。

データを受け取るPHPスクリプトは、以下のアドレス。
http://kodamalab.sakura.ne.jp/FaceDetectNum/record.php
データは、「20230608.csv」などのように、日付ファイル名で記録される。記録内容は、検出された顔の数、顔1の信頼性、X座標、y座標、横幅、縦幅、顔2の…といった並びである。

<?php
$data = isset($_POST['data']) ? $_POST['data'] : '';  ; // データをPOSTから取得

if (!empty($data)) {   					
    $date = date('Ymd');   				// 現在の日付をYYYYMMDD形式で取得
    $timestamp = date('Ymd_His');   
    $filename = "{$date}.csv";   		
    $file = fopen($filename, 'a');   	
    fwrite($file, $timestamp . ',' . $data . "\n");   // タイムスタンプとデータをファイルに書き込む
    fclose($file);   					
    echo "recorded";   					
} else {
    echo "No data!";   					
}
?>

EliteHRV

PolarH10をHRV解析に用いる、低コストで確実な方法はEliteHRVを用いることだ。このソフトウェアはHRVバイオフィードバックを行うためのものであり、iPhone版とAndroid版がある(これ大事)。日本での紹介事例は少ないが、海外では広く使われている。使い方は下の動画を見ればなんとなくわかるが、Polar H10とEliteHRVの組み合わせはとてもすばらしい。

動画では光計測デバイスを用いる例もあるが、指先の血管に伝わってきた拍動情報では心拍変動を正確に評定することは困難であるため、EliteHRVはH10での計測を推奨しており、計測したRR間隔データをExportする方法も提供している。具体的には測定後、ユーザーアカウント部分をクリックし、ExportDataをクリックすると、登録されたメールアドレスに最近計測したデータがメールで転送されるという仕組みだ。出力結果は標準的なRR間隔データで、kubiosにそのまま読み込むことができるので便利だ。

音声の印象評定のお願い その2

文京学院大学大学院1年の庵袋晴輝と申します。修士論文の予備調査として、音声の印象に関する調査をしています。
ご協力いただける方は下記の手順に従って回答をお願いいたします。
ひとつの音声は30~40秒程度です。

手順
1.下記のFromを表示して質問票を表示してください
 フォームへのリンクはこちら


2.音声再生ページを下記のリンクから表示してください。
 音声再生ページへのリンクはこちら


3.下記のように、印象評定ページと音声再生ページを並べた状態で評価を行ってください。再生する音声の順序は、参加者によってランダムになっているため、Formsに表示された順序で上から順番に8種類を評定してください。

音声は回答ができるなと感じたところで再生を止めていただいても構いません

ARkit vs MediaPipe

Apple ARkitのアドバンテージは
 MediaPipeのFacemeshを使って、「・・・コレ大丈夫か?」と思ったのは事実。推定されたMesh位置情報を高速でサンプルして描画すると、激しいノイズが。メッシュの推定がブレているということだ(見た目にもそんな予感はしていた)。顔の姿勢取得も苦労したが、そもそも2/15の記事で、ARkitの方は眼球の姿勢がとれていた(当然顔の姿勢もとれるだろう)。
 改めてARkitの顔認識をよく見ると、(こんなに暗いのに)ものすごくピッタリ推定できており、口の動作もほとんど完璧だ(Mediapipeは横向くと推定が怪しくなり、口も勝手に開いたりする)。google vs apple、IT界の両雄が作る似たサービス、同じくらいのクオリティがあると思ったら間違いで、おそらくこれに関してはARkitの圧勝だ。・・・自分は今、岐路に立たされている。Appleの軍門に下る事(あるいはApple製品を新たに購入するコスト)を避け、Mediapipeで実装すると、AIによる顔認識研究のクオリティは下がり、僕の評判も悪くなるだろう。「ここは買っとけ、iPad買っとけ!」・・・私のゴーストがそう囁くので、ケチケチせずiPadを2つ購入しようと思う。今、AI非接触計測の経験値を高めておけば、きっと何十倍にもなって返ってくる(ハズ)。このような実践で鍛えられた「◯◯のような用途にはこのAIが使える」といった細かな経験値が、今後数年はモノを言う気がする。

iPadのARkit対応状況はこちらのサイトにまとめられている。2019年のモデルあたりから使えるみたいだけれど一番安いのはどれかな。iPad Proは高機能だけれど2つ買うのは絶対嫌だし、ひょっとして2021年モデル(64GB)が43000円というのは、「買い」なのではないだろうか!?自腹なら中古でiPhoneという手もあるが、\24800のいつ壊れるかわからないXsを購入するのは危険に思える(実際壊れたXsが手元にひとつあるわけだし)。

Blendshapeを取得する
そして確認されるBlendshape。これはあくまでBlendshape値なので、顔の動きそのものではないが、運用上はこのほうが都合が良い。お笑いを見せて、一番動きが大きなBlendshapeを笑いの評価に使うとか。。。大頬骨筋に比べると、皺眉筋の動作検出は弱いように思える。用途の問題なのか、そもそも動く量が少ないからか。数値的にはEMGのアドバンテージは確定的にも思えるが・・・。

皺眉筋、大頬骨筋、上唇挙筋などに対応したブレンドシェイプがあるように思える。いろいろな動画刺激に対して、これで感情評価を行えば良い。ipadをどう固定するか考えないとね。

音声の印象評定のお願い

実験の大まかな内容

1.下記のFromを表示して質問票を表示してください。
フォーム

2.音声再生ページを下記のリンクから表示してください。
音声再生ページ

3.下記のように、印象評定ページと音声再生ページを並べた状態で評価を行ってください。再生する音声の順序は、参加者によってランダムになっているため、Formsに表示された順序で上から順番に10種類を評定してください。

pythonでbardを使う

ChatGPT対抗手段として、とうとうリリースされたのがGoogleの大規模言語AI「Bard(吟遊詩人?)」である。どうやってPythonから使うのかな?・・・なんかBardに教えてもらった方法で、できないんだけど・・・

PythonでBardを使う例(230516)
BardAPIて?
あ、こちらでも解説されていますね
BardAPI本家 すばらしい・・・
GPTとBardで意思決定 自己採点のアイデアが良い。互いに採点もできるの!?
GPTとBardに概念を考えてもらう プロンプトが参考になる

・・・そして完成するBradとの会話プログラム。BradAPIを使っています。APIkeyは、ブラウザのクッキーから取得するという手順。これでいいんだろうか?・・・やはり現在の情報が参照できるのはアドバンテージありますね。しかし、推論の妥当さなどはGPT3.5や4にアドバンテージがあるように思う。どのような実装にすべきか、悩みますね。Unityから呼び出す方法は、まだ解説されていないようだし。

from bardapi import Bard
import os

os.environ['_BARD_API_KEY']="hogehoge."
 
 
def main():
  
    print("チャットをはじめます。q または quit で終了します。")
    print("-"*50)
    while True:
        user = input("<あなた>\n")
        if user == "q" or user == "quit":
            print(f"トークン数は{amount_tokens}でした。")
            break
        else:
            #chat.append({"role": "user", "content": user})
            msg=Bard().get_answer(user)['content']
 
        print("<Bart>")
        print(msg)
 
 
if __name__ == "__main__":
    main()

現状・・・調べ物にはBardがいいけど、人格ドライブにはGPT3.5がいいように思える。Bardに協力してもらおう!

#サイト上のcommandをチェックしBardでanswerに問い合わせ結果を書き込むスクリプト
from bardapi import Bard
import os
import time
import requests

os.environ['_BARD_API_KEY']="ほげ"

command_url = "http://kodamalab.sakura.ne.jp/AIchat/Bard1/command.txt"
response_url = "http://kodamalab.sakura.ne.jp/AIchat/Bard1/response.php"

#ファイルサイズを得る
def get_file_size(file_url):
    response = requests.head(file_url)
    try:
        return int(response.headers['Content-Length'])
    except KeyError:
        return None


#コマンドの内容を得る
def get_command(file_url):
    response = requests.get(file_url)
    response.encoding = 'utf-8'
    return response.text.strip()

#Bardからの回答を保存する
def write_response(response_url, response_text):
    data = {'response': response_text}
    requests.post(response_url, data=data)


answer=""
last_size = get_file_size(command_url) or 0
while True:
    current_size = get_file_size(command_url) or 0
    print("." , end=' ')
    if current_size != last_size:
        command = get_command(command_url)
        print(f"<COMMAND>{command}")
        answer = Bard().get_answer(command)['content']
        print(f"<ANSWER>{answer}")
        write_response(response_url, answer)
        last_size = current_size
    time.sleep(2)  # wait for 5 seconds before checking again

c#から呼び出す方法
解説されているが、アラビア語で理解できないという。コードを見れば・・・わかるよね?

・・・このコードは、UnityではないC#用で、LinqやThreadingなどの新しい手法を用いており、いまいち何をしているのか理解できない。エラーも出ているようだし・・・。とりあえずすべて打ち込んで、GPT4に解説してもらう。

コード1(Bard_Serviceクラス)とコード2(BardResponseModelおよびResultクラス)は、Googleの大規模言語モデルBardのAPIに接続して問い合わせを行うためのクラスです。

コード3は、これらのクラスを使用して実際に問い合わせを行うためのコードです。具体的には、エジプトの面積について問い合わせています。

以下に、各コードの詳細な説明をします。

コード1(Bard_Serviceクラス)

このクラスは、Googleの大規模言語モデルBardのAPIに接続し、問い合わせを行うためのサービスクラスです。

    apikeyとendpointは、Bard APIへの接続情報を保持します。
    Bard_Service(string apikey, string endpoint)は、コンストラクタで、APIキーとエンドポイントをセットします。
    AskBard(string prompt)メソッドは、引数として渡されたプロンプト(問い合わせ)を用いてBard APIにPOSTリクエストを送信し、その応答を返します。成功した場合は応答の内容を返すべきですが、現状では成功した場合の処理が不足していて何も返していません。失敗した場合は例外をスローします。

コード2(BardResponseModelおよびResultクラス)

このクラスは、Bard APIからの応答をデシリアライズ(文字列からオブジェクトへの変換)するためのモデルクラスです。しかし、コード1の現在の状態では、このモデルクラスは使われていません。

コード3(Programクラス)

このクラスは、上記のサービスクラス(Bard_Service)を使って、Bard APIに問い合わせを行うメインのプログラムです。

    BARD_API_ENDPOINTは、Bard APIのエンドポイントを保持しています。
    Mainメソッドでは、Bard_Serviceクラスのインスタンスを作成し、そのAskBardメソッドを呼び出してBard APIに問い合わせを行い、その結果をコンソールに出力します。

ただし、現状のコードでは、問い合わせが成功した場合の処理が不足しているため、問い合わせの結果を正しく取得して出力することはできません。これを修正するには、コード1のAskBardメ

すみません、切れてしまいました。

AskBardメソッド内で、成功した場合の結果(result)を適切に返すように修正する必要があります。つまり、以下のように修正すべきです:
public async Task<string> AskBard(string prompt)
{
    var request = new HttpRequestMessage
    {
        Method = HttpMethod.Post,
        RequestUri = new Uri(endpoint),
        Content = new StringContent(prompt,Encoding.UTF8, "application/json")
    };

    var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apikey);

    using (var response = await client.SendAsync(request))
    {
        if (response.IsSuccessStatusCode)
        {
            var result = await response.Content.ReadAsStringAsync();
            return result;  // 追加:成功した場合には結果を返す
        }
        else
        {
            throw new Exception("Request failed with status code "+ response.StatusCode);
        }
    }
}

なお、Bard APIからの応答は通常JSON形式であり、コード2に示されたBardResponseModelクラスを用いてデシリアライズすることを想定しているように見えます。したがって、より完全な実装では、応答をこのクラスにデシリアライズし、その結果を返すようにすべきでしょう。それには、たとえばSystem.Text.Json.JsonSerializerを使用することができます。

最後に、ProgramクラスのMainメソッド内では、AskBardメソッドが非同期(Taskを返す)であることを考慮し、その結果を適切に取得するようにする必要があります。具体的には、.Resultまたは.GetAwaiter().GetResult()を使用して結果を取得するか、Mainメソッド自体を非同期にしてawaitを使用することが考えられます。

ふむふむ・・・やはりコード自体も未完成のようだね。UnityでCoroutineをつかう形式に書き換えてもらおうか。・・・うーむ、そもそもこのコード呼び出し用URLもNotFoundだし、正しく動作しない気がする。一旦保留しようか。
・・・BartAPI本家に、Python他言語に移植したソースが昨日公開されていた。CSもあったが、UnityはフルCSじゃないから、GPT4にUnity対応してもらったのが下コード。コレが動けば、Bart関係の処理が盤石になるな。

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;

public class Bard : MonoBehaviour
{
    private const string BARD_URL = "https://bard.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate";

    private string token = "your bard token";
    private int reqId;
    private string conversationId;
    private string responseId;
    private string choiceId;

    void Start()
    {
        this.reqId = new System.Random().Next(1000, 9999);
        this.conversationId = "";
        this.responseId = "";
        this.choiceId = "";
        StartCoroutine(GetAnswer("input text here"));
    }

    private IEnumerator GetAnswer(string inputText)
    {
        Dictionary<string, string> headers = new Dictionary<string, string>
        {
            { "Content-Type", "application/x-www-form-urlencoded;charset=UTF-8" }
        };
        headers.Add("Cookie", $"__Secure-1PSID={this.token}");
        WWWForm form = new WWWForm();
        form.AddField("bl", "boq_assistant-bard-web-server_20230419.00_p1");
        form.AddField("_reqid", reqId.ToString());
        form.AddField("rt", "c");
        // to be filled out with correct data serialization
        form.AddField("f.req", $"[null, []]");
        form.AddField("at", "SNlM0e"); // to be filled out with correct SNlM0e value

        using UnityWebRequest www = UnityWebRequest.Post(BARD_URL, form);
        foreach (KeyValuePair<string, string> entry in headers)
        {
            www.SetRequestHeader(entry.Key, entry.Value);
        }

        yield return www.SendWebRequest();

        if (www.result != UnityWebRequest.Result.Success)
        {
            Debug.Log(www.error);
        }
        else
        {
            // TODO: Process the response, similar to the GetAnswer method
            Debug.Log(www.downloadHandler.text);
        }
    }
}

FcapMeasure230513

プログラムの概要
 OpenCV顔認識を用い、2名の参加者の顔位置と向きを測定します。このプログラムは以下の条件を前提としています。

・2名ぶんの顔のみが画面に写っていること
・2名の顔は、中央の赤線を境界として、左側・右側に写っていること

3名以上の顔が写っている状態や、2名の顔が片側に映るような状況では適切に動作しません。

データの取得方法
 画面下部の1行入力部分に、測定結果のCSVファイルの保存場所が格納されています。エクスプローラーのパス部分にコピーしてお使いください。ファイルには、「20230513_204805.csv」のような、日付と時間を基準にした名前がつけられています。
 取得したファイルを開くと、下記のような構造の25列のデータが入っています。左右の顔に関して、顔である確率(0~1)、右目(x1,y1)、左目(x2,y2)、鼻(x3,y3)、右口角(x4,y4)、左口角(x5,y5)の5ポイントの座標が保存されています。顔が検出されなかった場合は、空白が記録されます。

FcapMeasure実行ファイル

Biopac・KubiosでHRV解析

BIOPACで測定したECGデータをKubiosで解析する
ここでは、Biopacを用いてECGを計測しKubiosを用いて、各種の心拍変動解析を実施する方法を説明します。各手順の詳しい方法は、動画の下の解説をご覧ください。

BIOPACでECGを測定
BiopacのMP100を用いて、1チャンネル目にECG、2チャンネル目にHR、3チャンネル目にRR間隔を表示・計測します。

データの出力と形式の確認
「File>Save as」でデータを保存すると、ECG、HR、RR間隔の各値が、200Hzのサンプル周波数で、タブ区切りで保存されていることがわかります。以下のPythonスクリプトを用いて、ECG波形からRR間隔を算出してみましょう。

#ECG2RR.py
#Transfer ECG wave data to RR interval data
import sys
import os
import numpy as np
from scipy.signal import find_peaks
import matplotlib.pyplot as plt

# コマンドライン引数からファイル名を取得
file_name = sys.argv[1]

# ファイルの読み込み
data = np.genfromtxt(file_name, delimiter='\t', usecols=[0, 1, 2])

# 1列目のデータの取り出し
ecg = data[:, 0]

plt.figure(figsize=(10, 6))  # 幅: 10インチ, 高さ: 6インチ
plt.plot(ecg)
plt.xlabel('Sample')
plt.ylabel('ECG')
plt.title('ECG Data')
plt.show(block=True)

# ピークの検出
peaks, _ = find_peaks(ecg, height=0.5)

# ピーク間隔の計算
peak_intervals = np.diff(peaks) * 0.005 * 1000  # convert to milliseconds
#print(peak_intervals)

plt.figure(figsize=(10, 6), dpi=100)  # 幅: 10インチ, 高さ: 6インチ, dpi: 100
plt.plot(np.arange(1, len(peaks)), peak_intervals)
plt.xlabel('Peak')
plt.ylabel('Interval (ms)')
plt.title('Peak Intervals')
plt.show(block=True)

# ファイル名の拡張子を除いた部分を取得
file_name_base = os.path.splitext(file_name)[0]

# ピーク間隔を保存
output_file_name = file_name_base + '_RR.csv'
np.savetxt(output_file_name, peak_intervals, delimiter=',', fmt='%.2f')
print('Peak intervals saved to', output_file_name)

このスクリプトを使って「ECGdata1.txt」というファイルを処理する際は、「python ECG2RR.py ECGdata1.txt」のようにコマンドを打ち込みます。すると、データが読み込まれ、ECG波形が表示された後、検出されたRR間隔データが表示されます。RR間隔データは、「ECGdata1_RR.csv」という名称で同一階層に保存されます。

Kubiosを用いた心拍変動解析
上記で出力されたRR間隔データをKubiosで解析してみましょう。解析を行う範囲を指定すると、画面下方に「Time-domain」、「Frequency-domain」、「Nonlinear」の解析結果が表示されます。解析範囲を切り替えながら、各評価地をチェックしていきましょう。

Pythonでつくよみちゃん

基本編
Coeiroinkは、Python経由で制御が可能だ。こちらの記事を参考にやってみる。ふむふむ、仮想環境をAnacondaで作成したら必要なライブラリをインストールしていくと。なるほど、ローカルホストに音声合成サーバーが立ち上がっているので、そこに向かってコマンドを投げると喋ってくれるというわけですね?・・・なるほど~、CPU版だと変化にそこそこ時間がかかりますね。リアルタイムで会話するためにはやはりGPU版が必要ですね。まあしかし、チャットjptにクエリを投げるPythonプログラムとこのプログラムが合体させると、音声による会話に一歩近づくわけで。

import json

import requests
from pydub import AudioSegment, playback

# パラメータ
text = "こんにちは、つくよみちゃんです。よろしくお願いします。"  # セリフ
text2="先日さ、友達とカラオケに行ったんだけど、その時の話がすごく面白かったんだよね。みんなで歌って楽しんでいたんだけど、途中で友達の一人が「誰か知らないけど、めちゃくちゃ上手い人が歌ってる!」って言い出したんだ。";
speaker_id = 0  # スピーカーID (0:つくよみちゃん)

# 音声合成のクエリの作成
response = requests.post(
    "http://localhost:50031/audio_query",
    params={
        'text': text2,
        'speaker': speaker_id,
        'core_version': '0.0.0'
    })
query = response.json()

# 音声合成のwavの生成
response = requests.post(
    'http://localhost:50031/synthesis',
    params={
        'speaker': speaker_id,
        'core_version': "0.0.0",
        'enable_interrogative_upspeak': 'true'
    },
    data=json.dumps(query))

# wavの音声を再生
playback.play(AudioSegment(response.content,
    sample_width=2, frame_rate=44100, channels=1))

話者を変える編
さて、Coeiroinkは様々な音声を登録可能だが、どのようにして声を変えるのだろうか?各話者の音声ファイルはspeaker_infoフォルダに、話者ごとにフォルダで区別されて保存されている。その中に、「metas.json」ファイルがあり、Pythonプログラム内で使用できるspeaker_idが記録されている。このIDをPythonスクリプトに記述することで、声を変えることができるというわけだね。

PythonでChatGPT編
ここを参考に進めるが・・・動かない。なんでぇ?ここのほうがわかりやすいか。やはり動かない・・・。結局APIキーを新しく発行したところ、なぜか正しく動作するようになった。前のキーが無効になってる!?なんてことはないと思うんだけど。オープンAIのAPI使用量を、研究費から支払う方法がないかなぁ?

import openai
openai.organization = ""
openai.api_key      = ""

# =============================================================
# チャットボット関数
# =============================================================

def Ask_ChatGPT(message):
    
    # 応答設定
    completion = openai.ChatCompletion.create(
                 model    = "gpt-3.5-turbo",     # モデルを選択
                 messages = [{
                            "role":"user",
                            "content":message,   # メッセージ 
                            }],
    
                 max_tokens  = 1024,             # 生成する文章の最大単語数
                 n           = 1,                # いくつの返答を生成するか
                 stop        = None,             # 指定した単語が出現した場合、文章生成を打ち切る
                 temperature = 0.5,              # 出力する単語のランダム性(0から2の範囲) 0であれば毎回返答内容固定
    )
    
    # 応答
    response = completion.choices[0].message.content
    
    # 応答内容出力
    return response

# =============================================================
# チャットボット実行
# =============================================================

# 質問内容
message = "日本人の若者の特徴を教えてください"

# ChatGPT起動
res = Ask_ChatGPT(message)


# 出力
print(res)

このプログラムで、ChatGPTと延々と会話ができる。

import openai
openai.organization = "org-ほげ"
openai.api_key      = "sk-ほげ"


def main():
    amount_tokens = 0
    chat = []

    setting = input("ChatGPTに設定を加えますか? y/n\n")
    if setting == "y" or setting == "Y":
        content = input("内容を入力してください。\n")
        chat.append({"role": "system", "content": content})

    print("チャットをはじめます。q または quit で終了します。")
    print("-"*50)
    while True:
        user = input("<あなた>\n")
        if user == "q" or user == "quit":
            print(f"トークン数は{amount_tokens}でした。")
            break
        else:
            chat.append({"role": "user", "content": user})

        print("<ChatGPT>")
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",messages=chat
        )
        msg = response["choices"][0]["message"]["content"].lstrip()
        amount_tokens += response["usage"]["total_tokens"]
        print(msg)
        chat.append({"role": "assistant", "content": msg})


if __name__ == "__main__":
    main()

とうとう会話が成立する
それでまあ、音声AIを制御するスクリプトとオープンAIのAPIを呼び出すスクリプトを合体させると、音声で会話ができるということになる。あまり長々喋られると、変換に時間もかかるので、何文字以内で喋ってほしいなど、制限を加えた方がいいかもしれないね。入力側の音声認識を切っておかないと、AI側の発話が認識されてしまうという・・・この辺も工夫が必要だね。

まあ予想していたことではあるんですが、現状で利用可能なGPT 3.5は、ふじみ野市にツインリンクもてぎがあるなど、でたらめなことを平気で行ってくるので、使いどころが難しいですね。「○○について調べて、レポートにまとめておいて」などといった使い方ができるようになれば、少しはエージェントっぽくなるんだけど。GPT 4のウェイティングリストに入らないと・・・