LoginSignup
6
9

More than 5 years have passed since last update.

OpenCV+Python で動画に偽ティルトシフトエフェクトをつける

Posted at

ソースは
https://github.com/osoken/tiltshift_effector

.mov などの動画ファイルに、偽ティルトシフトエフェクトを掛けるPythonスクリプト。
Numpy, Scipy, OpenCV, Pillow を使用した。Python 2.7 向け。

原理は単純で、オリジナルの画像と、ぼけた画像をマスクしながら合成するというだけのもの。
オリジナル
orig.png

マスク
mask.png

ぼけた画像
blur.png

合成後
out.png

※わかりやすいように、かなり強めに掛けています。

マスクを生成する関数。

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,bc0.0から1.0の数値で、tは上部の白い領域、cは真ん中の黒い領域、tctcの間、bccと下の白い領域の間のグラデーションの幅を、全体を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]));

変数imagePILImage型で、フィルタ掛けるためだけにnumpy.array型に変換してすぐに戻しているのが無駄っぽいですが、他の操作との兼ね合いでこうなっています…。blur_factorはぼかしの強さで、浮動小数で与えます。

マスクと、ぼけた画像を生成したら、PILpaste関数で合成します。

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 == 27escで途中終了です。

6
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
9