OpenCVとPyGame
画像刺激提示のために
画像を連続して表示する場合、画像の処理(計算)時間がディスプレイのリフレッシュレートの時間間隔より長いときは、スムーズな表示ができない。この場合、画像を予め作成しておき、画像の画面への提示はそれらの作成しておいた画像を設定して提示すると、リフレッシュレートに釣り合った表示ができる。画像処理ソフトとしてOpenCVはよく知られており、画像をダイナミックに表示するソフトとしてPyGameがよく知られている。OpenCVで画像を作成して、PyGameで画像を表示するというスクリプトを作成してみた(リスト1)。リスト1では、作成した画像をリスト型でまとめているが、心理学実験で用いる画像の場合は辞書型を用いる方が分かり易いスクリプトになると思われる。
なお、PyGameを利用したプログラミングにおいて、作成中のプログラムを実行したときに、PyGameによるWindowsが表示された状態でプログラムの実行を止める方法を説明したウェブサイトも用意した。
例として用いた画像は図1に示す色のパターンであり、これを回転したものを10個用意した。
図1
提示パターンは、次のスクリプトでn_patterns = 10個作成して、pygameのスクリーンとしてリストList_screensに格納している。
pygame.init()
n_patterns = 10
List_screens = []
for pttrn in range(n_patterns):
sft =
pttrn*(2*pi)/n_patterns;
for i in range(500):
for
j in range(500):
theta = arg_v(i -250, j - 250)
theta -= sft
theta = theta if theta > 0 else theta + 2*pi
r, g, b = rgb_v(theta)
img[i][j][0] = b
img[i][j][1] = g
img[i][j][2] = r
print('Step', pttrn,
'ended.')
cv2.imwrite('temp.png', img)
List_screens.append(pygame.image.load('temp.png'))
OpenCVで作成する画像は、OpenCV用の配列imgを用意して、画像を描いている。
img = np.full((500,500,3), 255,
dtype=np.uint8)
"""
img[a][b][c]: a = 横軸、右方向に正
b = 縦軸、下方向に正
左上角が(0,0)
c = BGR、 pygameはRGBである。
関数cv2.cvtColor(img,
cv2.COLOR_BGR2RGB)
で、RGBに変換できる。
"""
画像は、次のコードのsft = pttrn*(2*pi)/n_patterns;で表される角度だけ回転されたものが描かれる。
for pttrn in range(n_patterns):
sft =
pttrn*(2*pi)/n_patterns;
for i in range(500):
for
j in range(500):
theta = arg_v(i -250, j - 250)
theta -= sft
theta = theta if theta > 0 else theta + 2*pi
r, g, b = rgb_v(theta)
画面の更新flipは、次のコードにより行われ、100ミリ秒間隔である。
while t1 < t0 + (1.0e9)/
10:
t1 =
time.perf_counter_ns()
t0 = t1
pygame.display.flip()
画像のリストList_screensが用意されると、画像が順次提示され、図1のパターンが回転しているように見える(図2)。
図2
画面の右上角の閉じるボタンをクリックすると、プログラムは実行終了となる。
リスト1 スクリプト例(ファイルは、圧縮ファイルopencvpygamefile.zipとした)
from math import pi
import cv2
import numpy as np
import matplotlib.pyplot as plt
import pygame
import time
def arg_v(x, y):
"""
点(x, y)の極座標における角度座標
"""
r = (x**2 + y**2)**0.5
if r <= 0.0:
return 0.0
else:
t =
np.arccos(x/r)
if y
>= 0.0:
return t
else:
return 2*pi - t
def rgb_v(t):
"""
角度座標tに対する(r,g,b)を設定
"""
if t < (pi/3):
r =
255
g =
int(255 * t / (pi/3))
b= 0
elif t < (2*pi/3):
r =
int(255 * ((2*pi/3) - t) / (pi/3))
g =
255
b =
0
elif t < pi:
r =
0
g =
255
b =
int(255 * (t - (2*pi/3)) / (pi/3))
elif t < (4*pi/3):
r =
0
g =
int(255 * ((4*pi/3) - t) / (pi/3))
b =
255
elif t < (5*pi/3):
r =
int(255 * (t - (4*pi/3)) / (pi/3))
g =
0
b =
255
else:
r =
255
g =
0
b =
int(255 * (2*pi - t) / (pi/3))
return r, g, b
img = np.full((500,500,3), 255,
dtype=np.uint8)
"""
img[a][b][c]: a
= 横軸、右方向に正
b = 縦軸、下方向に正
左上角が(0,0)
c = BGR、 pygameはRGBである。
関数cv2.cvtColor(img,
cv2.COLOR_BGR2RGB)
で、RGBに変換できる。
"""
pygame.init()
n_patterns = 10
List_screens = []
for pttrn in range(n_patterns):
sft =
pttrn*(2*pi)/n_patterns;
for i in range(500):
for
j in range(500):
theta = arg_v(i -250, j - 250)
theta -= sft
theta = theta if theta > 0 else theta + 2*pi
r, g, b = rgb_v(theta)
img[i][j][0] = b
img[i][j][1] = g
img[i][j][2] = r
print('Step', pttrn,
'ended.')
cv2.imwrite('temp.png', img)
List_screens.append(pygame.image.load('temp.png'))
intvl = 1.0e9 / 60
ck_step = 0
t0 = time.perf_counter_ns()
screen = pygame.display.set_mode((700, 700))
screen.fill((255,255,255))
pygame.display.set_caption('Color
Pattern')
screen.blit(List_screens[0], (100,
100))
pygame.display.flip()
while True:
screen.blit(List_screens[ck_step], (100, 100))
ck_step += 1
if ck_step >= n_patterns:
ck_step = 0
t1 = t0
while t1 < t0 + (1.0e9)/
10:
t1 =
time.perf_counter_ns()
t0 = t1
pygame.display.flip()
ck = False
for e in pygame.event.get():
if
e.type == pygame.QUIT:
# Windowのcloseボタンがクリックされたか
ck = True
if ck:
break
pygame.quit()