werry-chanの日記.料理とエンジニアリング

料理!コーディング!研究!日常!飯!うんち!睡眠!人間の全て!

色の見える白黒錯視動画:Charles Benham

今回はベンハムの独楽と言われる錯視動画を作ります.

特定の模様を持つ白黒の画像を回転させると,実際には存在しない色が見えると言う錯視現象をpythonで作りましょう.


軽くどうでもいい世間話を少し.
2020年は長雨のせいか野菜が高いですね.
お盆でナスビとキュウリを串刺しにして走り回らせたいのですが,高くて中々手が出せません.
安い期間に買い込んだキャベツをひたすら食ってます.


閑話休題

ベンハムの独楽(コマ)は,このような動画になります.
www.youtube.com

そういえば動画保存倉庫用のyoutube channel作りました.

これから何か作業しながら動画をアップロードしたりすることもあるかと思います.


それでは,このベンハムの独楽を作成するコードを載せましょう.

このコードは加速度的に回転するベンハムの独楽です.

import numpy as np
import math as mt
import cv2

#br is Black R, mw is Mesh Width, st is Start Theta, et is End Theta
def drawArc(img, r, R, sth, eth):
    h, w = img.shape[0], img.shape[1]
    for i in range(w):
        for j in range(h):
            x = i-w/2
            y = h/2-j
            rr = x*x+y*y
            rr = mt.sqrt(rr)
            if (y < 0 and rr < 400):
                img[i][j] = 0
            if (x == 0 and y == 0):
                continue
            if (x == 0 and y > 0):
                th = 90
            elif (x == 0 and y < 0):
                th = 270
            else:
                th = mt.atan(y/x)
                th = mt.degrees(th)
                if (x > 0 and y < 0):
                    th = 360+th
                if(x < 0 and y > 0):
                    th = 90+(90+th)
                if (x < 0 and y < 0):
                    th = 180+th
            if (sth < eth):
                if (r <= rr and rr <= R and sth <= th and th <= eth):
                    img[i][j] = 0
            if (eth < sth):
                if (r <= rr and rr <= R and ((sth <= th and th <= 360) or (0 <= th and th <= eth))):
                    img[i][j] = 0

    return(img)


def imgRotation(img,angle):
    h,w = img.shape[0], img.shape[1]
    center = (w/2,h/2)
    scale = 1.0
    trans = cv2.getRotationMatrix2D(center, angle*(-1), scale)
    img = cv2.warpAffine(img, trans, (w, h))
    img = np.array(img,np.uint8)
    return(img)


w = 1000 #画像の横幅
h = 1000 #画像の縦幅

blank = np.zeros((h, w, 1), np.uint8) #黒の画像を作成
img = cv2.circle(blank, (h//2,w//2), 400, 255, -1) #画像の中心に白い円を描画
img = np.array(img, np.uint8)
mw = 2 #描画する黒い線の太さ
for i in range(400//mw):
    sth = (i*10) % 240 #黒い線の描画開始の角度
    eth = sth+60 #黒い線の描画終了の角度
    if (sth >= 120):
        eth = 180 - (eth % 180)
        sth = eth - 60
    if i%2 == 1: #毎回黒線を描画すると線同士の隙間ができない.線の隙間がCharles Benhamで重要. 
        continue
    else:
        img = drawArc(img, 400-i*mw, 400-i*mw+mw, sth=sth, eth=eth) #黒い線を描画

alsec = 10
fp = 100.0
hz = 5
frame = int(alsec*fp)
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
video = cv2.VideoWriter('test.mov', fourcc, fp, (w, h))
sDv = 0 #開始時の回転速度
eDv = hz*360/fp #終了時の回転速度
Da = (eDv-sDv)/frame #回転加速度

for i in range(frame):
    Dx = Da*i*i #変位=加速度*(時間)^2
    oimg = imgRotation(img,Dx)
    print("now process is ",i,"/",frame)
    cv2.imwrite("gray.png",oimg)
    oimg = cv2.imread("gray.png")
    video.write(oimg)

video.release()

このコードで注意したい点として,初期画像を作成した後に,初期画像に変更は加えないで,回転後の画像はoimgという別の変数に格納している点です.

一枚の画像に対して,cv2.getRotationMatrix2Dを何度も行うと,初めの数回は画像の劣化は乏しいのですが,回数を重ねるごとに非常に劣化していきます.

そのため,初期画像と回転後の画像は別の変数として保存しました.


この他にも色々なパターンのベンハムの独楽が存在します.

www.youtube.com

www.youtube.com

www.youtube.com

それぞれの模様で色が変わるかどうかを調べてみようと思って,趣味で作ってみました.