.. title:: 「心理学実験プログラミング: Python/PsychoPyによる実験作成・データ処理」 第3章練習問題の解答 3.2.1節 複数のディレクトリに分散したデータの集約 ===================================================== 1) 正答した試行の反応時間 --------------------------- 以下のように真偽値のリストを作成すればよい。 .. code-block:: python tf_list = (df['target']==target) & ( df['items']==items) & ( df['correct']==1) 2) 下限、上限を指定した抽出 ------------------------------- 通常、xがxmin以上xmax未満という条件は ``xmin<=x image.size[1]: diff = image.size[0]-image.size[1] if diff % 2 == 1: # 2の剰余が1であれば奇数である left = int((diff-1)/2) right = image.size[0]-(left+1) else: left = int(diff/2) right = image.size[0]-left top = 0 bottom = image.size[1] else: diff = image.size[1]-image.size[0] if diff % 2 == 1: top = int((diff-1)/2) bottom = image.size[1]-(top+1) else: top = int(diff/2) bottom = image.size[1]-top left = 0 right = image.size[0] image.crop((left, top, right, bottom)).save( os.path.join(output_dir, 'c_'+file)) 2) 画像の縦横比を考慮した処理 --------------------------------------- 以下に例を示す。 .. code-block:: python :linenos: :emphasize-lines: 12-20 #coding:utf-8 from __future__ import division from __future__ import unicode_literals from PIL import Image import numpy as np # 画像を開いて透明度チャネルを追加 image = Image.open('test.jpg').convert('RGBA') w,h = image.size # Imageオブジェクトからndarrayオブジェクトを作成 image_np = np.array(image) # 画像サイズと同じ要素数のndarrayを作成 if w>h: r = w/h x = np.arange(-2.0*r, 2.0*r, 4.0*r/w) y = np.arange(-2.0, 2.0, 4.0/h) else: r = h/w x = np.arange(-2.0, 2.0, 4.0/w) y = np.arange(-2.0*r, 2.0*r, 4.0*r/h) # 2次元ガウス関数を計算するためのメッシュを作成 x_mesh, y_mesh = np.meshgrid(x, y) # 透明度チャネルに値を書き込む image_np[:,:,3] = np.exp(-x_mesh**2 -y_mesh**2)*255 # Imageオブジェクトを作成しpngで保存 Image.fromarray(image_np).save('ouput.png') 3.4.3節 OpenCVを用いた画像処理 ======================================= 1) フォントの変更 ---------------------------- OpenCV 2.4.13のドキュメントに記載されている、putText()で使用できるfontFaceは以下のとおりである。コード3.10のFONT_HERSHEY_SIMPLEXを他のものに書き換えればよい。 + FONT_HERSHEY_SIMPLEX + FONT_HERSHEY_PLAIN + FONT_HERSHEY_DUPLEX + FONT_HERSHEY_COMPLEX + FONT_HERSHEY_TRIPLEX + FONT_HERSHEY_COMPLEX_SMALL + FONT_HERSHEY_SCRIPT_SIMPLEX + FONT_HERSHEY_SCRIPT_COMPLEX 2) 検出する顔の大きさの範囲を指定 --------------------------------------- OpenCV 2.4.13のドキュメントによると、detectMultiScaleの引数は以下のとおりである。 .. code-block:: none cv2.CascadeClassifier.detectMultiScale( image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) → objects Parametersの欄に + *minSize* – Minimum possible object size. Objects smaller than that are ignored. + *maxSize* – Maximum possible object size. Objects larger than that are ignored. とあるので、これらの引数を指定すればよい。問題はオブジェクトサイズの指定方法である。OpenCVではサイズはSizeというクラスを用いて指定し、幅と高さを指定する必要がある。このことを知っていれば要素数2のシーケンスで指定すればよいことは想像がつく。だが、筆者が確認する限りシーケンスではなんでも良いわけではなくタプルでならないようだ。本文でも述べたようにOpenCVのドキュメントの記述は非常に簡潔なので、こういった情報を読み取ることは難しい。検索を活用して実際にPythonで使用している例を探すと参考になるはずである。 以上より、この問題の解答は以下のようにdetectMultiScale()の引数を変更すればよい。サイズは各自が使用する画像に応じて変更すること。 .. code-block:: python faces = classifier.detectMultiScale(image, maxSize=(250,250), minSize=(100,100)) 3.5.1節 USBカメラの活用 =========================== 1) 左右反転映像の録画 --------------------------------- 以下に例を示す。reversedという変数を用いずに、write()とsetImage()の引数を直接imageから計算してもよい。 .. code-block:: python :linenos: :emphasize-lines: 9-10,21-23 #coding:utf-8 from __future__ import division from __future__ import unicode_literals import numpy as np import cv2 import psychopy.visual import psychopy.event writer = cv2.VideoWriter('output.m4v', cv2.cv.CV_FOURCC(b'M', b'P', b'4', b'V'), 30, (640,480)) win = psychopy.visual.Window(units='pix', fullscr=False) stim = psychopy.visual.ImageStim(win, size=(640,480), contrast=0.3) capture = cv2.VideoCapture(0) #if not capture.isOpened(): # # 開けなかったときの処理はこのようにif文とisOpened()を使う while not 'escape' in psychopy.event.getKeys(): retval, image = capture.read() # grabとretrieveを一気に if retval: reversed = image[:,::-1,:] # 動画用は左右反転のみ writer.write(reversed) # 動画として書き出し stim.setImage(reversed[::-1,:,::-1]/128-1) # PsychoPy用に変更 stim.draw() win.flip() capture.release() win.close() 2) 映像のディレイ処理 ------------------------- 以下に例を示す。「framesにappend()してからlen(frames)>=11であれば切り詰める」のではなく、「len(frames)==10であれば最も古いものを破棄してからappend()する」方が良いかも知れない。 .. code-block:: python :linenos: :emphasize-lines: 13,18-24 #coding:utf-8 from __future__ import division from __future__ import unicode_literals import numpy as np import cv2 import psychopy.visual import psychopy.event win = psychopy.visual.Window(units='pix', fullscr=False) stim = psychopy.visual.ImageStim(win, size=(640,480), contrast=0.3) capture = cv2.VideoCapture(0) frames = [] while not 'escape' in psychopy.event.getKeys(): retval, image = capture.read() # grabとretrieveを一気に if retval: frames.append(image) if len(frames) >= 11: frames = frames[-10:] if len(frames) == 10: stim.setImage(frames[0][::-1,:,::-1]/128-1) stim.draw() win.flip() capture.release() win.close() 3) カメラ映像から顔検出 ---------------------------- 以下に例を示す。 .. code-block:: python :linenos: #coding:utf-8 from __future__ import division from __future__ import unicode_literals import numpy as np import cv2 import psychopy.visual import psychopy.event win = psychopy.visual.Window(units='pix', fullscr=False) stim = psychopy.visual.ImageStim(win, size=(640,480), contrast=0.3) capture = cv2.VideoCapture(0) classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') while not 'escape' in psychopy.event.getKeys(): retval, image = capture.read() if retval: faces = classifier.detectMultiScale(image) for rect in faces: cv2.rectangle(image, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0,0,255), 2) cv2.putText(image, '{} face(s) detected'.format(len(faces)), (10, image.shape[0]-10), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255)) stim.setImage(image[::-1,:,::-1]/128-1) stim.draw() win.flip() capture.release() win.close() 4) read()とwrite()の速度 -------------------------------- 1)の解答を元にした例を示す。時刻を測るにはいろいろな方法があるが、ここではPsychoPyのClockオブジェクトを利用した。 .. code-block:: python :linenos: :emphasize-lines: 8-9,18-20,23,25,29,31,39-50 #coding:utf-8 from __future__ import division from __future__ import unicode_literals import numpy as np import cv2 import psychopy.visual import psychopy.event import psychopy.core import matplotlib.pyplot as plt writer = cv2.VideoWriter('output.m4v', cv2.cv.CV_FOURCC(b'M', b'P', b'4', b'V'), 30, (640,480)) win = psychopy.visual.Window(units='pix', fullscr=False) stim = psychopy.visual.ImageStim(win, size=(640,480), contrast=0.3) capture = cv2.VideoCapture(0) clock = psychopy.core.Clock() read_time = [] write_time = [] for frames in range(1000): clock.reset() retval, image = capture.read() read_time.append(1000*clock.getTime()) if retval: reversed = image[:,::-1,:] clock.reset() writer.write(reversed) write_time.append(1000*clock.getTime()) stim.setImage(reversed[::-1,:,::-1]/128-1) stim.draw() win.flip() capture.release() win.close() bins = np.arange(0,32,2) plt.subplot(1,2,1) plt.hist(read_time, bins=bins, color='blue') plt.title('read time') plt.ylabel('frequency') plt.xlabel('ms') plt.subplot(1,2,2) plt.hist(write_time, bins=bins, color='red') plt.title('write time') plt.ylabel('frequency') plt.xlabel('ms') plt.show() 3.5.2節 データへの非線形当てはめ =================================== 1) 残差平方和を用いた処理 ------------------------------------------------ 以下に例を示す。この例では残差平方和が最も大きい結果と最も小さい結果を最後にプロットしている。 強いて挙げるならば、argmin()、argmax()を使って最大、最小の残差平方和のインデックスを得て、このインデックスを使って対応するパラメータを取り出しているのがポイントである。 .. code-block:: python :linenos: #coding:utf-8 from __future__ import division from __future__ import unicode_literals import numpy as np import matplotlib.pyplot as plt import numpy.random as random import scipy.optimize as optimize # 最適化の数値計算を行うモジュール rt = 1000/random.normal(5.0,1.5,size=100) # ダミーデータの作成 n, bins = np.histogram(rt, bins=np.arange(100,650,50)) # ヒストグラムを得る bin_center = (bins[:-1]+bins[1:])/2 # 各階級の中央の値を計算 def pdf_LATER(t, k, mu, sigma): # 当てはめに用いる関数を定義する return k/(t**2*np.sqrt(2*np.pi)*sigma)*np.exp( -(1-mu*t)**2/(2*sigma**2*t**2)) residuals_list = [] params_list = [] for i in range(1000): k = 100 * random.random() mu = 1 + random.random() sigma = 1 + random.random() params, pcov = optimize.curve_fit( pdf_LATER, bin_center, n, p0=(k, mu, sigma)) params_list.append(params) residuals_list.append(np.sum((n - pdf_LATER(bin_center, *params))**2)) i_min = np.argmin(residuals_list) i_max = np.argmax(residuals_list) plt.bar(bins[:-1], n, width=50, color='0.75') t = np.arange(1,650) plt.plot(t, pdf_LATER(t, *params_list[i_min]), 'k-', linewidth=3) plt.plot(t, pdf_LATER(t, *params_list[i_max]), 'k:', linewidth=3) plt.text(400, 20, 'MIN k:{}\nmu:{}\nsigma:{}'.format(*params_list[i_min]) ) plt.text(400, 30, 'MAX k:{}\nmu:{}\nsigma:{}'.format(*params_list[i_max]) ) plt.show()