攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

機器之心發表於2017-10-08

在本文中,我們將學習如何使用 OpenCV 和影象處理技術來模擬長時曝光影象。為了模擬長時曝光,我們採用了對一組影象取平均值的幀平均法。機器之心對該教程進行了簡要的介紹。

攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

長時曝光是攝影師最喜歡的攝影技術之一,運用長時曝光技術可以拍出展示時光流逝的圖片,而這是傳統技術難以企及的。我們經常使用這種技術表達流光夜景或柔順的流水。優秀的長時曝光作品是攝影師對快門速度、光圈大小和 ISO 感光度的完美把控,那麼我們如何使用 Python 和 OpenCV 庫來實現這種長時曝光的效果呢?

使用長時曝光技術後,水流變得如絲般光滑,夜空中的星星也隨著地球的旋轉留下一道光線軌跡,車頭/尾燈成為了一束光帶。

長時曝光技術效果酷炫,但為了拍到這種型別的鏡頭,我們需要學習一些系統方法:把相機放在三腳架上,應用各種濾鏡,計算曝光值等。更不用說,我們需要成為一名熟練的攝影師!

作為一名計算機視覺研究員和程式設計師,本文作者知道很多關於影象處理的知識。雖然他是個菜鳥攝影師,但有一種通過應用多幀影象平均法來模擬長時曝光效果的方法。通過計算在特定時間內拍攝的影象的平均值,我們可以(有效)模擬長時間曝光效果。

而且由於視訊實際上是一系列的影象,我們可以通過計算視訊中的所有幀的平均值來實現長時曝光效果。如此得到的是令人驚歎的長時曝光效果。


用 OpenCV 和 Python 實現長時曝光效果

這篇文章分為三部分。在本文的第一部分,我們將討論如何通過幀平均法來模擬長時間曝光效果。隨後我們將編寫為輸入視訊建立長時曝光效果的 Python 和 OpenCV 程式碼。最後,我們將在一些樣例視訊上使用我們的程式碼,以建立酷炫的長時曝光影象。

通過多幀影象平均法模擬長時曝光效果

通過平均數模擬長時曝光的想法由來已久。事實上,如果我們去瀏覽熱門的攝影網站,就會找到有關如何使用相機和三腳架手動實現這類效果的教程。

我們今天的目標是簡單地實現這種方法,所以我們使用 Python 和 OpenCV 自動為輸入視訊建立長時曝光效果的影象。給定一個輸入視訊,我們將計算所有幀的平均值(加權平均)以建立長時曝光效果。

注意:我們也可以使用多個連續影象建立這種長時曝光效果,但是由於視訊的實質是一系列影象,因此使用視訊演示此技術更容易。在將此技術應用到自定義影象時,請牢記這一點。我們看到,程式碼並不複雜,並且在應用於使用三腳架捕獲的視訊時(不要抖動相機)效果很好。

OpenCV 實現模擬長時曝光效果

我們首先建立一個名為 long_exposure.py 的新檔案,然後插入以下程式碼:

# import the necessary packages
import argparse
import imutils
import cv2
 
# 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'long exposure'")
args = vars(ap.parse_args())

2-4 行匯入軟體包,因此我們需要預先安裝 Imutils 和 opencv。如果你沒有安裝 imutils 模組,可以通過 pip 安裝:

$ pip install --upgrade imutils

如果你的電腦沒有安裝配置 OpenCV,那麼請自行搜尋 OpenCV 3 的安裝教程,並選擇適合你係統的安裝方式。

我們在 7-12 行解析命令列引數。

  •  --video : 視訊檔案目錄路徑
  • --output : 輸出「長時曝光」影象的路徑+檔名

接下來執行一些初始化步驟:

# initialize the Red, Green, and Blue channel averages, along with
# the total number of frames read from the file
(rAvg, gAvg, bAvg) = (None, None, None)
total = 0
 
# open a pointer to the video file
print("[INFO] opening video file pointer...")
stream = cv2.VideoCapture(args["video"])
print("[INFO] computing frame averages (this will take awhile)...")

我們在第 16 行初始化 RGB 通道平均值,稍後會將其合併到最終的長時曝光影象中。我們還初始化了第 17 行的總幀數。

對於本教程,我們正在使用包含所有幀的視訊檔案,因此有必要在 21 行建立一個捕獲視訊流的檔案指標。

現在我們進入計算平均值的迴圈語句中:

# loop over frames from the video file stream
while True:
	# grab the frame from the file stream
	(grabbed, frame) = stream.read()
 
	# if the frame was not grabbed, then we have reached the end of
	# the sfile
	if not grabbed:
		break
 
	# otherwise, split the frmae into its respective channels
	(B, G, R) = cv2.split(frame.astype("float"))

在迴圈語句中,我們將從流中捕獲幀(27 行),並將幀各自分解到對應的 BGR 通道變數(35 行)。請注意迴圈語句退出條件 :如果未從視訊檔案流的末尾抓取幀,我們將退出迴圈(31 行和 32 行)。

我們將在迴圈語句的其它部分執行平均值計算:

	# if the frame averages are None, initialize them
	if rAvg is None:
		rAvg = R
		bAvg = B
		gAvg = G
 
	# otherwise, compute the weighted average between the history of
	# frames and the current frames
	else:
		rAvg = ((total * rAvg) + (1 * R)) / (total + 1.0)
		gAvg = ((total * gAvg) + (1 * G)) / (total + 1.0)
		bAvg = ((total * bAvg) + (1 * B)) / (total + 1.0)
 
	# increment the total number of frames read thus far
	total += 1

如果這是第一次迭代,我們在第 38-41 行上將 RGB 的初始平均值設定為抓取的第一幀的通道值(if 語句僅在第一次迭代時執行此操作)。

否則,我們將計算 45-48 行上抓取的影象每個通道的平均值。平均值計算非常簡單,我們將總幀數乘以通道平均值,加上相應的通道,然後將該結果除以浮點型總幀數(我們將分母總數加一,因為生成的是一個新幀)。我們將計算結果儲存在相應的 RGB 通道平均值陣列中。

最後,我們增加總幀數,以便能夠保持執行時平均值(第 51 行)。一旦我們遍歷完視訊檔案中的所有幀,我們就可以將(平均)通道值合併成一個新影象並將其寫入磁碟:

# merge the RGB averages together and write the output image to disk
avg = cv2.merge([bAvg, gAvg, rAvg]).astype("uint8")
cv2.imwrite(args["output"], avg)
 
# do a bit of cleanup on the file pointer
stream.release()

在 54 行,我們使用 cv2.merge 函式,同時指定了列表中的每個影象的通道平均值。因為這些陣列包含浮點數(它們是所有幀的平均值),所以我們需要使用 astype("uint8") 函式將畫素值轉換為 [0-255] 的整數。

我們使用命令列引數 path + filename 在隨後的第 55 行中將 avg 影象寫入磁碟。我們也可以通過 cv2.imshow 函式將影象顯示在螢幕上,但是由於這會花費大量的 CPU 資源來處理視訊檔案,所以我們只是將影象儲存到磁碟以便進一步檢視。

該指令碼的最後一步是通過釋放視訊流指標(58 行)來清空記憶體。

長時曝光效果與 OpenCV 實現對比

我們通過處理三個示例視訊來測試指令碼效果。請注意,每個視訊均由安裝在穩定性良好的三腳架上的相機拍攝。

請注意所用視訊非作者本人拍攝,但都得到原作者的授權許可; 因此,本文作者並不能提供除原始碼之外的視訊資源下載。不過,如果你想重現作者的實驗結果,請參考我提供的原始視訊的連結。

我們的第一個示例是 15 秒鐘的水衝石頭的視訊,下面的視訊中包含了一個樣本幀:

視訊地址:https://videohive.net/item/mountain-river-water-and-stones-01/16602591

攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

圖 1:河水衝擊石頭的樣本幀

我們只需執行以下命令以實現長時曝光效果。

$ time python long_exposure.py --video videos/river_01.mov --output river_01.png 
[INFO] opening video file pointer...
[INFO] computing frame averages (this will take awhile)...
 
real	2m1.710s
user	0m50.959s
sys		0m40.207s

攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

圖 2:通過 Python 和 OpenCV 運用平均幀法實現的 15 秒的河水長時曝光效果圖。

注意水是如何由平均法處理而得到絲滑的效果。我們繼續河流的第二個例子,再次得到一幅蒙太奇效果圖如下:

攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

圖 3:另一條河流的樣本幀

以下命令用於生成長時曝光效果圖:

$ time python long_exposure.py --video videos/river_02.mov --output river_02.png 
[INFO] opening video file pointer...
[INFO] computing frame averages (this will take awhile)...
 
real	0m57.881s
user	0m27.207s
sys		0m21.792s

攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

圖 4:第二條河流的絲滑的長時曝光效果圖(由 OpenCV 建立)

注意靜止的岩石是如何保持原狀,但是湍急的河水被平均化為連續的圖片,從而模擬出長時曝光效果。

最後一個例子是我最喜歡的,因為水的顏色令人讚歎,它使水和森林交相輝映:

攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

圖 5:激流穿越森林的樣本幀

當用 OpenCV 產生長時曝光效果時,它會給你一種超現實的夢幻般的感覺:

$ time python long_exposure.py --video videos/river_03.mov --output river_03.png 
[INFO] opening video file pointer...
[INFO] computing frame averages (this will take awhile)...
 
real	3m17.212s
user	1m11.826s
sys		0m56.707s

攝影愛好者玩程式設計:利用Python和OpenCV打造專業級長時曝光攝影圖

圖 6:通過使用 Python 和 OpenCV 建立的夢幻般的長時曝光效果圖。

此外,我們還可以考慮通過有規律的間隔從輸入,從視訊中對幀進行取樣而不是對所有幀取平均值來構造不同的輸出。

總結

在本文中,我們學習瞭如何使用 OpenCV 和影象處理技術來模擬長時曝光影象。為了模擬長時曝光,我們採用了對一組影象取平均值的幀平均法。我們假設輸入影象/視訊是使用固定的相機拍攝的(否則產生的輸出影象會失真)。雖然這並非真正的「長時曝光」,但是效果上是極其(視覺上)相似的。更重要的是,這允許你應用長時曝光效果,而不需要成為專家攝影師或購買昂貴的相機、鏡頭和濾鏡。

原文連結:https://www.pyimagesearch.com/2017/08/14/long-exposure-with-opencv-and-python/

相關文章