【知覚情報処理】 サイモン課題をpythonで作って遊ぼう！

# はじめに

&nbsp;こんにちは。自分はまだ社会にも出ていないただのエンジニア志望です。今までいろいろ作ってきてはいたのですが、人様に見せられるほどのものでないかそもそも機密で見せてはいけないものばかりで投稿できずにいました。今回はなんでもいいからとりあえず投げちゃえって思ったので投稿します。実際サイモン課題に関するプログラムを作ろうと思い立ってからこの記事を書き終えるまで合計作業時間は18時間も経っていないので出来栄えは正直悪いです(笑) 生暖かい目で読んでください！ また、記事を読んだ上で間違っている箇所やより効率の良い方法が他にある箇所は後学のためにぜひ教えていただきたいです。よろしくお願いします。

サンプルコードはあまり貼りません。

# サイモン課題とは
&nbsp;皆さんはサイモン課題って知っていますか？私は名前は知りませんでしたが、頭の体操のゲームなどで知らず知らずのうちにやっていたように思います。例えば、以下のスライドのようにモニターとボタンで実験できます。

<iframe src="https://docs.google.com/presentation/d/e/2PACX-1vTt9kjEFtxJJuhIZO606gIO6Ofjj2BilMl5L8Rw_GcDb_8haClkRN9BUD-ljImTd8aKyz9hBzpDf0X2/embed?start=false&loop=false&delayms=3000" frameborder="0" width="960" height="569" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>

色で判断しなければならないのに, 左右で勝手に反応しそうになります。ややこしいですね。


この課題を出題して回答を受け入れ、反応速度と正解率を出力するプログラムを作っちゃいます。

# 使用した環境・言語など 
* python 3.9
* MacOS Ventura 13.3.1
 
とりあえず私の今までの経験値から簡単に出来そうな以下のライブラリを使用しました[2]。
* OpenCV (numpy配列)
* PySimpleGUI

# モジュールのインポートとGUI

&nbsp;モジュールは以下のようにインポートしました。それぞれのinstall方法はcondaやらpipやらでテキトーにやります。
例: ` conda install numpy`, `pip install opencv-python`


``` Python 
import numpy as np
import cv2
import random
import PySimpleGUI as sg
import time
```

&nbsp;GUIについてはPySimpleGUIを使っていますが, 本来の目的から逸れるというのと, 元々Web上の別プラットフォームで動かすつもりだったので, かなりテキトーに作りました。
"Simon"というウィンドウに"simon test"と表示して, 画像を表示, その下に回答者が数値を指定可能な問題数(question number)を, 回答者が指定できない正答率(accuracy rate)と平均回答時間(average time)を表示しておき, その下にボタンを配置しておきました。

![スクリーンショット 2023-05-26 21.40.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2534042/f6da28ee-c65c-aa86-22ec-974b7273754f.png)

"start"を押すと数秒待ってから問題が出題されます。
![スクリーンショット 2023-05-26 21.41.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2534042/df915b0d-aee0-2d54-3f4d-aa0878507e49.png)

今回の問題は赤いマーカーなので左下のボタンを押します。 

![スクリーンショット 2023-05-26 21.41.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2534042/efe5fc72-c925-511a-dc99-929a1b8c05fc.png)

回答者がボタンを押すと, 次の問題が出題されます。今回は緑なので右下のボタンを押す必要があります。画面表示は, 正答率が100%, 平均回答時間が4176.6msとなっています。スクショを撮ったので回答時間が長くなってしまいました。

 

### 回答の流れ
1. 回答者が問題数を決めて"start"を押す
2. 問題を生成し, 表示する
3. 回答者が回答ボタンを押す。
4. 正解か判別し, 正答率と平均回答時間を更新
5. 最初に指定した問題数こなすまで2から4を繰り返す。
6. 問題数をこなすか, "stop"が押されると正答率と平均回答時間を表示する

# 問題の出題
&nbsp;要は真っ白(255,255,255)な背景に青やら赤やらのマーカーを出力させればいいです。本来、サイモン課題は2択問題として出題されますが, 今回は4択にしてみました。画面を縦横に2分割して, 左上, 左下, 右上, 右下の4択です。なので, 画面を縦横に分割する線も引く必要がありますね。
マーカーは青(255,0,0), 赤(0,0,255), 黒(0,0,0), 緑(0,255,0)と設定しています。
※色の横の()はOpenCVにおけるカラー画像の3原色の数値を示しています。<br>
&nbsp;まず, 出力画像サイズは`Nx*Ny`とします。
出力画像をimgとすると, 
``` python
img = np.full((Ny, Nx, 3), 255)
```
とすることで, 真っ白な画像を得ることができます。3次元配列(カラー画像)で, 横(Nxマス)と縦(Nyマス)と色(現在は(255,255,255))となります。これに対して, x=int(Nx/2)とy=int(Ny/2)を黒(0,0,0)にして, 分割する線を引いて, 領域をわかりやすくします(以下の図)。
![white.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2534042/cd6504fb-1398-9610-3363-3f25d3601b08.png)


&nbsp;次に, 出力する画像の正解ラベルを生成します。`random.randint(0,4)`で0~3の整数値を出力し, これを正解ラベルにします。また, マーカーの表示位置のx座標も`random.randint(0,Nx)`などとして求め(y座標も同様), その座標の色を正解ラベルに対応したものに更新します。なお, 1マスだけ色を変えてもわかりにくいので, 画像サイズによって生成された座標の周囲10*10の色を更新する, といった処理をします。 
これで問題を出題する画像はできたので, あとはこの画像を表示します。最終的には, 回答者が前の問題に答えたら次の画像を生成し, 出力するというサイクルになります。ただし, 出力の際はnp.array()の中身を以下の方法などで`np.uint8`型にしておく必要があります。 
``` python
img.astype(np.uint8)
```
型を変更した後に, [参考[2]の記事](https://qiita.com/Gyutan/items/b4781ee1baa4966699ef) のようにPySimpleGUIに合った表示形式に変換します。



###### 境界値付近について 
視覚を頼る以上, 画像の端にマーカーが出て, 色を判別できないのは良くないです。今回はオフセットを用意して, マーカーのx座標を`random.randint(10,Nx-10)`といった形式にすることで端にマーカーを生成させなくしました(y座標も同様)。


### 問題の画像生成手順 まとめ
1. 白画像(カラー)を生成し, 領域を分ける(縦横4分割)
2. 乱数で正解ラベルを生成する
  正解ラベルと出力するマーカーの色を対応させる。
3. 乱数でマーカーの座標も生成する
    その座標の周辺マスの色を変更する。
4. 出力する

出力される画像例
![marker_r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2534042/32f65449-7735-62bd-9ffc-142a409a1419.png)


# 入力の受付について 
&nbsp;今回はPySimpleGUIによるボタンによって回答を受け付けました。この処理の仕方だと, マウス操作によって回答することになるので, 位置による誤解と反応速度等を正確に測れないと思いましたができる限りコードを簡単にしたくてこうなりました。ボタンは左上を"blue", 左下を"red", 右上を"black", 右下を"green"としました。特に意味はありません。キーボード入力であれば青マーカーは'e', 赤マーカーは'x', 黒マーカーは'@', 緑マーカーは'/'というように, キーボードの上下左右4個所で指定するのがいいはずです。 

### 回答
1. 問題の画像を作る時に生成した正解ラベルと回答者の入力を比較する。
1. 比較した値があってれば, 回答者の得点を1増やす。
1. あってても間違ってても問題数のカウントを1増やして次の画像を生成する

回答時間については問題の画像を出力したタイミングの時間を`time.time()`から得て保存しておき, 回答者が答えたタイミングの時間も同じ方法で得て比較することで簡単に求められます。

# 応用と今後
&nbsp;本当はキーボード入力をしたかった！！！色々なモジュールがあるのはわかっていたのですが, adminで実行しなければならず, 面倒でした！！！！今後, キーボード入力の受付と,　Webで公開できる形式に進化させられたらいいなと考えています。
心理学とかって面白い実験がいっぱいあるので, それらもこれから実装して記事にしていきます。 
<br>
実際に記事を書いてみて, 説明するのがとても難しいと感じました... 精進します。
拙い文章でしたが読んでいただいてありがとうございました。

# 参考　
1. [PsychoPyのサイモン効果説明](http://www.s12600.net/psy/python/ppb/html_3/chapter03.html)
2. [PysimpleGUIを用いたOpenCV画像処理表示 - Qiita](https://qiita.com/Gyutan/items/b4781ee1baa4966699ef)