ソースは
https://github.com/osoken/tiltshift_effector
.mov
などの動画ファイルに、偽ティルトシフトエフェクトを掛けるPythonスクリプト。
Numpy
, Scipy
, OpenCV
, Pillow
を使用した。Python 2.7 向け。
原理は単純で、オリジナルの画像と、ぼけた画像をマスクしながら合成するというだけのもの。
オリジナル
※わかりやすいように、かなり強めに掛けています。
マスクを生成する関数。
def gen_mask(sz, t, tc, c, bc):
bounds = (0,limit_value(int(sz[1]*t),0,sz[1]),limit_value(int(sz[1]*(t+tc)),0,sz[1]),limit_value(int(sz[1]*(t+tc+c)),0,sz[1]),limit_value(int(sz[1]*(t+tc+c+bc)),0,sz[1]),sz[1]);
mask = np.array(Image.new('L', sz));
mask[bounds[0]:bounds[1],:] = 255;
for i in range(bounds[1],bounds[2]):
mask[i,:] = 255 - int(255.0/float( bounds[2] - bounds[1] ) * float(i - bounds[1]));
mask[bounds[3]:bounds[2],:] = 0;
for i in range(bounds[3],bounds[4]):
mask[i,:] = int(255.0/float( bounds[4] - bounds[3] ) * float(i - bounds[3]));
mask[bounds[4]:bounds[5],:] = 255;
mask = Image.fromarray(mask);
return mask;
sz
は(width,height)
のタプル。t
,tc
,c
,bc
は0.0
から1.0
の数値で、t
は上部の白い領域、c
は真ん中の黒い領域、tc
はt
とc
の間、bc
はc
と下の白い領域の間のグラデーションの幅を、全体を1.0
とした割合で表現します。
ぼけた画像を生成するのは、scipy
に入っているgaussian_filter
をそのまま使いました。
def gen_blurred_image(image, blur_factor):
if blur_factor == 0:
return image;
im = np.array(image);
return Image.fromarray(ndimage.gaussian_filter(im, sigma=[blur_factor, blur_factor, 0]));
変数image
がPIL
のImage
型で、フィルタ掛けるためだけにnumpy.array
型に変換してすぐに戻しているのが無駄っぽいですが、他の操作との兼ね合いでこうなっています…。blur_factor
はぼかしの強さで、浮動小数で与えます。
マスクと、ぼけた画像を生成したら、PIL
のpaste
関数で合成します。
def paste_image(base, layer, mask):
im = base;
im.paste(layer, mask = mask);
return im;
ここまで準備ができれば後は動画を開いて毎フレーム同じ処理を掛けていくだけです。
cap = cv2.VideoCapture('input.mov');
orig_size = (int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)));
out = cv2.VideoWriter('output.mov', cv2.cv.CV_FOURCC('m','p','4','v'), int(cap.get(cv2.cv.CV_CAP_PROP_FPS)), orig_size);
mask = gen_mask(orig_size, 0.4, 0.15, 0.05, 0.1);
while cap.isOpened():
ret,im_orig = cap.read();
if ret == True:
im = Image.fromarray(im_orig);
im_blur = gen_blurred_image(im, 2.0);
im = np.array(paste_image(im, im_blur, mask));
cv2.imshow('view', im);
out.write(im);
key = cv2.waitKey(1);
if key == 27:
break;
else:
break;
out.release();
cap.release();
cv2.destroyAllWindows();
リポジトリに置いてあるヴァージョンでは入力オプションの処理などをやっているため、行数が嵩んでいますが、本質的にはこんなもんかと。あと、画質や彩度などの調整もしています。ループの最後のkey == 27
はesc
で途中終了です。