【python】万華鏡作ってみる

はじめに

pythonで万華鏡(のような動作をする)ようなコードを書きました。完成物のデモ動画が以下です。

画像の一部を切り取って、鏡に見立てて複製していきます。複製はopencvの関数である、フリップとスタックでおこなっていきました。

今回デモに使用した元画像です。

Code

作成したコードはこちらです。


import cv2
import numpy as np
import time

class Kaleidoscope():
    def __init__(self, image, rotate=0):
        self.h, self.w, _ = image.shape
        self.crop_size = (self.h//2, self.w//2)

        self.rotate = rotate
        self.image = image
        self.croped_image = image
        self.kaleidoscope_img = None

        # initialize position
        self.top = np.random.randint(0, self.h - self.crop_size[0])
        self.left = np.random.randint(0, self.w - self.crop_size[1])

    def random_crop(self):
        # set bottom, right
        bottom = self.top + self.crop_size[0]
        right = self.left + self.crop_size[1]

        # generate croped img
        self.croped_image = self.image[self.top:bottom, self.left:right, :]
        self.croped_image = cv2.resize(self.croped_image, dsize=(self.h, self.w))

    def generate_kaleidoscope(self):
        # transpose the image
        imgt = cv2.transpose(self.croped_image)

        # create diagonal bi-tonal mask
        mask = np.zeros((self.w, self.h), dtype=np.uint8)
        points = np.array([[[0,0], [self.w,0], [self.w,self.h]]])
        cv2.fillPoly(mask, points, 255)

        # composite img and imgt using mask
        compA = cv2.bitwise_and(imgt, imgt, mask=mask)
        compB = cv2.bitwise_and(self.croped_image, self.croped_image, mask=255-mask)
        comp = cv2.add(compA, compB)

        # rotate -> flip -> concat -> flip -> concat -> resize
        comp = self.set_rotate(comp)
        mirror = cv2.flip(comp, 1)
        top = np.hstack((comp, mirror))
        bottom = cv2.flip(top, 0)
        kaleidoscope_big = np.vstack((top, bottom))
        self.kaleidoscope_img = cv2.resize(kaleidoscope_big, (0,0), fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)

    def update(self):
        r = np.random.randint(0,1)
        if r == 0:
            self.top += 1
        else:
            self.left += 1

        # reset 
        if self.top > self.h-self.crop_size[0] or self.left > self.w-self.crop_size[1]:
            self.top = np.random.randint(0, self.h - self.crop_size[0])
            self.left = np.random.randint(0, self.w - self.crop_size[1])

        self.random_crop()
        self.generate_kaleidoscope()

        return self.kaleidoscope_img

    def set_rotate(self, comp):
        if self.rotate == 90:
            comp = cv2.rotate(comp,cv2.ROTATE_90_CLOCKWISE)
        elif self.rotate == 180:
            comp = cv2.rotate(comp,cv2.ROTATE_180)
        elif self.rotate == 270:
            comp = cv2.rotate(comp,cv2.ROTATE_90_COUNTERCLOCKWISE)

        return comp

def main():
    img = cv2.imread('liver.png')
    myKaleidoscope = Kaleidoscope(img)

    while True:
        kaleidoscope = myKaleidoscope.update()
        time.sleep(0.03)

        cv2.imshow('kaleidoscope', kaleidoscope)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cv2.destroyWindow('kaleidoscope')

if __name__ == "__main__":
    main()

おわりに

万華鏡は簡単なアルゴリズムの割に見栄えがとても綺麗ですよね。調べてもそれらしいコードが見つからなかったので、自作してみました。これを使ったインタラクティブ系の作品とかもできたら面白そうです。

おすすめの記事