2018年1月14日

用python+OpenCV做「如果鳥飛行留下軌跡」

前幾天看到國家地理的網站有一篇If Birds Left Tracks in the Sky, They’d Look Like This

攝影師用特殊的攝影技巧拍下鳥在空中移動的軌跡

我看著看著就好想也做看看啊,不過怎麼做呢?三年前我用imagej做過類似的事情
《imagej將時間資料視覺化》

當時的作法先用ffmpeg把影片轉成圖片之後,再交給imagej做成stack,然後做z軸的投影。這一次呢,我想要用python一次達成,練習看看怎麼做。

要作到這種軌跡圖,得要用到影片才行,然後我想到幾個月前在pyimagesearch這裡看到這篇Long exposure with OpenCV and Python。我就想應該用這種程式處理成長時間曝光的就可以了吧?結果答案並不是這麼簡單。

「長時間曝光」於是把影片中每幀影格的像素數值做平均,為了方便說明,先假設影片都是灰階的,像素數值是從0-255,某個影片中一共有100幀影格,其中左上角拍攝天空的一個像素,在一幀影格的數值有黑鳥,數值為0,另外99幀影格因為鳥飛過去了,都是拍到天空,數值是255。如果用長時間曝光的方式來做,那麼那個位置的像素數值就會是(0x1+255x99)/100=252。本來我們期待要拍到鳥的地方,結果變成只是稍暗的天空。


所以作法應該是怎樣呢?每幀影格中出現的鳥(像素數值很低的範圍)都應該被保留下來,這樣才能達到留下痕跡的效果。

想著怎麼做的那幾天,剛好看到大愛的節目「發現」有不少鳥飛行的片段,我就用他們的影片寫了程式玩玩,果然這樣的想法可行。

如果使用的影片幀率更高,應該就能做出像最上面文章裡的照片那樣






補充,週末傍晚剛好看到有人在放鴿子,順道拍了五秒影片來做鳥軌跡





執行的程式如下,須安裝opencv
source activate cv
python track.py -v bird.mp4 -o bird.png
------------track.py--------------------------------
# import the necessary packages
import argparse
import cv2
import numpy as np

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", required=True, help="path to input video file")
ap.add_argument("-o", "--output", required=True, help="path to output png")
args = vars(ap.parse_args())

cap = cv2.VideoCapture(args["video"])


width = int(cap.get(3))  # float
height = int(cap.get(4)) # float
resultFrame = np.empty((height,width,3), dtype=np.uint8)
resultFrame.fill(255)

while True:
    boolen, frame = cap.read() # get the frame
    resultFrame = np.minimum(resultFrame,frame)
    cv2.imshow('final',resultFrame)

    k = cv2.waitKey(5) & 0xFF
    if k == 27:
        break
cv2.imwrite(args["output"],resultFrame)
cap.release()
cv2.destroyAllWindows()