
最近、機械学習でsciket-learnを色々と使っていたりで、なんとなく動画をPCA次元削減して遊んでみた。元々はPCAを行ったあとにk-meansでカテゴライズをしたりする時に使ったりする訳だけど、画像を次元削減して見てみると面白いフィルターとして使えそうな。
動画は元々はMaker Faire Bay Areaで撮った1920×1080のmp4ファイルをJPEGで取り出して、ちょっと大きいから半分にサイズを減らした後、10(右下), 60(左下), 120(右上), 240(左上)次元削減している。次元が少ない方が面白い感じに。動画から切り出した写真で10・240次元削減した物はこんな感じになる。
ソースコードはこんな感じ。とっても簡単。PCAのモデルは処理が終わったら保存しておいて、別途回せるように保存している。
import cv2
from PIL import Image
import numpy as np
import pickle
from sklearn.decomposition import PCA
# 動画の読み込み
cap = cv2.VideoCapture("movie.mp4")
dataset = []
while(cap.isOpened()):
ret, img = cap.read()
if ret == True:
# CV2ImageをPILImageに変換
img = img[:, :, ::-1].copy()
img = Image.fromarray(img).resize((int(960), int(540)), Image.BICUBIC)
# (540, 960, 3)配列にして1次元にする
img = np.asarray(img)
img = img.reshape(1, img.shape[0] * img.shape[1] * img.shape[2])
dataset.append(img[0])
else:
break
# PCA実行
pca = PCA(n_components=10)
pca.fit(np.array(dataset).astype(np.float64))
# モデルを保存しとく(後で使うとき用)
with open('pca.pickle', mode='wb') as fp:
pickle.dump(pca, fp)
# 動画を作成
fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
video = cv2.VideoWriter('result.mp4', fourcc, 30.0, (960, 540))
for img in dataset:
# PCAして動画に突っ込む
transformed = pca.transform([img])
pilImg = Image.fromarray(255 - (transformed.dot(pca.components_).reshape([540, 960, 3])).astype(np.uint8))
cv2Img = np.asarray(pilImg)
video.write(cv2Img)
video.release()
画像を色々と集めてきてPCAをかけたモデルを保存して、思わずJamiroquaiのVirtual Insanityの動画をmp4で取得してきてPCAをかけた。なんとなく最初に浮かんだのがJamiroquaiだったからで、特に深い意味は無いけど。
面白いフィルターとして使えそうな気がちょっぴりしていたり。RGB成分をそれぞれ分離して、それぞれの成分毎に圧縮して掛け合わせてみても面白いかも。