目次
* フレーム差分法
フレーム差分法(フレーム間の同じ位置の画素値の差の絶対値を画素とする画像から移動物体を切り出す方法)を用いて動画を処理する.
"""動画を読み込み加工して保存する""" vid_name = os.path.join("kento.mp4") cap = cv2.VideoCapture(vid_name) fourcc = cv2.VideoWriter_fourcc(*'MJPG') out = cv2.VideoWriter('for_hatena_0.avi',fourcc, 60.0, (1280,720)) ret, frame = cap.read() last_last_frame = cv2.cvtColor(frame,cv2.COLOR_RGB2GRAY) ret, frame = cap.read() last_frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) threshold = 15 while(cap.isOpened): ret, frame = cap.read() if frame is None: break frame = cv2.cvtColor(frame,cv2.COLOR_RGB2GRAY) # 差分計算 new_frame1 = cv2.absdiff(frame,last_frame) new_frame2 = cv2.absdiff(last_frame, last_last_frame) new_frame = cv2.bitwise_and(new_frame1, new_frame2) # Medianフィルターによるノイズ除去 new_frame = cv2.medianBlur(new_frame,5) # 2値化 new_frame[new_frame>threshold]=255 new_frame[new_frame<=threshold]=0 new_frame = cv2.cvtColor(new_frame,cv2.COLOR_GRAY2RGB) out.write(new_frame) last_last_frame = last_frame last_frame = frame cap.release() out.release()
結果: (この動画は僕が椅子に座るところを撮ったものだが)人っぽい輪郭が映し出されているのがわかる 背景は動いていないので、もちろん黒抜きの画像となった。
* オプティカルフローの可視化
オプティカルフローとはフレーム間の対応画素の移動量をベクトルデータとして表現したもの。周囲数ピクセルも同じベクトルを持つ(制約条件)と近似することで計算を可能とした。OpenCVには"疎なオプティカルフロー"と"密なオプティカルフロー"との二つをモジュールがある。
1. 疎なオプティカルフロー
Lucas-Kanadeの方法(LK法)によるもの。固定した特徴点と周囲の3x3ピクセルを考慮したオプティカルフローを計算する。
"""オプティカルフロー""" import cv2 import numpy as np import os # cap = cv2.VideoCapture(os.path.join("data","train","000029.mpg")) cap = cv2.VideoCapture("walk.mp4") fourcc = cv2.VideoWriter_fourcc(*'MJPG') out = cv2.VideoWriter('for_hatena_1.avi',fourcc, 60.0, (1920,1080)) # cap = cv2.VideoCapture("29.avi") # params for ShiTomasi corner detection feature_params = dict( maxCorners = 100, qualityLevel = 0.1, minDistance = 7, blockSize = 7 ) # Parameters for lucas kanade optical flow lk_params = dict( winSize = (15,15), maxLevel = 2, criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) # Create some random colors color = np.random.randint(0,255,(100,3)) # Take first frame and find corners in it ret, old_frame = cap.read() old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params) # Create a mask image for drawing purposes mask = np.zeros_like(old_frame) count = 0 while(1): count += 1 ret,frame = cap.read() if frame is None: break frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # calculate optical flow p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) # Select good points good_new = p1[st==1] good_old = p0[st==1] # draw the tracks for i,(new,old) in enumerate(zip(good_new,good_old)): a,b = new.ravel() c,d = old.ravel() mask = cv2.line(mask, (a,b),(c,d), color[i].tolist(), 2) frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1) img = cv2.add(frame,mask) out.write(img) # Now update the previous frame and previous points old_gray = frame_gray.copy() p0 = good_new.reshape(-1,1,2) if count % 5 == 0: p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params) cv2.destroyAllWindows() cap.release()
結果: 線で軌跡が描かれている。これは5フレームごとに特徴量をとってその動きを追っている。(フレーム差分法と同じ動画を使うと僕の顔がでてしまうためフリー動画を使った。)
2. 密なオプティカルフロー
この場合は画像中の全ての画素についてオプティカルフローを計算する。
"""差分オプティカルフロー""" cap = cv2.VideoCapture("kento.mp4") fourcc = cv2.VideoWriter_fourcc(*'MJPG') out = cv2.VideoWriter('kento_2.avi',fourcc, 60.0, (1280,720)) ret, frame1 = cap.read() prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY) hsv = np.zeros_like(frame1) hsv[...,1] = 255 while(1): count += 1 ret, frame2 = cap.read() if frame2 is None: break next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY) flow = cv2.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0) mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1]) hsv[...,0] = ang*180/np.pi/2 hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX) rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR) out.write(rgb) prvs = next cap.release() cv2.destroyAllWindows()
ここでは方向ベクトルごとに色分けされている。
また、以下に定義する関数によって動画中にオプティカルフローのベクトル情報を図示したものを加えることができる。
def draw_flow(img, gray, flow, step=16): h, w = img.shape[:2] y, x = np.mgrid[step / 2:h:step, step / 2:w:step].reshape(2, -1).astype(int) fx, fy = flow[y, x].T lines = np.vstack([x, y, x + fx, y + fy]).T.reshape(-1, 2, 2) lines = np.int32(lines) vis = cv2.cvtColor(gray, cv2.COLOR_GRAY2RBG) vis = 255 - vis rad = int(step / 2) i = 0 for (x1, y1), (x2, y2) in lines: pv = img[y1, x1] col = (int(pv[0]), int(pv[1]), int(pv[2])) r = rad - int(rad * abs(fx[i]) * .05) cv2.circle(vis, (x1, y1), abs(r), col, -1) i += 1 cv2.polylines(vis, lines, False, (255, 255, 0)) return vis
for hatena 2
このエフェクトめちゃくちゃかっこいい。。。
参考: http://www.digifie.jp/blog/archives/1448
参考: http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html